2022-01-13 10:45:40 -08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright (c) 2022 The Zcash developers
|
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
|
|
|
|
2022-01-13 14:43:21 -08:00
|
|
|
from test_framework.authproxy import JSONRPCException
|
2022-01-14 06:40:10 -08:00
|
|
|
from test_framework.mininode import COIN
|
2022-01-13 10:45:40 -08:00
|
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
|
|
from test_framework.util import (
|
2022-02-23 09:20:11 -08:00
|
|
|
NU5_BRANCH_ID,
|
2022-01-13 10:45:40 -08:00
|
|
|
assert_equal,
|
2022-01-13 14:43:21 -08:00
|
|
|
assert_raises_message,
|
2022-01-14 07:34:46 -08:00
|
|
|
get_coinbase_address,
|
2022-02-23 09:20:11 -08:00
|
|
|
nuparams,
|
2022-01-13 10:45:40 -08:00
|
|
|
start_nodes,
|
2022-01-14 07:34:46 -08:00
|
|
|
wait_and_assert_operationid_status,
|
2022-01-13 10:45:40 -08:00
|
|
|
)
|
|
|
|
|
2022-01-14 07:34:46 -08:00
|
|
|
from decimal import Decimal
|
|
|
|
|
2022-01-13 10:45:40 -08:00
|
|
|
# Test wallet accounts behaviour
|
|
|
|
class WalletAccountsTest(BitcoinTestFramework):
|
|
|
|
def setup_nodes(self):
|
|
|
|
return start_nodes(self.num_nodes, self.options.tmpdir, [[
|
|
|
|
'-experimentalfeatures',
|
|
|
|
'-orchardwallet',
|
2022-02-23 09:20:11 -08:00
|
|
|
nuparams(NU5_BRANCH_ID, 210),
|
2022-01-13 10:45:40 -08:00
|
|
|
]] * self.num_nodes)
|
|
|
|
|
2022-01-13 16:56:33 -08:00
|
|
|
def check_receiver_types(self, ua, expected):
|
|
|
|
actual = self.nodes[0].z_listunifiedreceivers(ua)
|
|
|
|
assert_equal(set(expected), set(actual))
|
|
|
|
|
2022-01-14 06:40:10 -08:00
|
|
|
# Check we only have balances in the expected pools.
|
|
|
|
# Remember that empty pools are omitted from the output.
|
2022-02-12 14:24:48 -08:00
|
|
|
def _check_balance_for_rpc(self, rpcmethod, node, account, expected, minconf):
|
|
|
|
rpc = getattr(self.nodes[node], rpcmethod)
|
2022-03-15 03:19:54 -07:00
|
|
|
actual = rpc(account, minconf)
|
2022-01-14 06:40:10 -08:00
|
|
|
assert_equal(set(expected), set(actual['pools']))
|
2022-03-15 03:19:54 -07:00
|
|
|
total_balance = 0
|
2022-01-14 06:40:10 -08:00
|
|
|
for pool in expected:
|
|
|
|
assert_equal(expected[pool] * COIN, actual['pools'][pool]['valueZat'])
|
2022-03-15 03:19:54 -07:00
|
|
|
total_balance += expected[pool]
|
|
|
|
assert_equal(actual['minimum_confirmations'], minconf)
|
|
|
|
return total_balance
|
2022-01-14 06:40:10 -08:00
|
|
|
|
2022-03-15 05:43:27 -07:00
|
|
|
def check_balance(self, node, account, address, expected, minconf=1):
|
2022-03-15 03:19:54 -07:00
|
|
|
acct_balance = self._check_balance_for_rpc('z_getbalanceforaccount', node, account, expected, minconf)
|
|
|
|
z_getbalance = self.nodes[node].z_getbalance(address, minconf)
|
|
|
|
assert_equal(acct_balance, z_getbalance)
|
2022-02-12 14:24:48 -08:00
|
|
|
fvk = self.nodes[node].z_exportviewingkey(address)
|
|
|
|
self._check_balance_for_rpc('z_getbalanceforviewingkey', node, fvk, expected, minconf)
|
2022-01-25 16:34:45 -08:00
|
|
|
|
2022-01-13 10:45:40 -08:00
|
|
|
def run_test(self):
|
|
|
|
# With a new wallet, the first account will be 0.
|
|
|
|
account0 = self.nodes[0].z_getnewaccount()
|
|
|
|
assert_equal(account0['account'], 0)
|
|
|
|
|
|
|
|
# The next account will be 1.
|
|
|
|
account1 = self.nodes[0].z_getnewaccount()
|
|
|
|
assert_equal(account1['account'], 1)
|
|
|
|
|
2022-01-13 14:43:21 -08:00
|
|
|
# Generate the first address for account 0.
|
|
|
|
addr0 = self.nodes[0].z_getaddressforaccount(0)
|
|
|
|
assert_equal(addr0['account'], 0)
|
2022-02-23 02:04:34 -08:00
|
|
|
assert_equal(set(addr0['pools']), set(['transparent', 'sapling', 'orchard']))
|
2022-01-13 14:43:21 -08:00
|
|
|
ua0 = addr0['unifiedaddress']
|
|
|
|
|
|
|
|
# We pick mnemonic phrases to ensure that we can always generate the default
|
|
|
|
# address in account 0; this is however not necessarily at diversifier index 0.
|
|
|
|
# We should be able to generate it directly and get the exact same data.
|
|
|
|
j = addr0['diversifier_index']
|
2022-01-18 12:02:13 -08:00
|
|
|
assert_equal(self.nodes[0].z_getaddressforaccount(0, [], j), addr0)
|
2022-01-13 14:43:21 -08:00
|
|
|
if j > 0:
|
|
|
|
# We should get an error if we generate the address at diversifier index 0.
|
|
|
|
assert_raises_message(
|
|
|
|
JSONRPCException,
|
|
|
|
'no address at diversifier index 0',
|
2022-01-18 12:02:13 -08:00
|
|
|
self.nodes[0].z_getaddressforaccount, 0, [], 0)
|
2022-01-13 14:43:21 -08:00
|
|
|
|
2022-02-23 02:04:34 -08:00
|
|
|
# The second address for account 0 is different to the first address.
|
|
|
|
addr0_2 = self.nodes[0].z_getaddressforaccount(0)
|
|
|
|
assert_equal(addr0_2['account'], 0)
|
|
|
|
assert_equal(set(addr0_2['pools']), set(['transparent', 'sapling', 'orchard']))
|
|
|
|
ua0_2 = addr0_2['unifiedaddress']
|
|
|
|
assert(ua0 != ua0_2)
|
|
|
|
|
|
|
|
# We can generate a fully-shielded address.
|
|
|
|
addr0_3 = self.nodes[0].z_getaddressforaccount(0, ['sapling', 'orchard'])
|
|
|
|
assert_equal(addr0_3['account'], 0)
|
|
|
|
assert_equal(set(addr0_3['pools']), set(['sapling', 'orchard']))
|
|
|
|
ua0_3 = addr0_3['unifiedaddress']
|
|
|
|
|
|
|
|
# We can generate an address without a Sapling receiver.
|
|
|
|
addr0_4 = self.nodes[0].z_getaddressforaccount(0, ['transparent', 'orchard'])
|
|
|
|
assert_equal(addr0_4['account'], 0)
|
|
|
|
assert_equal(set(addr0_4['pools']), set(['transparent', 'orchard']))
|
|
|
|
ua0_4 = addr0_4['unifiedaddress']
|
|
|
|
|
2022-01-13 14:43:21 -08:00
|
|
|
# The first address for account 1 is different to account 0.
|
|
|
|
addr1 = self.nodes[0].z_getaddressforaccount(1)
|
|
|
|
assert_equal(addr1['account'], 1)
|
2022-02-23 02:04:34 -08:00
|
|
|
assert_equal(set(addr1['pools']), set(['transparent', 'sapling', 'orchard']))
|
2022-01-13 14:43:21 -08:00
|
|
|
ua1 = addr1['unifiedaddress']
|
|
|
|
assert(ua0 != ua1)
|
|
|
|
|
2022-01-13 16:56:33 -08:00
|
|
|
# The UA contains the expected receiver kinds.
|
2022-02-23 02:04:34 -08:00
|
|
|
self.check_receiver_types(ua0, ['transparent', 'sapling', 'orchard'])
|
|
|
|
self.check_receiver_types(ua0_2, ['transparent', 'sapling', 'orchard'])
|
|
|
|
self.check_receiver_types(ua0_3, [ 'sapling', 'orchard'])
|
|
|
|
self.check_receiver_types(ua0_4, ['transparent', 'orchard'])
|
|
|
|
self.check_receiver_types(ua1, ['transparent', 'sapling', 'orchard'])
|
2022-01-13 16:56:33 -08:00
|
|
|
|
2022-01-14 06:40:10 -08:00
|
|
|
# The balances of the accounts are all zero.
|
2022-02-12 14:24:48 -08:00
|
|
|
self.check_balance(0, 0, ua0, {})
|
|
|
|
self.check_balance(0, 1, ua1, {})
|
2022-01-14 06:40:10 -08:00
|
|
|
|
2022-03-14 10:52:57 -07:00
|
|
|
# Send coinbase funds to the UA.
|
|
|
|
print('Sending coinbase funds to account')
|
2022-01-31 11:10:17 -08:00
|
|
|
recipients = [{'address': ua0, 'amount': Decimal('10')}]
|
2022-01-14 07:34:46 -08:00
|
|
|
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
|
|
|
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
|
|
|
|
|
|
|
# The wallet should detect the new note as belonging to the UA.
|
|
|
|
tx_details = self.nodes[0].z_viewtransaction(txid)
|
|
|
|
assert_equal(len(tx_details['outputs']), 1)
|
|
|
|
assert_equal(tx_details['outputs'][0]['type'], 'sapling')
|
|
|
|
assert_equal(tx_details['outputs'][0]['address'], ua0)
|
|
|
|
|
2022-01-14 06:40:10 -08:00
|
|
|
# The new balance should not be visible with the default minconf, but should be
|
|
|
|
# visible with minconf=0.
|
2022-01-14 07:34:46 -08:00
|
|
|
self.sync_all()
|
2022-02-12 14:24:48 -08:00
|
|
|
self.check_balance(0, 0, ua0, {})
|
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 10}, 0)
|
2022-01-14 06:40:10 -08:00
|
|
|
|
2022-01-14 07:34:46 -08:00
|
|
|
self.nodes[2].generate(1)
|
|
|
|
self.sync_all()
|
|
|
|
|
2022-01-14 06:40:10 -08:00
|
|
|
# The default minconf should now detect the balance.
|
2022-02-12 14:24:48 -08:00
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 10})
|
2022-01-14 06:40:10 -08:00
|
|
|
|
2022-03-14 10:52:57 -07:00
|
|
|
# Send Sapling funds from the UA.
|
|
|
|
print('Sending account funds to Sapling address')
|
2022-01-14 07:34:46 -08:00
|
|
|
node1sapling = self.nodes[1].z_getnewaddress('sapling')
|
|
|
|
recipients = [{'address': node1sapling, 'amount': Decimal('1')}]
|
2022-01-31 11:10:17 -08:00
|
|
|
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
|
2022-01-14 07:34:46 -08:00
|
|
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
|
|
|
|
|
|
|
# The wallet should detect the spent note as belonging to the UA.
|
|
|
|
tx_details = self.nodes[0].z_viewtransaction(txid)
|
|
|
|
assert_equal(len(tx_details['spends']), 1)
|
|
|
|
assert_equal(tx_details['spends'][0]['type'], 'sapling')
|
|
|
|
assert_equal(tx_details['spends'][0]['address'], ua0)
|
|
|
|
|
2022-01-14 06:40:10 -08:00
|
|
|
# The balances of the account should reflect whether zero-conf transactions are
|
|
|
|
# being considered. We will show either 0 (because the spent 10-ZEC note is never
|
|
|
|
# shown, as that transaction has been created and broadcast, and _might_ get mined
|
|
|
|
# up until the transaction expires), or 9 (if we include the unmined transaction).
|
|
|
|
self.sync_all()
|
2022-02-12 14:24:48 -08:00
|
|
|
self.check_balance(0, 0, ua0, {})
|
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 9}, 0)
|
2022-01-14 06:40:10 -08:00
|
|
|
|
2022-02-23 09:20:11 -08:00
|
|
|
# Activate NU5
|
2022-03-14 10:52:57 -07:00
|
|
|
print('Activating NU5')
|
2022-02-23 09:20:11 -08:00
|
|
|
self.nodes[2].generate(9)
|
|
|
|
self.sync_all()
|
|
|
|
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 210)
|
|
|
|
|
|
|
|
# Send more coinbase funds to the UA.
|
2022-03-14 10:52:57 -07:00
|
|
|
print('Sending coinbase funds to account')
|
2022-02-23 09:20:11 -08:00
|
|
|
recipients = [{'address': ua0, 'amount': Decimal('10')}]
|
|
|
|
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
|
|
|
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
|
|
|
|
|
|
|
# The wallet should detect the new note as belonging to the UA.
|
2022-03-14 10:52:57 -07:00
|
|
|
# TODO: Uncomment once z_viewtransaction shows Orchard details (#5186).
|
2022-02-23 09:20:11 -08:00
|
|
|
#tx_details = self.nodes[0].z_viewtransaction(txid)
|
|
|
|
#assert_equal(len(tx_details['outputs']), 1)
|
|
|
|
#assert_equal(tx_details['outputs'][0]['type'], 'orchard')
|
|
|
|
#assert_equal(tx_details['outputs'][0]['address'], ua0)
|
|
|
|
|
|
|
|
# The new balance should not be visible with the default minconf, but should be
|
|
|
|
# visible with minconf=0.
|
|
|
|
self.sync_all()
|
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 9})
|
2022-03-01 15:30:29 -08:00
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 9, 'orchard': 10}, 0)
|
2022-02-23 09:20:11 -08:00
|
|
|
|
2022-03-03 16:39:26 -08:00
|
|
|
# The total balance with the default minconf should be just the Sapling balance
|
|
|
|
assert_equal('9.00', self.nodes[0].z_gettotalbalance()['private'])
|
|
|
|
assert_equal('19.00', self.nodes[0].z_gettotalbalance(0)['private'])
|
|
|
|
|
2022-02-23 09:20:11 -08:00
|
|
|
self.nodes[2].generate(1)
|
|
|
|
self.sync_all()
|
|
|
|
|
2022-03-14 10:52:57 -07:00
|
|
|
# Send Orchard funds from the UA.
|
|
|
|
print('Sending account funds to Orchard-only UA')
|
|
|
|
node1account = self.nodes[1].z_getnewaccount()['account']
|
|
|
|
node1orchard = self.nodes[1].z_getaddressforaccount(node1account, ['orchard'])['unifiedaddress']
|
|
|
|
recipients = [{'address': node1orchard, 'amount': Decimal('1')}]
|
|
|
|
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
|
|
|
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
|
|
|
|
|
|
|
# The wallet should detect the spent note as belonging to the UA.
|
|
|
|
# TODO ORCHARD: Uncomment this once z_viewtransaction shows Orchard details (#5186).
|
|
|
|
# tx_details = self.nodes[0].z_viewtransaction(txid)
|
|
|
|
# assert_equal(len(tx_details['spends']), 1)
|
|
|
|
# assert_equal(tx_details['spends'][0]['type'], 'orchard')
|
|
|
|
# assert_equal(tx_details['spends'][0]['address'], ua0)
|
|
|
|
# assert_equal(len(tx_details['outputs']), 1)
|
|
|
|
# assert_equal(tx_details['outputs'][0]['type'], 'orchard')
|
|
|
|
# assert_equal(tx_details['outputs'][0]['address'], ua0)
|
|
|
|
|
|
|
|
# The balances of the account should reflect whether zero-conf transactions are
|
|
|
|
# being considered. The Sapling balance should remain at 9, while the Orchard
|
|
|
|
# balance will show either 0 (because the spent 10-ZEC note is never shown, as
|
|
|
|
# that transaction has been created and broadcast, and _might_ get mined up until
|
|
|
|
# the transaction expires), or 9 (if we include the unmined transaction).
|
|
|
|
self.sync_all()
|
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 9})
|
|
|
|
self.check_balance(0, 0, ua0, {'sapling': 9, 'orchard': 9}, 0)
|
|
|
|
|
2022-01-13 10:45:40 -08:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
WalletAccountsTest().main()
|