Repository: bitcoin/bitcoin
File: test/functional/wallet_labels.py (Python)

Code before:
        response = node.importdescriptors([{
            'desc': f'pkh({pubkey})',
            'label': '*',
            'timestamp': 'now',
        }])

        assert_equal(response[0]['success'], False)
        assert_equal(response[0]['error']['code'], -11)
        assert_equal(response[0]['error']['message'], "Invalid label name")

        for rpc_call in rpc_calls:
            assert_raises_rpc_error(-11, "Invalid label name", *rpc_call, label="*")

    def run_test(self):
        # Check that there's no UTXO on the node
        node = self.nodes[0]
        assert_equal(len(node.listunspent()), 0)

        self.log.info("Checking listlabels' invalid parameters")
        assert_raises_rpc_error(-8, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.", node.listlabels, "notavalidpurpose")
        assert_raises_rpc_error(-8, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.", node.listlabels, "unknown")

        # Note each time we call generate, all generated coins go into
        # the same address, so we call twice to get two addresses w/50 each
        self.generatetoaddress(node, nblocks=1, address=node.getnewaddress(label='coinbase'))
        self.generatetoaddress(node, nblocks=COINBASE_MATURITY + 1, address=node.getnewaddress(label='coinbase'))
        assert_equal(node.getbalance(), 100)

        # there should be 2 address groups
        # each with 1 address with a balance of 50 Bitcoins
        address_groups = node.listaddressgroupings()
        assert_equal(len(address_groups), 2)
        # the addresses aren't linked now, but will be after we send to the
        # common address
        linked_addresses = set()
        for address_group in address_groups:
            assert_equal(len(address_group), 1)
            assert_equal(len(address_group[0]), 3)
            assert_equal(address_group[0][1], 50)
            assert_equal(address_group[0][2], 'coinbase')
            linked_addresses.add(address_group[0][0])

        # send 50 from each address to a third address not in this wallet
        common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr"
        node.sendmany(
            amounts={common_address: 100},
            subtractfeefrom=[common_address],
            minconf=1,
        )
        # there should be 1 address group, with the previously
        # unlinked addresses now linked (they both have 0 balance)

Review comment (suggestion):
In commit "test: Add functional tests for named argument parsing" (56b1da77b5998c44b3bb799689a6c0449e9223fc)

Note: this comment is not exactly describing what is happening during this test. It's not true in general that unknown parameters with `=` are treated as positional. Unknown parameters with `=` are only treated as positional if the next positional parameter is known and either accepts a string (this case) or if it's known and accepts JSON and the argument actually parses as json. Otherwise it is still treated as a named parameter. Could consider clarifying the comment.

Code after:
        response = node.importdescriptors([{
            'desc': f'pkh({pubkey})',
            'label': '*',
            'timestamp': 'now',
        }])

        assert_equal(response[0]['success'], False)
        assert_equal(response[0]['error']['code'], -11)
        assert_equal(response[0]['error']['message'], "Invalid label name")

        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]
        assert_equal(len(node.listunspent()), 0)

        self.log.info("Checking listlabels' invalid parameters")
        assert_raises_rpc_error(-8, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.", node.listlabels, "notavalidpurpose")
        assert_raises_rpc_error(-8, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.", node.listlabels, "unknown")

        # Note each time we call generate, all generated coins go into
        # the same address, so we call twice to get two addresses w/50 each
        self.generatetoaddress(node, nblocks=1, address=node.getnewaddress(label='coinbase'))
        self.generatetoaddress(node, nblocks=COINBASE_MATURITY + 1, address=node.getnewaddress(label='coinbase'))
        assert_equal(node.getbalance(), 100)

        # there should be 2 address groups
        # each with 1 address with a balance of 50 Bitcoins