2016-08-03 22:28:39 -04:00
#!/usr/bin/env python3
2022-12-24 20:49:50 -03:00
# Copyright (c) 2016-2022 The Bitcoin Core developers
2016-08-03 22:28:39 -04:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-10-20 14:27:55 -03:00
""" Test label RPCs.
2017-01-17 20:34:40 -03:00
RPCs tested are :
2018-07-27 17:04:58 -04:00
- getaddressesbylabel
2017-04-23 19:41:59 -03:00
- listaddressgroupings
2017-10-20 14:27:55 -03:00
- setlabel
2017-01-17 20:34:40 -03:00
"""
2016-03-21 12:55:02 -03:00
from collections import defaultdict
2016-08-03 22:28:39 -04:00
2021-05-17 10:38:19 -04:00
from test_framework . blocktools import COINBASE_MATURITY
2016-08-03 22:28:39 -04:00
from test_framework . test_framework import BitcoinTestFramework
2018-04-17 15:13:00 -03:00
from test_framework . util import assert_equal , assert_raises_rpc_error
2019-11-24 08:05:38 -03:00
from test_framework . wallet_util import test_address
2016-08-03 22:28:39 -04:00
2017-10-20 14:27:55 -03:00
class WalletLabelsTest ( BitcoinTestFramework ) :
2022-11-09 08:53:13 -03:00
def add_options ( self , parser ) :
self . add_wallet_options ( parser )
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
2016-08-03 22:28:39 -04:00
self . setup_clean_chain = True
2022-12-20 11:02:11 -03:00
self . num_nodes = 2
2016-08-03 22:28:39 -04:00
2018-09-09 14:32:37 -03:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2022-09-27 06:10:01 -03:00
def invalid_label_name_test ( self ) :
node = self . nodes [ 0 ]
address = node . getnewaddress ( )
pubkey = node . getaddressinfo ( address ) [ ' pubkey ' ]
rpc_calls = [
[ node . getnewaddress ] ,
[ node . setlabel , address ] ,
[ node . getaddressesbylabel ] ,
[ node . importpubkey , pubkey ] ,
[ node . addmultisigaddress , 1 , [ pubkey ] ] ,
[ node . getreceivedbylabel ] ,
[ node . listsinceblock , node . getblockhash ( 0 ) , 1 , False , True , False ] ,
]
if self . options . descriptors :
response = node . importdescriptors ( [ {
' desc ' : f ' pkh( { pubkey } ) ' ,
' label ' : ' * ' ,
' timestamp ' : ' now ' ,
} ] )
else :
rpc_calls . extend ( [
[ node . importprivkey , node . dumpprivkey ( address ) ] ,
[ node . importaddress , address ] ,
] )
response = node . importmulti ( [ {
' scriptPubKey ' : { ' address ' : address } ,
' 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 , " * " )
2017-04-23 19:41:59 -03:00
def run_test ( self ) :
2018-07-27 17:04:58 -04:00
# Check that there's no UTXO on the node
node = self . nodes [ 0 ]
2016-08-03 22:28:39 -04:00
assert_equal ( len ( node . listunspent ( ) ) , 0 )
2017-04-23 19:41:59 -03:00
2023-02-20 19:45:32 -03:00
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 " )
2017-04-23 19:41:59 -03:00
# 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
2021-08-19 11:10:24 -04:00
self . generatetoaddress ( node , nblocks = 1 , address = node . getnewaddress ( label = ' coinbase ' ) )
self . generatetoaddress ( node , nblocks = COINBASE_MATURITY + 1 , address = node . getnewaddress ( label = ' coinbase ' ) )
2017-04-23 19:41:59 -03:00
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 )
2018-10-11 12:04:06 -03:00
assert_equal ( len ( address_group [ 0 ] ) , 3 )
2017-04-23 19:41:59 -03:00
assert_equal ( address_group [ 0 ] [ 1 ] , 50 )
2018-10-11 12:04:06 -03:00
assert_equal ( address_group [ 0 ] [ 2 ] , ' coinbase ' )
2017-04-23 19:41:59 -03:00
linked_addresses . add ( address_group [ 0 ] [ 0 ] )
# send 50 from each address to a third address not in this wallet
common_address = " msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr "
2018-07-27 17:04:58 -04:00
node . sendmany (
2017-04-23 19:41:59 -03:00
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)
address_groups = node . listaddressgroupings ( )
assert_equal ( len ( address_groups ) , 1 )
assert_equal ( len ( address_groups [ 0 ] ) , 2 )
assert_equal ( set ( [ a [ 0 ] for a in address_groups [ 0 ] ] ) , linked_addresses )
assert_equal ( [ a [ 1 ] for a in address_groups [ 0 ] ] , [ 0 , 0 ] )
2021-08-19 11:10:24 -04:00
self . generate ( node , 1 )
2017-04-23 19:41:59 -03:00
2017-10-20 14:27:55 -03:00
# we want to reset so that the "" label has what's expected.
2017-04-23 19:41:59 -03:00
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
2016-08-03 22:28:39 -04:00
amount_to_send = 1.0
2017-10-24 08:45:17 -03:00
2017-10-20 14:27:55 -03:00
# Create labels and make sure subsequent label API calls
# recognize the label/address associations.
2018-07-27 17:04:58 -04:00
labels = [ Label ( name ) for name in ( " a " , " b " , " c " , " d " , " e " ) ]
2017-10-20 14:27:55 -03:00
for label in labels :
2018-07-27 17:04:58 -04:00
address = node . getnewaddress ( label . name )
2018-04-23 16:16:09 -03:00
label . add_receive_address ( address )
2017-10-20 14:27:55 -03:00
label . verify ( node )
2017-10-24 08:45:17 -03:00
2022-12-20 11:02:11 -03:00
# Check listlabels when passing 'purpose'
node2_addr = self . nodes [ 1 ] . getnewaddress ( )
node . setlabel ( node2_addr , " node2_addr " )
assert_equal ( node . listlabels ( purpose = " send " ) , [ " node2_addr " ] )
assert_equal ( node . listlabels ( purpose = " receive " ) , sorted ( [ ' coinbase ' ] + [ label . name for label in labels ] ) )
2016-03-21 12:55:02 -03:00
# Check all labels are returned by listlabels.
2022-12-20 11:02:11 -03:00
assert_equal ( node . listlabels ( ) , sorted ( [ ' coinbase ' ] + [ label . name for label in labels ] + [ " node2_addr " ] ) )
2016-03-21 12:55:02 -03:00
2018-07-27 17:04:58 -04:00
# Send a transaction to each label.
2017-10-20 14:27:55 -03:00
for label in labels :
2018-07-27 17:04:58 -04:00
node . sendtoaddress ( label . addresses [ 0 ] , amount_to_send )
2017-10-20 14:27:55 -03:00
label . verify ( node )
2017-10-24 08:45:17 -03:00
# Check the amounts received.
2021-08-19 11:10:24 -04:00
self . generate ( node , 1 )
2017-10-20 14:27:55 -03:00
for label in labels :
2017-10-24 08:45:17 -03:00
assert_equal (
2017-10-20 14:27:55 -03:00
node . getreceivedbyaddress ( label . addresses [ 0 ] ) , amount_to_send )
assert_equal ( node . getreceivedbylabel ( label . name ) , amount_to_send )
for i , label in enumerate ( labels ) :
2018-04-17 15:13:00 -03:00
to_label = labels [ ( i + 1 ) % len ( labels ) ]
2018-07-27 17:04:58 -04:00
node . sendtoaddress ( to_label . addresses [ 0 ] , amount_to_send )
2021-08-19 11:10:24 -04:00
self . generate ( node , 1 )
2017-10-20 14:27:55 -03:00
for label in labels :
2018-07-27 17:04:58 -04:00
address = node . getnewaddress ( label . name )
2018-04-23 16:16:09 -03:00
label . add_receive_address ( address )
2017-10-20 14:27:55 -03:00
label . verify ( node )
assert_equal ( node . getreceivedbylabel ( label . name ) , 2 )
label . verify ( node )
2021-08-19 11:10:24 -04:00
self . generate ( node , COINBASE_MATURITY + 1 )
2017-10-20 14:27:55 -03:00
# Check that setlabel can assign a label to a new unused address.
for label in labels :
2018-04-23 16:16:09 -03:00
address = node . getnewaddress ( )
2017-10-20 14:27:55 -03:00
node . setlabel ( address , label . name )
label . add_address ( address )
label . verify ( node )
2018-07-27 17:04:58 -04:00
assert_raises_rpc_error ( - 11 , " No addresses with label " , node . getaddressesbylabel , " " )
2017-10-20 14:27:55 -03:00
# Check that addmultisigaddress can assign labels.
2019-07-16 15:33:35 -04:00
if not self . options . descriptors :
for label in labels :
addresses = [ ]
2020-08-02 19:10:56 -04:00
for _ in range ( 10 ) :
2019-07-16 15:33:35 -04:00
addresses . append ( node . getnewaddress ( ) )
multisig_address = node . addmultisigaddress ( 5 , addresses , label . name ) [ ' address ' ]
label . add_address ( multisig_address )
label . purpose [ multisig_address ] = " send "
label . verify ( node )
2021-08-19 11:10:24 -04:00
self . generate ( node , COINBASE_MATURITY + 1 )
2017-10-24 08:45:17 -03:00
2017-10-20 14:27:55 -03:00
# Check that setlabel can change the label of an address from a
# different label.
2018-07-27 17:04:58 -04:00
change_label ( node , labels [ 0 ] . addresses [ 0 ] , labels [ 0 ] , labels [ 1 ] )
2017-10-24 08:45:17 -03:00
2017-10-20 14:27:55 -03:00
# Check that setlabel can set the label of an address already
# in the label. This is a no-op.
2018-07-27 17:04:58 -04:00
change_label ( node , labels [ 2 ] . addresses [ 0 ] , labels [ 2 ] , labels [ 2 ] )
2017-10-24 08:45:17 -03:00
2022-09-27 06:10:01 -03:00
self . invalid_label_name_test ( )
2021-06-04 17:35:47 -04:00
if self . options . descriptors :
# This is a descriptor wallet test because of segwit v1+ addresses
self . log . info ( ' Check watchonly labels ' )
node . createwallet ( wallet_name = ' watch_only ' , disable_private_keys = True )
wallet_watch_only = node . get_wallet_rpc ( ' watch_only ' )
BECH32_VALID = {
' ✔️ _VER15_PROG40 ' : ' bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxkg7fn ' ,
' ✔️ _VER16_PROG03 ' : ' bcrt1sqqqqq8uhdgr ' ,
' ✔️ _VER16_PROB02 ' : ' bcrt1sqqqq4wstyw ' ,
}
BECH32_INVALID = {
' ❌_VER15_PROG41 ' : ' bcrt1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqajlxj8 ' ,
' ❌_VER16_PROB01 ' : ' bcrt1sqq5r4036 ' ,
}
for l in BECH32_VALID :
ad = BECH32_VALID [ l ]
wallet_watch_only . importaddress ( label = l , rescan = False , address = ad )
2021-08-19 11:10:24 -04:00
self . generatetoaddress ( node , 1 , ad )
2021-06-04 17:35:47 -04:00
assert_equal ( wallet_watch_only . getaddressesbylabel ( label = l ) , { ad : { ' purpose ' : ' receive ' } } )
assert_equal ( wallet_watch_only . getreceivedbylabel ( label = l ) , 0 )
for l in BECH32_INVALID :
ad = BECH32_INVALID [ l ]
assert_raises_rpc_error (
- 5 ,
" Address is not valid " if self . options . descriptors else " Invalid Bitcoin address or script " ,
lambda : wallet_watch_only . importaddress ( label = l , rescan = False , address = ad ) ,
)
2020-05-14 08:40:23 -04:00
2017-10-20 14:27:55 -03:00
class Label :
2018-07-27 17:04:58 -04:00
def __init__ ( self , name ) :
2017-10-20 14:27:55 -03:00
# Label name
2017-10-24 08:45:17 -03:00
self . name = name
2017-10-20 14:27:55 -03:00
# Current receiving address associated with this label.
2017-10-24 08:45:17 -03:00
self . receive_address = None
2017-10-20 14:27:55 -03:00
# List of all addresses assigned with this label
2017-10-24 08:45:17 -03:00
self . addresses = [ ]
2016-03-21 12:55:02 -03:00
# Map of address to address purpose
self . purpose = defaultdict ( lambda : " receive " )
2017-10-24 08:45:17 -03:00
def add_address ( self , address ) :
assert_equal ( address not in self . addresses , True )
self . addresses . append ( address )
def add_receive_address ( self , address ) :
self . add_address ( address )
def verify ( self , node ) :
if self . receive_address is not None :
assert self . receive_address in self . addresses
for address in self . addresses :
2019-11-24 21:31:38 -03:00
test_address ( node , address , labels = [ self . name ] )
2019-10-28 10:54:41 -03:00
assert self . name in node . listlabels ( )
2016-03-21 12:55:02 -03:00
assert_equal (
node . getaddressesbylabel ( self . name ) ,
{ address : { " purpose " : self . purpose [ address ] } for address in self . addresses } )
2017-10-24 08:45:17 -03:00
2018-07-27 17:04:58 -04:00
def change_label ( node , address , old_label , new_label ) :
2017-10-20 14:27:55 -03:00
assert_equal ( address in old_label . addresses , True )
2018-07-27 17:04:58 -04:00
node . setlabel ( address , new_label . name )
2017-10-24 08:45:17 -03:00
2017-10-20 14:27:55 -03:00
old_label . addresses . remove ( address )
new_label . add_address ( address )
2017-10-24 08:45:17 -03:00
2017-10-20 14:27:55 -03:00
old_label . verify ( node )
new_label . verify ( node )
2017-10-24 08:45:17 -03:00
2016-08-03 22:28:39 -04:00
if __name__ == ' __main__ ' :
2017-10-20 14:27:55 -03:00
WalletLabelsTest ( ) . main ( )