// getUsedAddr gets the script hash for the given series, branch and index from // the used addresses DB and uses that to look up the ManagedScriptAddress // from the address manager. It must be called with the manager unlocked. func (p *Pool) getUsedAddr(seriesID uint32, branch Branch, index Index) ( waddrmgr.ManagedScriptAddress, error) { mgr := p.manager var encryptedHash []byte err := p.namespace.View( func(tx walletdb.Tx) error { encryptedHash = getUsedAddrHash(tx, p.ID, seriesID, branch, index) return nil }) if err != nil { return nil, newError(ErrDatabase, "failed to lookup script hash for used addr", err) } if encryptedHash == nil { return nil, nil } hash, err := p.manager.Decrypt(waddrmgr.CKTPublic, encryptedHash) if err != nil { return nil, newError(ErrCrypto, "failed to decrypt stored script hash", err) } addr, err := btcutil.NewAddressScriptHashFromHash(hash, mgr.ChainParams()) if err != nil { return nil, newError(ErrInvalidScriptHash, "failed to parse script hash", err) } mAddr, err := mgr.Address(addr) if err != nil { return nil, err } return mAddr.(waddrmgr.ManagedScriptAddress), nil }
// newAddressScriptHash returns a new btcutil.AddressScriptHash from the // provided hash. It panics if an error occurs. This is only used in the tests // as a helper since the only way it can fail is if there is an error in the // test source code. func newAddressScriptHash(scriptHash []byte) btcutil.Address { addr, err := btcutil.NewAddressScriptHashFromHash(scriptHash, &chaincfg.MainNetParams) if err != nil { panic("invalid script hash in test source") } return addr }
// newScriptAddress initializes and returns a new pay-to-script-hash address. func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []byte) (*scriptAddress, error) { address, err := btcutil.NewAddressScriptHashFromHash(scriptHash, m.chainParams) if err != nil { return nil, err } return &scriptAddress{ manager: m, account: account, address: address, scriptEncrypted: scriptEncrypted, }, nil }
// ExtractPkScriptAddrs returns the type of script, addresses and required // signatures associated with the passed PkScript. Note that it only works for // 'standard' transaction script types. Any data such as public keys which are // invalid are omitted from the results. func ExtractPkScriptAddrs(pkScript []byte, chainParams *chaincfg.Params) (ScriptClass, []btcutil.Address, int, error) { var addrs []btcutil.Address var requiredSigs int // No valid addresses or required signatures if the script doesn't // parse. pops, err := parseScript(pkScript) if err != nil { return NonStandardTy, nil, 0, err } scriptClass := typeOfScript(pops) switch scriptClass { case PubKeyHashTy: // A pay-to-pubkey-hash script is of the form: // OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG // Therefore the pubkey hash is the 3rd item on the stack. // Skip the pubkey hash if it's invalid for some reason. requiredSigs = 1 addr, err := btcutil.NewAddressPubKeyHash(pops[2].data, chainParams) if err == nil { addrs = append(addrs, addr) } case WitnessPubKeyHashTy: // A pay-to-witness-pubkey-hash script is of thw form: // OP_0 <20-byte hash> // Therefore, the pubkey hash is the second item on the stack. // Skip the pubkey hash if it's invalid for some reason. requiredSigs = 1 addr, err := btcutil.NewAddressWitnessPubKeyHash(pops[1].data, chainParams) if err == nil { addrs = append(addrs, addr) } case PubKeyTy: // A pay-to-pubkey script is of the form: // <pubkey> OP_CHECKSIG // Therefore the pubkey is the first item on the stack. // Skip the pubkey if it's invalid for some reason. requiredSigs = 1 addr, err := btcutil.NewAddressPubKey(pops[0].data, chainParams) if err == nil { addrs = append(addrs, addr) } case ScriptHashTy: // A pay-to-script-hash script is of the form: // OP_HASH160 <scripthash> OP_EQUAL // Therefore the script hash is the 2nd item on the stack. // Skip the script hash if it's invalid for some reason. requiredSigs = 1 addr, err := btcutil.NewAddressScriptHashFromHash(pops[1].data, chainParams) if err == nil { addrs = append(addrs, addr) } case WitnessScriptHashTy: // A pay-to-witness-script-hash script is of the form: // OP_0 <32-byte hash> // Therefore, the script hash is the second item on the stack. // Skip the script hash if it's invalid for some reason. requiredSigs = 1 addr, err := btcutil.NewAddressWitnessScriptHashFromHash(pops[1].data, chainParams) if err == nil { addrs = append(addrs, addr) } case MultiSigTy: // A multi-signature script is of the form: // <numsigs> <pubkey> <pubkey> <pubkey>... <numpubkeys> OP_CHECKMULTISIG // Therefore the number of required signatures is the 1st item // on the stack and the number of public keys is the 2nd to last // item on the stack. requiredSigs = asSmallInt(pops[0].opcode) numPubKeys := asSmallInt(pops[len(pops)-2].opcode) // Extract the public keys while skipping any that are invalid. addrs = make([]btcutil.Address, 0, numPubKeys) for i := 0; i < numPubKeys; i++ { addr, err := btcutil.NewAddressPubKey(pops[i+1].data, chainParams) if err == nil { addrs = append(addrs, addr) } } case NullDataTy: // Null data transactions have no addresses or required // signatures. case NonStandardTy: // Don't attempt to extract addresses or required signatures for // nonstandard transactions. } return scriptClass, addrs, requiredSigs, nil }
// TestPayToAddrScript ensures the PayToAddrScript function generates the // correct scripts for the various types of addresses. func TestPayToAddrScript(t *testing.T) { t.Parallel() // 1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX p2pkhMain, err := btcutil.NewAddressPubKeyHash(hexToBytes("e34cce70c86"+ "373273efcc54ce7d2a491bb4a0e84"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create public key hash address: %v", err) return } // Taken from transaction: // b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d p2shMain, _ := btcutil.NewAddressScriptHashFromHash(hexToBytes("e8c300"+ "c87986efa84c37c0519929019ef86eb5b4"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create script hash address: %v", err) return } // mainnet p2pk 13CG6SJ3yHUXo4Cr2RY4THLLJrNFuG3gUg p2pkCompressedMain, err := btcutil.NewAddressPubKey(hexToBytes("02192d"+ "74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create pubkey address (compressed): %v", err) return } p2pkCompressed2Main, err := btcutil.NewAddressPubKey(hexToBytes("03b0b"+ "d634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create pubkey address (compressed 2): %v", err) return } p2pkUncompressedMain, err := btcutil.NewAddressPubKey(hexToBytes("0411"+ "db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5"+ "cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b4"+ "12a3"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create pubkey address (uncompressed): %v", err) return } tests := []struct { in btcutil.Address expected string err error }{ // pay-to-pubkey-hash address on mainnet { p2pkhMain, "DUP HASH160 DATA_20 0xe34cce70c86373273efcc54ce7d2a4" + "91bb4a0e8488 CHECKSIG", nil, }, // pay-to-script-hash address on mainnet { p2shMain, "HASH160 DATA_20 0xe8c300c87986efa84c37c0519929019ef8" + "6eb5b4 EQUAL", nil, }, // pay-to-pubkey address on mainnet. compressed key. { p2pkCompressedMain, "DATA_33 0x02192d74d0cb94344c9569c2e77901573d8d7903c3" + "ebec3a957724895dca52c6b4 CHECKSIG", nil, }, // pay-to-pubkey address on mainnet. compressed key (other way). { p2pkCompressed2Main, "DATA_33 0x03b0bd634234abbb1ba1e986e884185c61cf43e001" + "f9137f23c2c409273eb16e65 CHECKSIG", nil, }, // pay-to-pubkey address on mainnet. uncompressed key. { p2pkUncompressedMain, "DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382e" + "97b1482ecad7b148a6909a5cb2e0eaddfb84ccf97444" + "64f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 " + "CHECKSIG", nil, }, // Supported address types with nil pointers. {(*btcutil.AddressPubKeyHash)(nil), "", ErrUnsupportedAddress}, {(*btcutil.AddressScriptHash)(nil), "", ErrUnsupportedAddress}, {(*btcutil.AddressPubKey)(nil), "", ErrUnsupportedAddress}, // Unsupported address type. {&bogusAddress{}, "", ErrUnsupportedAddress}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { pkScript, err := PayToAddrScript(test.in) if err != test.err { t.Errorf("PayToAddrScript #%d unexpected error - "+ "got %v, want %v", i, err, test.err) continue } expected := mustParseShortForm(test.expected) if !bytes.Equal(pkScript, expected) { t.Errorf("PayToAddrScript #%d got: %x\nwant: %x", i, pkScript, expected) continue } } }
func (p *Pool) addressFor(script []byte) (btcutil.Address, error) { scriptHash := btcutil.Hash160(script) return btcutil.NewAddressScriptHashFromHash(scriptHash, p.manager.ChainParams()) }