// newManagedAddressWithoutPrivKey returns a new managed address based on the // passed account, public key, and whether or not the public key should be // compressed. func newManagedAddressWithoutPrivKey(m *Manager, account uint32, pubKey chainec.PublicKey, compressed bool) (*managedAddress, error) { // Create a pay-to-pubkey-hash address from the public key. var pubKeyHash []byte if compressed { pubKeyHash = dcrutil.Hash160(pubKey.SerializeCompressed()) } else { pubKeyHash = dcrutil.Hash160(pubKey.SerializeUncompressed()) } address, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, m.chainParams, chainec.ECTypeSecp256k1) if err != nil { return nil, err } return &managedAddress{ manager: m, address: address, account: account, imported: false, internal: false, multisig: false, compressed: compressed, pubKey: pubKey, privKeyEncrypted: nil, privKeyCT: nil, }, nil }
// TestFilterInsertKey ensures inserting public keys and addresses works as // expected. func TestFilterInsertKey(t *testing.T) { secret := "PtWU93QdrNBasyWA7GDJ3ycEN5aQRF69EynXJfmnyWDS4G7pzpEvN" wif, err := dcrutil.DecodeWIF(secret) if err != nil { t.Errorf("TestFilterInsertKey DecodeWIF failed: %v", err) return } f := bloom.NewFilter(2, 0, 0.001, wire.BloomUpdateAll) f.Add(wif.SerializePubKey()) f.Add(dcrutil.Hash160(wif.SerializePubKey())) want, err := hex.DecodeString("03323f6e080000000000000001") if err != nil { t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err) return } got := bytes.NewBuffer(nil) err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion) if err != nil { t.Errorf("TestFilterInsertWithTweak BtcDecode failed: %v\n", err) return } if !bytes.Equal(got.Bytes(), want) { t.Errorf("TestFilterInsertWithTweak failure: got %v want %v\n", got.Bytes(), want) return } }
// addUsedAddr creates a deposit script for the given seriesID/branch/index, // ensures it is imported into the address manager and finaly adds the script // hash to our used addresses DB. It must be called with the manager unlocked. func (p *Pool) addUsedAddr(seriesID uint32, branch Branch, index Index) error { script, err := p.DepositScript(seriesID, branch, index) if err != nil { return err } // First ensure the address manager has our script. That way there's no way // to have it in the used addresses DB but not in the address manager. // TODO: Decide how far back we want the addr manager to rescan and set the // BlockStamp height according to that. _, err = p.manager.ImportScript(script, &waddrmgr.BlockStamp{}) if err != nil && err.(waddrmgr.ManagerError).ErrorCode != waddrmgr.ErrDuplicateAddress { return err } encryptedHash, err := p.manager.Encrypt(waddrmgr.CKTPublic, dcrutil.Hash160(script)) if err != nil { return newError(ErrCrypto, "failed to encrypt script hash", err) } err = p.namespace.Update( func(tx walletdb.Tx) error { return putUsedAddrHash(tx, p.ID, seriesID, branch, index, encryptedHash) }) if err != nil { return newError(ErrDatabase, "failed to store used addr script hash", err) } return nil }
func TestAddrIndexKeySerialization(t *testing.T) { var hash160Bytes [ripemd160.Size]byte var packedIndex [35]byte fakeHash160 := dcrutil.Hash160([]byte("testing")) copy(fakeHash160, hash160Bytes[:]) fakeIndex := database.TxAddrIndex{ Hash160: hash160Bytes, Height: 1, TxOffset: 5, TxLen: 360, } serializedKey := addrIndexToKey(&fakeIndex) copy(packedIndex[:], serializedKey[0:35]) unpackedIndex := unpackTxIndex(packedIndex) if unpackedIndex.Height != fakeIndex.Height { t.Errorf("Incorrect block height. Unpack addr index key"+ "serialization failed. Expected %d, received %d", 1, unpackedIndex.Height) } if unpackedIndex.TxOffset != fakeIndex.TxOffset { t.Errorf("Incorrect tx offset. Unpack addr index key"+ "serialization failed. Expected %d, received %d", 5, unpackedIndex.TxOffset) } if unpackedIndex.TxLen != fakeIndex.TxLen { t.Errorf("Incorrect tx len. Unpack addr index key"+ "serialization failed. Expected %d, received %d", 360, unpackedIndex.TxLen) } }
// convertToAddrIndex indexes all data pushes greater than 8 bytes within the // passed SPK and returns a TxAddrIndex with the given data. Our "address" // index is actually a hash160 index, where in the ideal case the data push // is either the hash160 of a publicKey (P2PKH) or a Script (P2SH). func convertToAddrIndex(scrVersion uint16, scr []byte, height int64, locInBlock *wire.TxLoc) ([]*database.TxAddrIndex, error) { var tais []*database.TxAddrIndex if scr == nil || locInBlock == nil { return nil, fmt.Errorf("passed nil pointer") } var indexKey [ripemd160.Size]byte // Get the script classes and extract the PKH if applicable. // If it's multisig, unknown, etc, just hash the script itself. class, addrs, _, err := txscript.ExtractPkScriptAddrs(scrVersion, scr, activeNetParams.Params) if err != nil { return nil, fmt.Errorf("script conversion error: %v", err.Error()) } knownType := false for _, addr := range addrs { switch { case class == txscript.PubKeyTy: copy(indexKey[:], addr.Hash160()[:]) case class == txscript.PubkeyAltTy: copy(indexKey[:], addr.Hash160()[:]) case class == txscript.PubKeyHashTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.PubkeyHashAltTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeSubmissionTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeGenTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeRevocationTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeSubChangeTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.MultiSigTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.ScriptHashTy: copy(indexKey[:], addr.ScriptAddress()[:]) } tai := &database.TxAddrIndex{ indexKey, uint32(height), uint32(locInBlock.TxStart), uint32(locInBlock.TxLen), } tais = append(tais, tai) knownType = true } if !knownType { copy(indexKey[:], dcrutil.Hash160(scr)) tai := &database.TxAddrIndex{ indexKey, uint32(height), uint32(locInBlock.TxStart), uint32(locInBlock.TxLen), } tais = append(tais, tai) } return tais, nil }
// This example demonstrates manually creating and signing a redeem transaction. func ExampleSignTxOutput() { // Ordinarily the private key would come from whatever storage mechanism // is being used, but for this example just hard code it. privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + "d4f8720ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := chainec.Secp256k1.PrivKeyFromBytes(privKeyBytes) pubKeyHash := dcrutil.Hash160(pubKey.SerializeCompressed()) addr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams, chainec.ECTypeSecp256k1) if err != nil { fmt.Println(err) return } // For this example, create a fake transaction that represents what // would ordinarily be the real transaction that is being spent. It // contains a single output that pays to address in the amount of 1 DCR. originTx := wire.NewMsgTx() prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), dcrutil.TxTreeRegular) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { fmt.Println(err) return } txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) originTxHash := originTx.TxSha() // Create the transaction to redeem the fake transaction. redeemTx := wire.NewMsgTx() // Add the input(s) the redeeming transaction will spend. There is no // signature script at this point since it hasn't been created or signed // yet, hence nil is provided for it. prevOut = wire.NewOutPoint(&originTxHash, 0, dcrutil.TxTreeRegular) txIn = wire.NewTxIn(prevOut, nil) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, // but for this example don't bother. txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) // Sign the redeeming transaction. lookupKey := func(a dcrutil.Address) (chainec.PrivateKey, bool, error) { // Ordinarily this function would involve looking up the private // key for the provided address, but since the only thing being // signed in this example uses the address associated with the // private key from above, simply return it with the compressed // flag set since the address is using the associated compressed // public key. // // NOTE: If you want to prove the code is actually signing the // transaction properly, uncomment the following line which // intentionally returns an invalid key to sign with, which in // turn will result in a failure during the script execution // when verifying the signature. // // privKey.D.SetInt64(12345) // return privKey, true, nil } // Notice that the script database parameter is nil here since it isn't // used. It must be specified when pay-to-script-hash transactions are // being signed. sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil, nil, secp) if err != nil { fmt.Println(err) return } redeemTx.TxIn[0].SignatureScript = sigScript // Prove that the transaction has been validly signed by executing the // script pair. flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, 0) if err != nil { fmt.Println(err) return } if err := vm.Execute(); err != nil { fmt.Println(err) return } fmt.Println("Transaction successfully signed") // Output: // Transaction successfully signed }
// Address converts the extended key to a standard decred pay-to-pubkey-hash // address for the passed network. func (k *ExtendedKey) Address(net *chaincfg.Params) (*dcrutil.AddressPubKeyHash, error) { pkHash := dcrutil.Hash160(k.pubKeyBytes()) return dcrutil.NewAddressPubKeyHash(pkHash, net, chainec.ECTypeSecp256k1) }
// Child returns a derived child extended key at the given index. When this // extended key is a private extended key (as determined by the IsPrivate // function), a private extended key will be derived. Otherwise, the derived // extended key will be also be a public extended key. // // When the index is greater to or equal than the HardenedKeyStart constant, the // derived extended key will be a hardened extended key. It is only possible to // derive a hardended extended key from a private extended key. Consequently, // this function will return ErrDeriveHardFromPublic if a hardened child // extended key is requested from a public extended key. // // A hardened extended key is useful since, as previously mentioned, it requires // a parent private extended key to derive. In other words, normal child // extended public keys can be derived from a parent public extended key (no // knowledge of the parent private key) whereas hardened extended keys may not // be. // // NOTE: There is an extremely small chance (< 1 in 2^127) the specific child // index does not derive to a usable child. The ErrInvalidChild error will be // returned if this should occur, and the caller is expected to ignore the // invalid child and simply increment to the next index. func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) { // There are four scenarios that could happen here: // 1) Private extended key -> Hardened child private extended key // 2) Private extended key -> Non-hardened child private extended key // 3) Public extended key -> Non-hardened child public extended key // 4) Public extended key -> Hardened child public extended key (INVALID!) // Case #4 is invalid, so error out early. // A hardened child extended key may not be created from a public // extended key. isChildHardened := i >= HardenedKeyStart if !k.isPrivate && isChildHardened { return nil, ErrDeriveHardFromPublic } // The data used to derive the child key depends on whether or not the // child is hardened per [BIP32]. // // For hardened children: // 0x00 || ser256(parentKey) || ser32(i) // // For normal children: // serP(parentPubKey) || ser32(i) keyLen := 33 data := make([]byte, keyLen+4) if isChildHardened { // Case #1. // When the child is a hardened child, the key is known to be a // private key due to the above early return. Pad it with a // leading zero as required by [BIP32] for deriving the child. copy(data[1:], k.key) } else { // Case #2 or #3. // This is either a public or private extended key, but in // either case, the data which is used to derive the child key // starts with the secp256k1 compressed public key bytes. copy(data, k.pubKeyBytes()) } binary.BigEndian.PutUint32(data[keyLen:], i) // Take the HMAC-SHA512 of the current key's chain code and the derived // data: // I = HMAC-SHA512(Key = chainCode, Data = data) hmac512 := hmac.New(sha512.New, k.chainCode) hmac512.Write(data) ilr := hmac512.Sum(nil) // Split "I" into two 32-byte sequences Il and Ir where: // Il = intermediate key used to derive the child // Ir = child chain code il := ilr[:len(ilr)/2] childChainCode := ilr[len(ilr)/2:] // Both derived public or private keys rely on treating the left 32-byte // sequence calculated above (Il) as a 256-bit integer that must be // within the valid range for a secp256k1 private key. There is a small // chance (< 1 in 2^127) this condition will not hold, and in that case, // a child extended key can't be created for this index and the caller // should simply increment to the next index. ilNum := new(big.Int).SetBytes(il) if ilNum.Cmp(chainec.Secp256k1.GetN()) >= 0 || ilNum.Sign() == 0 { return nil, ErrInvalidChild } // The algorithm used to derive the child key depends on whether or not // a private or public child is being derived. // // For private children: // childKey = parse256(Il) + parentKey // // For public children: // childKey = serP(point(parse256(Il)) + parentKey) var isPrivate bool var childKey []byte if k.isPrivate { // Case #1 or #2. // Add the parent private key to the intermediate private key to // derive the final child key. // // childKey = parse256(Il) + parenKey keyNum := new(big.Int).SetBytes(k.key) ilNum.Add(ilNum, keyNum) ilNum.Mod(ilNum, chainec.Secp256k1.GetN()) childKey = ilNum.Bytes() isPrivate = true } else { // Case #3. // Calculate the corresponding intermediate public key for // intermediate private key. ilx, ily := chainec.Secp256k1.ScalarBaseMult(il) if ilx.Sign() == 0 || ily.Sign() == 0 { return nil, ErrInvalidChild } // Convert the serialized compressed parent public key into X // and Y coordinates so it can be added to the intermediate // public key. pubKey, err := chainec.Secp256k1.ParsePubKey(k.key) if err != nil { return nil, err } // Add the intermediate public key to the parent public key to // derive the final child key. // // childKey = serP(point(parse256(Il)) + parentKey) childX, childY := chainec.Secp256k1.Add(ilx, ily, pubKey.GetX(), pubKey.GetY()) pk := chainec.Secp256k1.NewPublicKey(childX, childY) childKey = pk.SerializeCompressed() } // The fingerprint of the parent for the derived child is the first 4 // bytes of the RIPEMD160(SHA256(parentPubKey)). parentFP := dcrutil.Hash160(k.pubKeyBytes())[:4] return newExtendedKey(k.version, childKey, childChainCode, parentFP, k.depth+1, i, isPrivate), nil }
func (p *Pool) addressFor(script []byte) (dcrutil.Address, error) { scriptHash := dcrutil.Hash160(script) return dcrutil.NewAddressScriptHashFromHash(scriptHash, p.manager.ChainParams()) }
// convertToAddrIndex indexes all data pushes greater than 8 bytes within the // passed SPK and returns a TxAddrIndex with the given data. Our "address" // index is actually a hash160 index, where in the ideal case the data push // is either the hash160 of a publicKey (P2PKH) or a Script (P2SH). func convertToAddrIndex(scrVersion uint16, scr []byte, height int64, locInBlock *wire.TxLoc, txType stake.TxType) ([]*database.TxAddrIndex, error) { var tais []*database.TxAddrIndex if scr == nil || locInBlock == nil { return nil, fmt.Errorf("passed nil pointer") } var indexKey [ripemd160.Size]byte // Get the script classes and extract the PKH if applicable. // If it's multisig, unknown, etc, just hash the script itself. class, addrs, _, err := txscript.ExtractPkScriptAddrs(scrVersion, scr, activeNetParams.Params) if err != nil { return nil, fmt.Errorf("script conversion error: %v", err.Error()) } knownType := false for _, addr := range addrs { switch { case class == txscript.PubKeyTy: copy(indexKey[:], addr.Hash160()[:]) case class == txscript.PubkeyAltTy: copy(indexKey[:], addr.Hash160()[:]) case class == txscript.PubKeyHashTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.PubkeyHashAltTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeSubmissionTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeGenTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeRevocationTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.StakeSubChangeTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.MultiSigTy: copy(indexKey[:], addr.ScriptAddress()[:]) case class == txscript.ScriptHashTy: copy(indexKey[:], addr.ScriptAddress()[:]) } tai := &database.TxAddrIndex{ Hash160: indexKey, Height: uint32(height), TxOffset: uint32(locInBlock.TxStart), TxLen: uint32(locInBlock.TxLen), } tais = append(tais, tai) knownType = true } // This is a commitment for a future vote or // revocation. Extract the address data from // it and store it in the addrIndex. if txType == stake.TxTypeSStx && class == txscript.NullDataTy { addr, err := stake.AddrFromSStxPkScrCommitment(scr, activeNetParams.Params) if err != nil { return nil, fmt.Errorf("ticket commit pkscr conversion error: %v", err.Error()) } copy(indexKey[:], addr.ScriptAddress()[:]) tai := &database.TxAddrIndex{ Hash160: indexKey, Height: uint32(height), TxOffset: uint32(locInBlock.TxStart), TxLen: uint32(locInBlock.TxLen), } tais = append(tais, tai) } else if !knownType { copy(indexKey[:], dcrutil.Hash160(scr)) tai := &database.TxAddrIndex{ Hash160: indexKey, Height: uint32(height), TxOffset: uint32(locInBlock.TxStart), TxLen: uint32(locInBlock.TxLen), } tais = append(tais, tai) } return tais, nil }