From f53dbbc5057b6f676db4be9bc720898149f293fc Mon Sep 17 00:00:00 2001 From: zaidmstrr Date: Sun, 29 Jun 2025 00:19:22 +0530 Subject: [PATCH] test: Add functional tests for named argument parsing --- test/functional/interface_bitcoin_cli.py | 22 ++++++++++++++ test/functional/rpc_psbt.py | 38 ++++++++++++++++++++++++ test/functional/wallet_labels.py | 22 ++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index 734f3110af3..1d917749a56 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -94,8 +94,30 @@ class TestBitcoinCli(BitcoinTestFramework): assert re.match(rf"^{re.escape(self.config['environment']['CLIENT_NAME'])} client.+services nwl2?$", det[0]) assert not any(line.startswith("Local services:") for line in det) + def test_echojson_positional_equals(self): + """Test JSON parameter parsing containing '=' with -named echojson""" + self.log.info("Test JSON parameter parsing containing '=' is handled correctly with -named") + + # This should be treated as a positional JSON argument, not as a named + result = self.nodes[0].cli("-named", "echojson", '["key=value"]').send_cli() + assert_equal(result, [["key=value"]]) + + result = self.nodes[0].cli("-named", "echojson", '["key=value", "another=test"]').send_cli() + assert_equal(result, [["key=value", "another=test"]]) + + result = self.nodes[0].cli("-named", "echojson", '["data=test"]', "42").send_cli() + expected = [["data=test"], 42] + assert_equal(result, expected) + + # This should be treated as a named parameter, as arg0 and arg1 are valid parameter names + result = self.nodes[0].cli("-named", "echojson", 'arg0=["data=test"]', 'arg1=42').send_cli() + expected = [["data=test"], 42] + assert_equal(result, expected) + def run_test(self): """Main test logic""" + self.test_echojson_positional_equals() + self.generate(self.nodes[0], BLOCKS) self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`") diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 933e4cd1ade..1db79e424f9 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -369,6 +369,43 @@ class PSBTTest(BitcoinTestFramework): changepos = psbtx["changepos"] assert_equal(decoded_psbt["tx"]["vout"][changepos]["scriptPubKey"]["type"], expected_type) + def test_psbt_named_parameter_handling(self): + """Test that PSBT Base64 parameters with '=' padding are handled correctly in -named mode""" + self.log.info("Testing PSBT Base64 parameter handling with '=' padding characters") + node = self.nodes[0] + psbt_with_padding = "cHNidP8BAJoCAAAAAqvNEjSrzRI0q80SNKvNEjSrzRI0q80SNKvNEjSrzRI0AAAAAAD9////NBLNqzQSzas0Es2rNBLNqzQSzas0Es2rNBLNqzQSzasBAAAAAP3///8CoIYBAAAAAAAWABQVQBGVs/sqFAmC8HZ8O+g1htqivkANAwAAAAAAFgAUir7MzgyzDnRMjdkVa7d+Dwr07jsAAAAAAAAAAAA=" + + # Test decodepsbt with explicit named parameter containing '=' padding + result = node.cli("-named", "decodepsbt", f"psbt={psbt_with_padding}").send_cli() + assert 'tx' in result + + # Test decodepsbt with positional argument containing '=' padding + result = node.cli("-named", "decodepsbt", psbt_with_padding).send_cli() + assert 'tx' in result + + # Test analyzepsbt with positional argument containing '=' padding + result = node.cli("-named", "analyzepsbt", psbt_with_padding).send_cli() + assert 'inputs' in result + + # Test finalizepsbt with positional argument containing '=' padding + result = node.cli("-named", "finalizepsbt", psbt_with_padding, "extract=true").send_cli() + assert 'complete' in result + + # Test walletprocesspsbt with positional argument containing '=' padding + result = node.cli("-named", "walletprocesspsbt", psbt_with_padding).send_cli() + assert 'complete' in result + + # Test utxoupdatepsbt with positional argument containing '=' padding + result = node.cli("-named", "utxoupdatepsbt", psbt_with_padding).send_cli() + assert isinstance(result, str) and len(result) > 0 + + # Test that unknown parameter with '=' gets treated as positional and return error + unknown_psbt_param = "unknown_param_data=more_data=" + # This should be treated as positional and fail with decode error, not parameter error + assert_raises_rpc_error(-22, "TX decode failed invalid base64", node.cli("-named", "finalizepsbt", unknown_psbt_param).send_cli) + + self.log.info("PSBT parameter handling test completed successfully") + def run_test(self): # Create and fund a raw tx for sending 10 BTC psbtx1 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress():10})['psbt'] @@ -1211,6 +1248,7 @@ class PSBTTest(BitcoinTestFramework): if not self.options.usecli: self.test_sighash_mismatch() self.test_sighash_adding() + self.test_psbt_named_parameter_handling() if __name__ == '__main__': PSBTTest(__file__).main() diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 068a4980db7..4a752486782 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -50,6 +50,27 @@ class WalletLabelsTest(BitcoinTestFramework): for rpc_call in rpc_calls: assert_raises_rpc_error(-11, "Invalid label name", *rpc_call, label="*") + def test_label_named_parameter_handling(self): + """Test that getnewaddress with labels containing '=' characters is handled correctly in -named mode""" + self.log.info("Test getnewaddress label parameter handling") + node = self.nodes[0] + + # Test getnewaddress with explicit named parameter containing '=' + label_with_equals = "wallet=wallet" + result = node.cli("-named", "getnewaddress", f"label={label_with_equals}").send_cli() + address = result.strip() + addr_info = node.getaddressinfo(address) + assert_equal(addr_info.get('labels', []), [label_with_equals]) + + self.log.info("Test bitcoin-cli -named passes parameter containing '=' by position if it does not specify a known parameter name and is in a string position") + equals_label = "my=label" + result = node.cli("-named", "getnewaddress", equals_label).send_cli() + address = result.strip() + addr_info = node.getaddressinfo(address) + assert_equal(addr_info.get('labels', []), [equals_label]) + + self.log.info("getnewaddress label parameter handling test completed successfully") + def run_test(self): # Check that there's no UTXO on the node node = self.nodes[0] @@ -159,6 +180,7 @@ class WalletLabelsTest(BitcoinTestFramework): change_label(node, labels[2].addresses[0], labels[2], labels[2]) self.invalid_label_name_test() + self.test_label_named_parameter_handling() # This is a descriptor wallet test because of segwit v1+ addresses self.log.info('Check watchonly labels')