From 1111c7e3f1f5c550f62016d71ccc518aafd97acf Mon Sep 17 00:00:00 2001 From: MacroFake Date: Tue, 26 Jul 2022 15:24:08 +0200 Subject: [PATCH 01/12] univalue: Avoid std::string copies --- src/univalue/include/univalue.h | 9 +++------ src/univalue/lib/univalue.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index ccaf56a271..6ef60b16e2 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -20,10 +20,7 @@ public: enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, }; UniValue() { typ = VNULL; } - UniValue(UniValue::VType initialType, const std::string& initialStr = "") { - typ = initialType; - val = initialStr; - } + UniValue(UniValue::VType type, std::string str = {}) : typ{type}, val{std::move(str)} {} template >, std::enable_if_t || // setFloat std::is_same_v || // setBool @@ -49,12 +46,12 @@ public: void setNull(); void setBool(bool val); - void setNumStr(const std::string& val); + void setNumStr(std::string str); void setInt(uint64_t val); void setInt(int64_t val); void setInt(int val_) { return setInt(int64_t{val_}); } void setFloat(double val); - void setStr(const std::string& val); + void setStr(std::string str); void setArray(); void setObject(); diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index 12a434dd0e..d1d4f18d26 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -44,15 +44,15 @@ static bool validNumStr(const std::string& s) return (tt == JTOK_NUMBER); } -void UniValue::setNumStr(const std::string& val_) +void UniValue::setNumStr(std::string str) { - if (!validNumStr(val_)) { - throw std::runtime_error{"The string '" + val_ + "' is not a valid JSON number"}; + if (!validNumStr(str)) { + throw std::runtime_error{"The string '" + str + "' is not a valid JSON number"}; } clear(); typ = VNUM; - val = val_; + val = std::move(str); } void UniValue::setInt(uint64_t val_) @@ -82,11 +82,11 @@ void UniValue::setFloat(double val_) return setNumStr(oss.str()); } -void UniValue::setStr(const std::string& val_) +void UniValue::setStr(std::string str) { clear(); typ = VSTR; - val = val_; + val = std::move(str); } void UniValue::setArray() From 2dede9f67596787e18904d3d5961f48ec75f241e Mon Sep 17 00:00:00 2001 From: Leonardo Araujo Date: Tue, 4 Oct 2022 13:51:24 -0300 Subject: [PATCH 02/12] Adjust RPCTypeCheckObj error string --- src/rpc/util.cpp | 7 ++----- test/functional/rpc_fundrawtransaction.py | 4 ++-- test/functional/rpc_mempool_info.py | 2 +- test/functional/rpc_psbt.py | 4 ++-- test/functional/wallet_bumpfee.py | 2 +- test/functional/wallet_send.py | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 3e98e89791..dd5739faf7 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -65,11 +65,8 @@ void RPCTypeCheckObj(const UniValue& o, if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); - if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) { - std::string err = strprintf("Expected type %s for %s, got %s", - uvTypeName(t.second.type), t.first, uvTypeName(v.type())); - throw JSONRPCError(RPC_TYPE_ERROR, err); - } + if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("JSON value of type %s for field %s is not of expected type %s", uvTypeName(v.type()), t.first, uvTypeName(t.second.type))); } if (fStrict) diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 17c6fce9c2..c6874f46de 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -793,7 +793,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.log.info("Test fundrawtxn with invalid estimate_mode settings") for k, v in {"number": 42, "object": {"foo": "bar"}}.items(): - assert_raises_rpc_error(-3, "Expected type string for estimate_mode, got {}".format(k), + assert_raises_rpc_error(-3, f"JSON value of type {k} for field estimate_mode is not of expected type string", node.fundrawtransaction, rawtx, {"estimate_mode": v, "conf_target": 0.1, "add_inputs": True}) for mode in ["", "foo", Decimal("3.141592")]: assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', @@ -803,7 +803,7 @@ class RawTransactionsTest(BitcoinTestFramework): for mode in ["unset", "economical", "conservative"]: self.log.debug("{}".format(mode)) for k, v in {"string": "", "object": {"foo": "bar"}}.items(): - assert_raises_rpc_error(-3, "Expected type number for conf_target, got {}".format(k), + assert_raises_rpc_error(-3, f"JSON value of type {k} for field conf_target is not of expected type number", node.fundrawtransaction, rawtx, {"estimate_mode": mode, "conf_target": v, "add_inputs": True}) for n in [-1, 0, 1009]: assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees.h diff --git a/test/functional/rpc_mempool_info.py b/test/functional/rpc_mempool_info.py index 9b5a3b9112..ae9c6572cf 100755 --- a/test/functional/rpc_mempool_info.py +++ b/test/functional/rpc_mempool_info.py @@ -84,7 +84,7 @@ class RPCMempoolInfoTest(BitcoinTestFramework): assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].gettxspendingprevout, [{'txid' : txidA, 'vout' : -1}]) self.log.info("Invalid txid provided") - assert_raises_rpc_error(-3, "Expected type string for txid, got number", self.nodes[0].gettxspendingprevout, [{'txid' : 42, 'vout' : 0}]) + assert_raises_rpc_error(-3, "JSON value of type number for field txid is not of expected type string", self.nodes[0].gettxspendingprevout, [{'txid' : 42, 'vout' : 0}]) self.log.info("Missing outputs") assert_raises_rpc_error(-8, "Invalid parameter, outputs are missing", self.nodes[0].gettxspendingprevout, []) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 4583ca25cf..30d476530a 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -272,7 +272,7 @@ class PSBTTest(BitcoinTestFramework): self.log.info("- raises RPC error with invalid estimate_mode settings") for k, v in {"number": 42, "object": {"foo": "bar"}}.items(): - assert_raises_rpc_error(-3, "Expected type string for estimate_mode, got {}".format(k), + assert_raises_rpc_error(-3, f"JSON value of type {k} for field estimate_mode is not of expected type string", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": v, "conf_target": 0.1, "add_inputs": True}) for mode in ["", "foo", Decimal("3.141592")]: assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', @@ -282,7 +282,7 @@ class PSBTTest(BitcoinTestFramework): for mode in ["unset", "economical", "conservative"]: self.log.debug("{}".format(mode)) for k, v in {"string": "", "object": {"foo": "bar"}}.items(): - assert_raises_rpc_error(-3, "Expected type number for conf_target, got {}".format(k), + assert_raises_rpc_error(-3, f"JSON value of type {k} for field conf_target is not of expected type number", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": mode, "conf_target": v, "add_inputs": True}) for n in [-1, 0, 1009]: assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees.h diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 158ef66110..2ee3e00a7b 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -151,7 +151,7 @@ class BumpFeeTest(BitcoinTestFramework): self.log.info("Test invalid estimate_mode settings") for k, v in {"number": 42, "object": {"foo": "bar"}}.items(): - assert_raises_rpc_error(-3, "Expected type string for estimate_mode, got {}".format(k), + assert_raises_rpc_error(-3, f"JSON value of type {k} for field estimate_mode is not of expected type string", rbf_node.bumpfee, rbfid, {"estimate_mode": v}) for mode in ["foo", Decimal("3.1415"), "sat/B", "BTC/kB"]: assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py index 07baa0595e..5f44e1f3a1 100755 --- a/test/functional/wallet_send.py +++ b/test/functional/wallet_send.py @@ -362,7 +362,7 @@ class WalletSendTest(BitcoinTestFramework): for mode in ["economical", "conservative"]: for k, v in {"string": "true", "bool": True, "object": {"foo": "bar"}}.items(): self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=v, estimate_mode=mode, - expect_error=(-3, f"Expected type number for conf_target, got {k}")) + expect_error=(-3, f"JSON value of type {k} for field conf_target is not of expected type number")) # Test setting explicit fee rate just below the minimum of 1 sat/vB. self.log.info("Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed") From 323890d0d7db2628f9dc6eaeba6e99ce0a12e1f5 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 28 Oct 2022 19:33:39 -0400 Subject: [PATCH 03/12] sign: Fill in taproot pubkey info for all script path sigs Taproot pubkey info was not being added for multi_a signing. The filling of this info is moved into the common function CreateTaprootScriptSig so that any signing of taproot scripts will include the pubkey info. --- src/script/sign.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) 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& 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({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 // 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({leaf_hash}), info)); - } else { - it->second.first.insert(leaf_hash); - } - } - std::vector sig; if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) { result = Vector(std::move(sig)); From 8781a1b6bbd0af3cfdf1421fd18de5432494619a Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 28 Oct 2022 20:00:58 -0400 Subject: [PATCH 04/12] psbt: Include output pubkey in additional pubkeys to sign In addition to the pubkeys in hd_keypaths and tap_bip32_keypaths, also see if the descriptor can produce a SigningProvider for the output pubkey. Also slightly refactors this area to reduce code duplication. --- src/wallet/scriptpubkeyman.cpp | 35 +++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 4c534d64ec..896ade77dd 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -2502,14 +2502,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(); - for (const auto& pk_pair : input.hd_keypaths) { - const CPubKey& pubkey = pk_pair.first; - std::unique_ptr pk_keys = GetSigningProvider(pubkey); - if (pk_keys) { - keys->Merge(std::move(*pk_keys)); - } + std::vector pubkeys; + + // ECDSA Pubkeys + for (const auto& [pk, _] : input.hd_keypaths) { + pubkeys.push_back(pk); } + + // Taproot output pubkey + std::vector> 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}) { @@ -2517,10 +2526,14 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& std::copy(pubkey.begin(), pubkey.end(), b + 1); CPubKey fullpubkey; fullpubkey.Set(b, b + 33); - std::unique_ptr 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 pk_keys = GetSigningProvider(pubkey); + if (pk_keys) { + keys->Merge(std::move(*pk_keys)); } } } From 6efcdf6b7f6daa83b5937aa630fce358fdaed333 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 28 Oct 2022 19:00:58 -0400 Subject: [PATCH 05/12] tests: Use new wallets for each test in wallet_taproot.py To avoid a wallet potentially being able to sign a transaction using keys from descriptors imported in previous tests, make new wallets for each test case rather than sharing them. --- test/functional/wallet_taproot.py | 117 ++++++++++++++++++------------ 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index 3c630ba433..14e35c2b48 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,46 @@ 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) + 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}") + 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']) + 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,13 +344,13 @@ 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) + 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) - decoded = self.psbt_offline.decodepsbt(res["psbt"]) + decoded = psbt_offline.decodepsbt(res["psbt"]) if pattern.startswith("tr("): for psbtin in decoded["inputs"]: assert "non_witness_utxo" not in psbtin @@ -326,7 +365,17 @@ class WalletTaprootTest(BitcoinTestFramework): 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) + 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 +385,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 +496,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() From 0de30ed509a9969cb254e00097671625c9e107d2 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 28 Oct 2022 19:28:21 -0400 Subject: [PATCH 06/12] tests: Test Taproot PSBT signing with keys in other descriptor Test that the same keys included in other descriptors will still be able to sign a PSBT that requires those keys. --- test/functional/wallet_taproot.py | 34 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index 14e35c2b48..c2acafb373 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -317,8 +317,10 @@ class WalletTaprootTest(BitcoinTestFramework): 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) @@ -334,6 +336,9 @@ class WalletTaprootTest(BitcoinTestFramework): assert(result[0]['success']) 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 = psbt_online.getnewaddress(address_type=address_type) @@ -349,20 +354,25 @@ class WalletTaprootTest(BitcoinTestFramework): # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. 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 = 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 + 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"] - 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) From c3b1fe59dbc7abe45973e282cddf3677514e220f Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Thu, 3 Nov 2022 19:02:42 +0100 Subject: [PATCH 07/12] rpc: doc: add missing option "bech32m" for `change_type` parameters Affects the help of the `fundrawtransaction`, `send` and `walletcratefundedpsbt` RPCs. --- src/wallet/rpc/spend.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 6cb33fc11e..f43cc8fb42 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -746,7 +746,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."}, @@ -1143,7 +1143,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" @@ -1596,7 +1596,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."}, From 25ef049d60535ac02508ba71ef60f17d8349f120 Mon Sep 17 00:00:00 2001 From: James O'Beirne Date: Fri, 28 Oct 2022 18:59:41 -0400 Subject: [PATCH 08/12] log: mempool: log removal reason in validation interface Currently the exact reason a transaction is removed from the mempool isn't logged. It is sometimes detectable from context, but adding the `reason` to the validation interface logs (where it is already passed) seems like an easy way to disambiguate. For example, in the case of mempool expiry, the logs look like this: ``` [validationinterface.cpp:220] [TransactionRemovedFromMempool] [validation] Enqueuing TransactionRemovedFromMempool: txid= wtxid= [txmempool.cpp:1050] [RemoveUnbroadcastTx] [mempool] Removed from set of unbroadcast txns before confirmation that txn was sent out [validationinterface.cpp:220] [operator()] [validation] TransactionRemovedFromMempool: txid= wtxid= [validation.cpp:267] [LimitMempoolSize] [mempool] Expired 1 transactions from the memory pool ``` There is no context-free way to know $txid was evicted on the basis of expiry. This change will make that case (and probably others) clear. --- src/txmempool.cpp | 14 ++++++++++++++ src/txmempool.h | 2 ++ src/validationinterface.cpp | 7 +++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 84ed2e9ef5..6a4cd842fb 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1182,3 +1182,17 @@ void CTxMemPool::SetLoadTried(bool load_tried) LOCK(cs); m_load_tried = load_tried; } + + +const std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept +{ + switch (r) { + case MemPoolRemovalReason::EXPIRY: return "expiry"; + case MemPoolRemovalReason::SIZELIMIT: return "sizelimit"; + case MemPoolRemovalReason::REORG: return "reorg"; + case MemPoolRemovalReason::BLOCK: return "block"; + case MemPoolRemovalReason::CONFLICT: return "conflict"; + case MemPoolRemovalReason::REPLACED: return "replaced"; + } + assert(false); +} diff --git a/src/txmempool.h b/src/txmempool.h index 4afaac0506..50d9a8236b 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -355,6 +355,8 @@ enum class MemPoolRemovalReason { REPLACED, //!< Removed for replacement }; +const std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept; + /** * CTxMemPool stores valid-according-to-the-current-best-chain transactions * that may be included in the next block. diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 613c5b65ef..740c39d99d 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -17,6 +17,8 @@ #include #include +const std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept; + /** * MainSignalsImpl manages a list of shared_ptr callbacks. * @@ -215,9 +217,10 @@ void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemP auto event = [tx, reason, mempool_sequence, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); }); }; - ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__, + ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__, tx->GetHash().ToString(), - tx->GetWitnessHash().ToString()); + tx->GetWitnessHash().ToString(), + RemovalReasonToString(reason)); } void CMainSignals::BlockConnected(const std::shared_ptr &pblock, const CBlockIndex *pindex) { From fa095257511e53d7a593c6714724aafb484e6b6f Mon Sep 17 00:00:00 2001 From: MacroFake Date: Thu, 14 Jul 2022 13:41:44 +0200 Subject: [PATCH 09/12] univalue: string_view test --- src/univalue/test/object.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp index 94d7343ff3..65e82543e4 100644 --- a/src/univalue/test/object.cpp +++ b/src/univalue/test/object.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #define BOOST_CHECK(expr) assert(expr) @@ -160,6 +161,14 @@ void univalue_set() BOOST_CHECK(v.isStr()); BOOST_CHECK_EQUAL(v.getValStr(), "zum"); + { + std::string_view sv{"ab\0c", 4}; + UniValue j{sv}; + BOOST_CHECK(j.isStr()); + BOOST_CHECK_EQUAL(j.getValStr(), sv); + BOOST_CHECK_EQUAL(j.write(), "\"ab\\u0000c\""); + } + v.setFloat(-1.01); BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); From 887d85e43d136dbfc2428f873ced3de50076bbd0 Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Tue, 8 Nov 2022 18:43:39 +0100 Subject: [PATCH 10/12] test: add missing bech32m / BIP86 test-cases to wallet_descriptor.py --- test/functional/wallet_descriptor.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py index 5dc23ba245..e7cfa56c46 100755 --- a/test/functional/wallet_descriptor.py +++ b/test/functional/wallet_descriptor.py @@ -62,6 +62,11 @@ class WalletDescriptorTest(BitcoinTestFramework): assert addr_info['desc'].startswith('wpkh(') assert_equal(addr_info['hdkeypath'], 'm/84\'/1\'/0\'/0/0') + addr = self.nodes[0].getnewaddress("", "bech32m") + addr_info = self.nodes[0].getaddressinfo(addr) + assert addr_info['desc'].startswith('tr(') + assert_equal(addr_info['hdkeypath'], 'm/86\'/1\'/0\'/0/0') + # Check that getrawchangeaddress works addr = self.nodes[0].getrawchangeaddress("legacy") addr_info = self.nodes[0].getaddressinfo(addr) @@ -78,6 +83,11 @@ class WalletDescriptorTest(BitcoinTestFramework): assert addr_info['desc'].startswith('wpkh(') assert_equal(addr_info['hdkeypath'], 'm/84\'/1\'/0\'/1/0') + addr = self.nodes[0].getrawchangeaddress("bech32m") + addr_info = self.nodes[0].getaddressinfo(addr) + assert addr_info['desc'].startswith('tr(') + assert_equal(addr_info['hdkeypath'], 'm/86\'/1\'/0\'/1/0') + # Make a wallet to receive coins at self.nodes[0].createwallet(wallet_name="desc2", descriptors=True) recv_wrpc = self.nodes[0].get_wallet_rpc("desc2") @@ -161,9 +171,11 @@ class WalletDescriptorTest(BitcoinTestFramework): addr_types = [('legacy', False, 'pkh(', '44\'/1\'/0\'', -13), ('p2sh-segwit', False, 'sh(wpkh(', '49\'/1\'/0\'', -14), ('bech32', False, 'wpkh(', '84\'/1\'/0\'', -13), + ('bech32m', False, 'tr(', '86\'/1\'/0\'', -13), ('legacy', True, 'pkh(', '44\'/1\'/0\'', -13), ('p2sh-segwit', True, 'sh(wpkh(', '49\'/1\'/0\'', -14), - ('bech32', True, 'wpkh(', '84\'/1\'/0\'', -13)] + ('bech32', True, 'wpkh(', '84\'/1\'/0\'', -13), + ('bech32m', True, 'tr(', '86\'/1\'/0\'', -13)] for addr_type, internal, desc_prefix, deriv_path, int_idx in addr_types: int_str = 'internal' if internal else 'external' From c8f91478c10affdfba8e52200c85379052f4e1b1 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Wed, 9 Nov 2022 09:28:46 +0000 Subject: [PATCH 11/12] test: Avoid collision with valid path names in `getarg_tests/logargs` --- src/test/getarg_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 70dd137e22..3643b80d5f 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -429,7 +429,7 @@ BOOST_AUTO_TEST_CASE(logargs) const auto okaylog = std::make_pair("-okaylog", ArgsManager::ALLOW_ANY); const auto dontlog = std::make_pair("-dontlog", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE); SetupArgs(local_args, {okaylog_bool, okaylog_negbool, okaylog, dontlog}); - ResetArgs(local_args, "-okaylog-bool -nookaylog-negbool -okaylog=public -dontlog=private"); + ResetArgs(local_args, "-okaylog-bool -nookaylog-negbool -okaylog=public -dontlog=private42"); // Everything logged to debug.log will also append to str std::string str; @@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(logargs) BOOST_CHECK(str.find("Command-line arg: okaylog-negbool=false") != std::string::npos); BOOST_CHECK(str.find("Command-line arg: okaylog=\"public\"") != std::string::npos); BOOST_CHECK(str.find("dontlog=****") != std::string::npos); - BOOST_CHECK(str.find("private") == std::string::npos); + BOOST_CHECK(str.find("private42") == std::string::npos); } BOOST_AUTO_TEST_SUITE_END() From 737c285f6955253b4bb6f5e2cb024cd79aca8ee4 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 10 Nov 2022 11:45:28 -0500 Subject: [PATCH 12/12] test: Don't pass add_to_wallet option to walletcreatefundedpsbt It's not a documented option. Noticed while working on #19762. --- test/functional/rpc_fundrawtransaction.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 54b42667bb..d67465b644 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -1173,7 +1173,6 @@ class RawTransactionsTest(BitcoinTestFramework): # Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount) options["add_inputs"] = True - options["add_to_wallet"] = False assert "psbt" in wallet.walletcreatefundedpsbt(outputs=[{addr1: 8}], inputs=inputs, options=options) # Case (4), Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount)