diff options
author | fanquake <fanquake@gmail.com> | 2022-11-09 10:24:17 +0000 |
---|---|---|
committer | fanquake <fanquake@gmail.com> | 2022-11-09 10:28:17 +0000 |
commit | 2a5d9818ed17b51657cd6fdc1b42884f0a6e4579 (patch) | |
tree | 32b4d02da375f65891c72c460097f54b87aea67e | |
parent | ca5f8f0de2c6fcf2ac122bd7fbb5a685d5935f71 (diff) | |
parent | 42c74a0a4c05c9b44593bacfb444949a8c152236 (diff) |
Merge bitcoin/bitcoin#26452: [24.x] rc4 backports
42c74a0a4c05c9b44593bacfb444949a8c152236 rpc: doc: add missing option "bech32m" for `change_type` parameters (Sebastian Falbesoner)
6e4d87e69622be8cd9ea199ac62ae36b4ab4f49f tests: Test Taproot PSBT signing with keys in other descriptor (Andrew Chow)
0a5ea2aa84cb8ad8387482c6b1b09322830171bd tests: Use new wallets for each test in wallet_taproot.py (Andrew Chow)
2159676b6e5ce2753db294068a699c0bfc0809fa psbt: Include output pubkey in additional pubkeys to sign (Andrew Chow)
754eefd21cf7bd253d0000e998115406ed7433c6 sign: Fill in taproot pubkey info for all script path sigs (Andrew Chow)
bb1fabda307b58a5a48d063e1b72491c17666f97 doc: mention BIP86 in doc/bips.md (Sebastian Falbesoner)
Pull request description:
Currently backports:
* https://github.com/bitcoin/bitcoin/pull/26418
* https://github.com/bitcoin/bitcoin/pull/26443
* https://github.com/bitcoin/bitcoin/pull/26449
ACKs for top commit:
dergoegge:
ACK 42c74a0a4c05c9b44593bacfb444949a8c152236
Tree-SHA512: e40f57b28cc4c06fc9dedebad6c9ab647fa0b98af1aef2d36874c6a05d39164c99fd763a2eb9ca162b7e276aa2e7d0f5934465ab4d7c1f5ba4a3162087e7089f
-rw-r--r-- | doc/bips.md | 1 | ||||
-rw-r--r-- | src/script/sign.cpp | 21 | ||||
-rw-r--r-- | src/wallet/rpc/spend.cpp | 6 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.cpp | 35 | ||||
-rwxr-xr-x | test/functional/wallet_taproot.py | 153 |
5 files changed, 131 insertions, 85 deletions
diff --git a/doc/bips.md b/doc/bips.md index 25381818e4..1d5c91b8bd 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -28,6 +28,7 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v24.0**): and it is disabled by default at build time since **v0.19.0** ([PR #15584](https://github.com/bitcoin/bitcoin/pull/15584)). It has been removed as of **v0.20.0** ([PR 17165](https://github.com/bitcoin/bitcoin/pull/17165)). * [`BIP 84`](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 84. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528)) +* [`BIP 86`](https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki): Descriptor wallets by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 86 since **v23.0** ([PR #22364](https://github.com/bitcoin/bitcoin/pull/22364)). * [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)). * [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)). * [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)), and has been *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)). diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 5da0d076d8..0d74a661a5 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -146,6 +146,16 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion) { + KeyOriginInfo info; + if (provider.GetKeyOriginByXOnly(pubkey, info)) { + auto it = sigdata.taproot_misc_pubkeys.find(pubkey); + if (it == sigdata.taproot_misc_pubkeys.end()) { + sigdata.taproot_misc_pubkeys.emplace(pubkey, std::make_pair(std::set<uint256>({leaf_hash}), info)); + } else { + it->second.first.insert(leaf_hash); + } + } + auto lookup_key = std::make_pair(pubkey, leaf_hash); auto it = sigdata.taproot_script_sigs.find(lookup_key); if (it != sigdata.taproot_script_sigs.end()) { @@ -170,17 +180,6 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu // <xonly pubkey> OP_CHECKSIG if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) { XOnlyPubKey pubkey{Span{script}.subspan(1, 32)}; - - KeyOriginInfo info; - if (provider.GetKeyOriginByXOnly(pubkey, info)) { - auto it = sigdata.taproot_misc_pubkeys.find(pubkey); - if (it == sigdata.taproot_misc_pubkeys.end()) { - sigdata.taproot_misc_pubkeys.emplace(pubkey, std::make_pair(std::set<uint256>({leaf_hash}), info)); - } else { - it->second.first.insert(leaf_hash); - } - } - std::vector<unsigned char> sig; if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) { result = Vector(std::move(sig)); diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index bc65cbf7bf..7064a50937 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -747,7 +747,7 @@ RPCHelpMan fundrawtransaction() "If that happens, you will need to fund the transaction with different inputs and republish it."}, {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"}, {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"}, - {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, + {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."}, {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n" "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n" "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."}, @@ -1144,7 +1144,7 @@ RPCHelpMan send() {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"}, {"change_address", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"}, {"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"}, - {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, + {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\" and \"bech32m\"."}, {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."}, {"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n" "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n" @@ -1597,7 +1597,7 @@ RPCHelpMan walletcreatefundedpsbt() "If that happens, you will need to fund the transaction with different inputs and republish it."}, {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"}, {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"}, - {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, + {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."}, {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"}, {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"}, {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."}, diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 41654579c6..3bcc6b4bdc 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -2499,14 +2499,23 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& keys->Merge(std::move(*script_keys)); } else { // Maybe there are pubkeys listed that we can sign for - script_keys = std::make_unique<FlatSigningProvider>(); - for (const auto& pk_pair : input.hd_keypaths) { - const CPubKey& pubkey = pk_pair.first; - std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey); - if (pk_keys) { - keys->Merge(std::move(*pk_keys)); - } + std::vector<CPubKey> pubkeys; + + // ECDSA Pubkeys + for (const auto& [pk, _] : input.hd_keypaths) { + pubkeys.push_back(pk); + } + + // Taproot output pubkey + std::vector<std::vector<unsigned char>> sols; + if (Solver(script, sols) == TxoutType::WITNESS_V1_TAPROOT) { + sols[0].insert(sols[0].begin(), 0x02); + pubkeys.emplace_back(sols[0]); + sols[0][0] = 0x03; + pubkeys.emplace_back(sols[0]); } + + // Taproot pubkeys for (const auto& pk_pair : input.m_tap_bip32_paths) { const XOnlyPubKey& pubkey = pk_pair.first; for (unsigned char prefix : {0x02, 0x03}) { @@ -2514,10 +2523,14 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& std::copy(pubkey.begin(), pubkey.end(), b + 1); CPubKey fullpubkey; fullpubkey.Set(b, b + 33); - std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(fullpubkey); - if (pk_keys) { - keys->Merge(std::move(*pk_keys)); - } + pubkeys.push_back(fullpubkey); + } + } + + for (const auto& pubkey : pubkeys) { + std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey); + if (pk_keys) { + keys->Merge(std::move(*pk_keys)); } } } diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index 3c630ba433..c2acafb373 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -5,6 +5,7 @@ """Test generation and spending of P2TR addresses.""" import random +import uuid from decimal import Decimal from test_framework.address import output_key_to_p2tr @@ -229,17 +230,28 @@ class WalletTaprootTest(BitcoinTestFramework): def do_test_addr(self, comment, pattern, privmap, treefn, keys): self.log.info("Testing %s address derivation" % comment) + + # Create wallets + wallet_uuid = uuid.uuid4().hex + self.nodes[0].createwallet(wallet_name=f"privs_tr_enabled_{wallet_uuid}", descriptors=True, blank=True) + self.nodes[0].createwallet(wallet_name=f"pubs_tr_enabled_{wallet_uuid}", descriptors=True, blank=True, disable_private_keys=True) + self.nodes[0].createwallet(wallet_name=f"addr_gen_{wallet_uuid}", descriptors=True, disable_private_keys=True, blank=True) + privs_tr_enabled = self.nodes[0].get_wallet_rpc(f"privs_tr_enabled_{wallet_uuid}") + pubs_tr_enabled = self.nodes[0].get_wallet_rpc(f"pubs_tr_enabled_{wallet_uuid}") + addr_gen = self.nodes[0].get_wallet_rpc(f"addr_gen_{wallet_uuid}") + desc = self.make_desc(pattern, privmap, keys, False) desc_pub = self.make_desc(pattern, privmap, keys, True) assert_equal(self.nodes[0].getdescriptorinfo(desc)['descriptor'], desc_pub) - result = self.addr_gen.importdescriptors([{"desc": desc_pub, "active": True, "timestamp": "now"}]) + result = addr_gen.importdescriptors([{"desc": desc_pub, "active": True, "timestamp": "now"}]) assert(result[0]['success']) + address_type = "bech32m" if "tr" in pattern else "bech32" for i in range(4): - addr_g = self.addr_gen.getnewaddress(address_type='bech32m') + addr_g = addr_gen.getnewaddress(address_type=address_type) if treefn is not None: addr_r = self.make_addr(treefn, keys, i) assert_equal(addr_g, addr_r) - desc_a = self.addr_gen.getaddressinfo(addr_g)['desc'] + desc_a = addr_gen.getaddressinfo(addr_g)['desc'] if desc.startswith("tr("): assert desc_a.startswith("tr(") rederive = self.nodes[1].deriveaddresses(desc_a) @@ -247,25 +259,37 @@ class WalletTaprootTest(BitcoinTestFramework): assert_equal(rederive[0], addr_g) # tr descriptors can be imported - result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}]) + result = privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}]) assert(result[0]["success"]) - result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}]) + result = pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}]) assert(result[0]["success"]) + # Cleanup + privs_tr_enabled.unloadwallet() + pubs_tr_enabled.unloadwallet() + addr_gen.unloadwallet() + def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, keys_change): self.log.info("Testing %s through sendtoaddress" % comment) + + # Create wallets + wallet_uuid = uuid.uuid4().hex + self.nodes[0].createwallet(wallet_name=f"rpc_online_{wallet_uuid}", descriptors=True, blank=True) + rpc_online = self.nodes[0].get_wallet_rpc(f"rpc_online_{wallet_uuid}") + desc_pay = self.make_desc(pattern, privmap, keys_pay) desc_change = self.make_desc(pattern, privmap, keys_change) desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True) desc_change_pub = self.make_desc(pattern, privmap, keys_change, True) assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub) assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub) - result = self.rpc_online.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}]) + result = rpc_online.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}]) assert(result[0]['success']) - result = self.rpc_online.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}]) + result = rpc_online.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}]) assert(result[0]['success']) + address_type = "bech32m" if "tr" in pattern else "bech32" for i in range(4): - addr_g = self.rpc_online.getnewaddress(address_type='bech32m') + addr_g = rpc_online.getnewaddress(address_type=address_type) if treefn is not None: addr_r = self.make_addr(treefn, keys_pay, i) assert_equal(addr_g, addr_r) @@ -273,31 +297,51 @@ class WalletTaprootTest(BitcoinTestFramework): to_amnt = random.randrange(1000000, boring_balance) self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - test_balance = int(self.rpc_online.getbalance() * 100000000) + test_balance = int(rpc_online.getbalance() * 100000000) ret_amnt = random.randrange(100000, test_balance) # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. - res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True, fee_rate=200) + res = rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True, fee_rate=200) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(self.rpc_online.gettransaction(res)["confirmations"] > 0) + assert(rpc_online.gettransaction(res)["confirmations"] > 0) + + # Cleanup + txid = rpc_online.sendall(recipients=[self.boring.getnewaddress()])["txid"] + self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) + assert(rpc_online.gettransaction(txid)["confirmations"] > 0) + rpc_online.unloadwallet() def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change): self.log.info("Testing %s through PSBT" % comment) + + # Create wallets + wallet_uuid = uuid.uuid4().hex + self.nodes[0].createwallet(wallet_name=f"psbt_online_{wallet_uuid}", descriptors=True, disable_private_keys=True, blank=True) + self.nodes[1].createwallet(wallet_name=f"psbt_offline_{wallet_uuid}", descriptors=True, blank=True) + self.nodes[1].createwallet(f"key_only_wallet_{wallet_uuid}", descriptors=True, blank=True) + psbt_online = self.nodes[0].get_wallet_rpc(f"psbt_online_{wallet_uuid}") + psbt_offline = self.nodes[1].get_wallet_rpc(f"psbt_offline_{wallet_uuid}") + key_only_wallet = self.nodes[1].get_wallet_rpc(f"key_only_wallet_{wallet_uuid}") + desc_pay = self.make_desc(pattern, privmap, keys_pay, False) desc_change = self.make_desc(pattern, privmap, keys_change, False) desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True) desc_change_pub = self.make_desc(pattern, privmap, keys_change, True) assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub) assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub) - result = self.psbt_online.importdescriptors([{"desc": desc_pay_pub, "active": True, "timestamp": "now"}]) + result = psbt_online.importdescriptors([{"desc": desc_pay_pub, "active": True, "timestamp": "now"}]) assert(result[0]['success']) - result = self.psbt_online.importdescriptors([{"desc": desc_change_pub, "active": True, "timestamp": "now", "internal": True}]) + result = psbt_online.importdescriptors([{"desc": desc_change_pub, "active": True, "timestamp": "now", "internal": True}]) assert(result[0]['success']) - result = self.psbt_offline.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}]) + result = psbt_offline.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}]) assert(result[0]['success']) - result = self.psbt_offline.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}]) + result = psbt_offline.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}]) assert(result[0]['success']) + for key in keys_pay + keys_change: + result = key_only_wallet.importdescriptors([{"desc": descsum_create(f"wpkh({key['xprv']}/*)"), "timestamp":"now"}]) + assert(result[0]["success"]) + address_type = "bech32m" if "tr" in pattern else "bech32" for i in range(4): - addr_g = self.psbt_online.getnewaddress(address_type='bech32m') + addr_g = psbt_online.getnewaddress(address_type=address_type) if treefn is not None: addr_r = self.make_addr(treefn, keys_pay, i) assert_equal(addr_g, addr_r) @@ -305,28 +349,43 @@ class WalletTaprootTest(BitcoinTestFramework): to_amnt = random.randrange(1000000, boring_balance) self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - test_balance = int(self.psbt_online.getbalance() * 100000000) + test_balance = int(psbt_online.getbalance() * 100000000) ret_amnt = random.randrange(100000, test_balance) # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. - psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200, "change_type": "bech32m"})['psbt'] - res = self.psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False) - - decoded = self.psbt_offline.decodepsbt(res["psbt"]) - if pattern.startswith("tr("): - for psbtin in decoded["inputs"]: - assert "non_witness_utxo" not in psbtin - assert "witness_utxo" in psbtin - assert "taproot_internal_key" in psbtin - assert "taproot_bip32_derivs" in psbtin - assert "taproot_key_path_sig" in psbtin or "taproot_script_path_sigs" in psbtin - if "taproot_script_path_sigs" in psbtin: - assert "taproot_merkle_root" in psbtin - assert "taproot_scripts" in psbtin - - rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] + psbt = psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200, "change_type": address_type})['psbt'] + res = psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False) + for wallet in [psbt_offline, key_only_wallet]: + res = wallet.walletprocesspsbt(psbt=psbt, finalize=False) + + decoded = wallet.decodepsbt(res["psbt"]) + if pattern.startswith("tr("): + for psbtin in decoded["inputs"]: + assert "non_witness_utxo" not in psbtin + assert "witness_utxo" in psbtin + assert "taproot_internal_key" in psbtin + assert "taproot_bip32_derivs" in psbtin + assert "taproot_key_path_sig" in psbtin or "taproot_script_path_sigs" in psbtin + if "taproot_script_path_sigs" in psbtin: + assert "taproot_merkle_root" in psbtin + assert "taproot_scripts" in psbtin + + rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] + res = self.nodes[0].testmempoolaccept([rawtx]) + assert res[0]["allowed"] + txid = self.nodes[0].sendrawtransaction(rawtx) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0) + assert(psbt_online.gettransaction(txid)['confirmations'] > 0) + + # Cleanup + psbt = psbt_online.sendall(recipients=[self.boring.getnewaddress()], options={"psbt": True})["psbt"] + res = psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False) + rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] + txid = self.nodes[0].sendrawtransaction(rawtx) + self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) + assert(psbt_online.gettransaction(txid)['confirmations'] > 0) + psbt_online.unloadwallet() + psbt_offline.unloadwallet() def do_test(self, comment, pattern, privmap, treefn): nkeys = len(privmap) @@ -336,21 +395,8 @@ class WalletTaprootTest(BitcoinTestFramework): self.do_test_psbt(comment, pattern, privmap, treefn, keys[2*nkeys:3*nkeys], keys[3*nkeys:4*nkeys]) def run_test(self): - self.log.info("Creating wallets...") - self.nodes[0].createwallet(wallet_name="privs_tr_enabled", descriptors=True, blank=True) - self.privs_tr_enabled = self.nodes[0].get_wallet_rpc("privs_tr_enabled") - self.nodes[0].createwallet(wallet_name="pubs_tr_enabled", descriptors=True, blank=True, disable_private_keys=True) - self.pubs_tr_enabled = self.nodes[0].get_wallet_rpc("pubs_tr_enabled") self.nodes[0].createwallet(wallet_name="boring") - self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True) - self.nodes[0].createwallet(wallet_name="rpc_online", descriptors=True, blank=True) - self.nodes[0].createwallet(wallet_name="psbt_online", descriptors=True, disable_private_keys=True, blank=True) - self.nodes[1].createwallet(wallet_name="psbt_offline", descriptors=True, blank=True) self.boring = self.nodes[0].get_wallet_rpc("boring") - self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen") - self.rpc_online = self.nodes[0].get_wallet_rpc("rpc_online") - self.psbt_online = self.nodes[0].get_wallet_rpc("psbt_online") - self.psbt_offline = self.nodes[1].get_wallet_rpc("psbt_offline") self.log.info("Mining blocks...") gen_addr = self.boring.getnewaddress() @@ -460,18 +506,5 @@ class WalletTaprootTest(BitcoinTestFramework): lambda k1: key(k1) ) - self.log.info("Sending everything back...") - - txid = self.rpc_online.sendall(recipients=[self.boring.getnewaddress()])["txid"] - self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0) - - psbt = self.psbt_online.sendall(recipients=[self.boring.getnewaddress()], options={"psbt": True})["psbt"] - res = self.psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False) - rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] - txid = self.nodes[0].sendrawtransaction(rawtx) - self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0) - if __name__ == '__main__': WalletTaprootTest().main() |