zcash/qa/rpc-tests/wallet_z_sendmany.py

564 lines
29 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# Copyright (c) 2020-2024 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
NU5_BRANCH_ID,
assert_equal,
assert_greater_than,
assert_raises_message,
connect_nodes_bi,
get_coinbase_address,
nuparams,
start_nodes,
wait_and_assert_operationid_status,
)
from test_framework.authproxy import JSONRPCException
from test_framework.mininode import COIN
from test_framework.zip317 import conventional_fee, ZIP_317_FEE
from decimal import Decimal
# Test wallet address behaviour across network upgrades
class WalletZSendmanyTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.cache_behavior = 'sprout'
def setup_network(self, split=False):
self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[[
nuparams(NU5_BRANCH_ID, 238),
'-allowdeprecated=getnewaddress',
'-allowdeprecated=z_getnewaddress',
'-allowdeprecated=z_getbalance',
'-allowdeprecated=z_gettotalbalance',
]] * self.num_nodes)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
self.is_network_split=False
self.sync_all()
# Check that an account has expected balances in only the expected pools.
# Remember that empty pools are omitted from the output.
def _check_balance_for_rpc(self, rpcmethod, node, account, expected, minconf):
rpc = getattr(self.nodes[node], rpcmethod)
actual = rpc(account, minconf)
assert_equal(set(expected), set(actual['pools']))
total_balance = 0
for pool in expected:
assert_equal(expected[pool] * COIN, actual['pools'][pool]['valueZat'])
total_balance += expected[pool]
assert_equal(actual['minimum_confirmations'], minconf)
return total_balance
# Check that an account has expected balances in only the expected pools, and that
# they are held only in `address`.
def check_balance(self, node, account, address, expected, minconf=1):
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)
fvk = self.nodes[node].z_exportviewingkey(address)
self._check_balance_for_rpc('z_getbalanceforviewingkey', node, fvk, expected, minconf)
def run_test(self):
# z_sendmany is expected to fail if tx size breaks limit
n0sapling = self.nodes[0].z_getnewaddress()
recipients = []
num_t_recipients = 1000
num_z_recipients = 2100
amount_per_recipient = Decimal('0.00000001')
errorString = ''
for i in range(0, num_t_recipients):
newtaddr = self.nodes[2].getnewaddress()
recipients.append({"address": newtaddr, "amount": amount_per_recipient})
for i in range(0, num_z_recipients):
newzaddr = self.nodes[2].z_getnewaddress()
recipients.append({"address": newzaddr, "amount": amount_per_recipient})
try:
self.nodes[0].z_sendmany(n0sapling, recipients)
except JSONRPCException as e:
errorString = e.error['message']
assert("size of raw transaction would be larger than limit" in errorString)
# add zaddr to node 2
n2saddr = self.nodes[2].z_getnewaddress()
# add taddr to node 2
n2taddr = self.nodes[2].getnewaddress()
# send from node 0 to node 2 taddr
mytxid = self.nodes[0].sendtoaddress(n2taddr, Decimal('10'))
self.sync_all()
self.nodes[0].generate(10)
self.sync_all()
# send node 2 taddr to zaddr
zsendmanynotevalue = Decimal('7.0')
fee = conventional_fee(3)
recipients = [{"address": n2saddr, "amount": zsendmanynotevalue}]
opid = self.nodes[2].z_sendmany(n2taddr, recipients, 1, fee, 'AllowFullyTransparent')
mytxid = wait_and_assert_operationid_status(self.nodes[2], opid)
self.sync_all()
n2sprout_balance = Decimal('50.00000000')
# check shielded balance status with getwalletinfo
wallet_info = self.nodes[2].getwalletinfo()
assert_equal(Decimal(wallet_info["shielded_unconfirmed_balance"]), zsendmanynotevalue)
assert_equal(Decimal(wallet_info["shielded_balance"]), n2sprout_balance)
self.nodes[2].generate(10)
self.sync_all()
n0t_balance = self.nodes[0].getbalance()
n2t_balance = Decimal('210.00000000') - zsendmanynotevalue - fee
assert_equal(Decimal(self.nodes[2].getbalance()), n2t_balance)
assert_equal(Decimal(self.nodes[2].getbalance("*")), n2t_balance)
# check zaddr balance with z_getbalance
n2saddr_balance = zsendmanynotevalue
assert_equal(self.nodes[2].z_getbalance(n2saddr), n2saddr_balance)
# check via z_gettotalbalance
resp = self.nodes[2].z_gettotalbalance()
assert_equal(Decimal(resp["transparent"]), n2t_balance)
assert_equal(Decimal(resp["private"]), n2sprout_balance + n2saddr_balance)
assert_equal(Decimal(resp["total"]), n2t_balance + n2sprout_balance + n2saddr_balance)
# check confirmed shielded balance with getwalletinfo
wallet_info = self.nodes[2].getwalletinfo()
assert_equal(Decimal(wallet_info["shielded_unconfirmed_balance"]), Decimal('0.0'))
assert_equal(Decimal(wallet_info["shielded_balance"]), n2sprout_balance + n2saddr_balance)
# there should be at least one Sapling output
mytxdetails = self.nodes[2].getrawtransaction(mytxid, 1)
assert_greater_than(len(mytxdetails["vShieldedOutput"]), 0)
# the Sapling output should take in all the public value
assert_equal(mytxdetails["valueBalance"], -zsendmanynotevalue)
# try sending with a memo to a taddr, which should fail
recipients = [{"address": self.nodes[0].getnewaddress(), "amount": Decimal('1'), "memo": "DEADBEEF"}]
opid = self.nodes[2].z_sendmany(n2saddr, recipients, 1, ZIP_317_FEE, 'AllowRevealedRecipients')
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', 'Failed to build transaction: Memos cannot be sent to transparent addresses.')
fee = conventional_fee(4)
recipients = [
{"address": self.nodes[0].getnewaddress(), "amount": Decimal('1')},
{"address": self.nodes[2].getnewaddress(), "amount": Decimal('1')},
];
opid = self.nodes[2].z_sendmany(n2saddr, recipients, 1, fee, 'AllowRevealedRecipients')
wait_and_assert_operationid_status(self.nodes[2], opid)
n2saddr_balance -= Decimal('2') + fee
n0t_balance += Decimal('1')
n2t_balance += Decimal('1')
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
n0t_balance += Decimal('10') # newly mature
assert_equal(Decimal(self.nodes[0].getbalance()), n0t_balance)
assert_equal(Decimal(self.nodes[0].getbalance("*")), n0t_balance)
assert_equal(Decimal(self.nodes[2].getbalance()), n2t_balance)
assert_equal(Decimal(self.nodes[2].getbalance("*")), n2t_balance)
assert_equal(self.nodes[2].z_getbalance(n2saddr), n2saddr_balance)
# Get a new unified account on node 0 & generate a UA
n0account = self.nodes[0].z_getnewaccount()['account']
n0ua0 = self.nodes[0].z_getaddressforaccount(n0account)['address']
n0ua0_balance = Decimal('0')
self.check_balance(0, 0, n0ua0, {})
# Prepare to fund the UA from coinbase
source = get_coinbase_address(self.nodes[2])
fee = conventional_fee(3)
recipients = [{"address": n0ua0, "amount": Decimal('10') - fee}]
# If we attempt to spend with the default privacy policy, z_sendmany
# fails because it needs to spend transparent coins in a transaction
# involving a Unified Address.
unified_address_msg = 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'
revealed_senders_msg = 'Insufficient funds: have 0.00, need 10.00; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.'
opid = self.nodes[2].z_sendmany(source, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', unified_address_msg)
# We can't create a transaction with an unknown privacy policy.
assert_raises_message(
JSONRPCException,
'Unknown privacy policy name \'ZcashIsAwesome\'',
self.nodes[2].z_sendmany,
source, recipients, 1, fee, 'ZcashIsAwesome')
# If we set any policy that does not include AllowRevealedSenders,
# z_sendmany also fails.
for (policy, msg) in [
('FullPrivacy', unified_address_msg),
('AllowRevealedAmounts', revealed_senders_msg),
('AllowRevealedRecipients', revealed_senders_msg),
]:
opid = self.nodes[2].z_sendmany(source, recipients, 1, fee, policy)
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', msg)
# By setting the correct policy, we can create the transaction.
opid = self.nodes[2].z_sendmany(source, recipients, 1, fee, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[2], opid)
n2t_balance -= Decimal('10.0')
n0ua0_balance += Decimal('10') - fee
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
n0t_balance += Decimal('10.0') # newly mature
self.check_balance(0, 0, n0ua0, {'sapling': n0ua0_balance})
assert_equal(Decimal(self.nodes[0].getbalance()), n0t_balance)
assert_equal(Decimal(self.nodes[2].getbalance()), n2t_balance)
# Send some funds to a specific legacy taddr that we can spend from
fee = conventional_fee(3)
recipients = [{"address": n2taddr, "amount": Decimal('5')}]
# If we attempt to spend with the default privacy policy, z_sendmany
# returns an error because it needs to create a transparent recipient in
# a transaction involving a Unified Address.
revealed_recipients_msg = "This transaction would have transparent recipients, which is not enabled by default because it will publicly reveal transaction recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway."
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[0], opid, 'failed', revealed_recipients_msg)
# If we set any policy that does not include AllowRevealedRecipients,
# z_sendmany also returns an error.
for policy in [
'FullPrivacy',
'AllowRevealedAmounts',
'AllowRevealedSenders',
'AllowLinkingAccountAddresses',
]:
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee, policy)
wait_and_assert_operationid_status(self.nodes[0], opid, 'failed', revealed_recipients_msg)
# By setting the correct policy, we can create the transaction.
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee, 'AllowRevealedRecipients')
wait_and_assert_operationid_status(self.nodes[0], opid)
n2t_balance += Decimal('5')
n0ua0_balance -= Decimal('5') + fee
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
self.check_balance(0, 0, n0ua0, {'sapling': n0ua0_balance})
assert_equal(Decimal(self.nodes[2].getbalance()), n2t_balance)
# Send some funds to a legacy sapling address that we can spend from
fee = conventional_fee(2)
recipients = [{"address": n2saddr, "amount": Decimal('3')}]
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[0], opid)
n2saddr_balance += Decimal('3')
n0ua0_balance -= Decimal('3') + fee
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
self.check_balance(0, 0, n0ua0, {'sapling': n0ua0_balance})
assert_equal(self.nodes[2].z_getbalance(n2saddr), n2saddr_balance)
# Send funds back from the legacy taddr to the UA. This requires
# AllowRevealedSenders, but we can also use any weaker policy that
# includes it.
fee = conventional_fee(3)
recipients = [{"address": n0ua0, "amount": Decimal('4')}]
opid = self.nodes[2].z_sendmany(n2taddr, recipients, 1, fee, 'AllowFullyTransparent')
wait_and_assert_operationid_status(self.nodes[2], opid)
n0ua0_balance += Decimal('4')
n2t_balance -= Decimal('4') + fee
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
self.check_balance(0, 0, n0ua0, {'sapling': n0ua0_balance})
assert_equal(Decimal(self.nodes[2].getbalance()), n2t_balance)
# Send funds back from the legacy zaddr to the UA
fee = conventional_fee(2)
recipients = [{"address": n0ua0, "amount": Decimal('2')}]
opid = self.nodes[2].z_sendmany(n2saddr, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[2], opid)
n0ua0_balance += Decimal('2')
n2saddr_balance -= Decimal('2') + fee
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
self.check_balance(0, 0, n0ua0, {'sapling': n0ua0_balance})
assert_equal(self.nodes[2].z_getbalance(n2saddr), n2saddr_balance)
#
# Test that z_sendmany avoids UA linkability unless we allow it.
#
# Generate a new account with two new addresses.
n1account = self.nodes[1].z_getnewaccount()['account']
n1ua0 = self.nodes[1].z_getaddressforaccount(n1account)['address']
n1ua1 = self.nodes[1].z_getaddressforaccount(n1account)['address']
# Send funds to the transparent receivers of both addresses.
for ua in [n1ua0, n1ua1]:
taddr = self.nodes[1].z_listunifiedreceivers(ua)['p2pkh']
self.nodes[0].sendtoaddress(taddr, 2)
n0sapling_balance = n0ua0_balance
n1ua0_balance = Decimal('2')
n1ua1_balance = Decimal('2')
n1t_balance = n1ua0_balance + n1ua1_balance
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
self.check_balance(0, 0, n0ua0, {'sapling': n0sapling_balance})
# The account should see all funds.
assert_equal(self.nodes[1].z_getbalanceforaccount(n1account)['pools'], {
'transparent': {'valueZat': n1t_balance * COIN},
})
# The addresses should see only the transparent funds sent to them.
assert_equal(Decimal(self.nodes[1].z_getbalance(n1ua0)), n1ua0_balance)
assert_equal(Decimal(self.nodes[1].z_getbalance(n1ua1)), n1ua1_balance)
# If we try to send 3 ZEC less fee from n1ua0, it will fail with insufficient funds.
fee = conventional_fee(4)
amount = Decimal('3') - fee
recipients = [{"address": n0ua0, "amount": amount}]
linked_addrs_with_coinbase_note_msg = 'Insufficient funds: have 0.00, need 3.00; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker. (This transaction may require selecting transparent coins that were sent to multiple addresses, which is not enabled by default because it would create a public link between those addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to allow this transaction to proceed anyway.)'
linked_addrs_without_coinbase_note_msg = 'Insufficient funds: have 2.00, need 3.00. (This transaction may require selecting transparent coins that were sent to multiple addresses, which is not enabled by default because it would create a public link between those addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to allow this transaction to proceed anyway.)'
revealed_amounts_msg = 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', revealed_amounts_msg)
# If we try it again with any policy that is too strong, it also fails.
for (policy, msg) in [
('FullPrivacy', revealed_amounts_msg),
('AllowRevealedAmounts', linked_addrs_with_coinbase_note_msg),
('AllowRevealedRecipients', linked_addrs_with_coinbase_note_msg),
('AllowRevealedSenders', linked_addrs_without_coinbase_note_msg),
('AllowFullyTransparent', linked_addrs_without_coinbase_note_msg),
]:
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee, policy)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', msg)
# If we try to send just a bit less than we have, it will fail, complaining about dust
opid = self.nodes[1].z_sendmany(n1ua0,
[{"address": n0ua0, "amount": Decimal('3.9999999') - fee}],
1, fee, 'AllowLinkingAccountAddresses')
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', 'Insufficient funds: have 4.00, need 0.00000044 more to avoid creating invalid change output 0.0000001 (dust threshold is 0.00000054).')
# Once we provide a sufficiently weak policy, the transaction succeeds.
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee, 'AllowLinkingAccountAddresses')
wait_and_assert_operationid_status(self.nodes[1], opid)
n0ua0_balance += amount
# Change should be sent to the Sapling change address (because NU5 is not active).
n1sapling_balance = n1t_balance - amount - fee
del n0sapling_balance
del n1t_balance
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
self.check_balance(0, 0, n0ua0, {'sapling': n0ua0_balance})
assert_equal(self.nodes[1].z_getbalanceforaccount(n1account)['pools'], {
'sapling': {'valueZat': n1sapling_balance * COIN},
})
# z_getbalance behaves inconsistently between transparent and shielded
# addresses: for a shielded address it gives the account balance.
assert_equal(Decimal(self.nodes[1].z_getbalance(n1ua0)), n1sapling_balance)
assert_equal(Decimal(self.nodes[1].z_getbalance(n1ua1)), n1sapling_balance)
#
# Test Orchard-only UA before NU5
#
n0orchard_only = self.nodes[0].z_getaddressforaccount(n0account, ["orchard"])['address']
recipients = [{"address": n0orchard_only, "amount": Decimal('1')}]
for (policy, msg) in [
('FullPrivacy', 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'),
('AllowRevealedAmounts', 'This transaction would send to a transparent receiver of a unified address, which is not enabled by default because it will publicly reveal transaction recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway.'),
('AllowRevealedRecipients', 'Could not send to an Orchard-only receiver despite a lax privacy policy, because NU5 has not been activated yet.'),
]:
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee, policy)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', msg)
#
# Test NoPrivacy policy
#
# Send some legacy transparent funds to n1ua0, creating Sapling outputs.
source = get_coinbase_address(self.nodes[2])
fee = conventional_fee(3)
recipients = [{"address": n1ua0, "amount": Decimal('10') - fee}]
# This requires the AllowRevealedSenders policy, but we specify only AllowRevealedAmounts...
opid = self.nodes[2].z_sendmany(source, recipients, 1, fee, 'AllowRevealedAmounts')
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg)
# ... which we can always override with the NoPrivacy policy.
opid = self.nodes[2].z_sendmany(source, recipients, 1, fee, 'NoPrivacy')
wait_and_assert_operationid_status(self.nodes[2], opid)
n1sapling_balance += Decimal('10') - fee
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
assert_equal(self.nodes[1].z_getbalanceforaccount(n1account)['pools'], {
'sapling': {'valueZat': n1sapling_balance * COIN},
})
# Send some funds from node 1's account to a transparent address.
fee = conventional_fee(3)
recipients = [{"address": n2taddr, "amount": Decimal('5')}]
# This requires the AllowRevealedRecipients policy...
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', revealed_recipients_msg)
# ... which we can always override with the NoPrivacy policy.
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee, 'NoPrivacy')
wait_and_assert_operationid_status(self.nodes[1], opid)
n1sapling_balance -= Decimal('5') + fee
# Activate NU5
self.sync_all()
self.nodes[1].generate(10)
self.sync_all()
assert_equal(self.nodes[1].z_getbalanceforaccount(n1account)['pools'], {
'sapling': {'valueZat': n1sapling_balance * COIN},
})
#
# Test sending Sprout funds to Orchard-only UA
#
sproutAddr = self.nodes[2].listaddresses()[0]['sprout']['addresses'][0]
recipients = [{"address": n0orchard_only, "amount": Decimal('100')}]
for (policy, msg) in [
('FullPrivacy', 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'),
('AllowRevealedAmounts', 'This transaction would send to a transparent receiver of a unified address, which is not enabled by default because it will publicly reveal transaction recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway.'),
('AllowRevealedRecipients', 'Could not send to an Orchard-only receiver despite a lax privacy policy, because you are sending from the Sprout pool and there is no transaction version that supports both Sprout and Orchard.'),
('NoPrivacy', 'Could not send to an Orchard-only receiver despite a lax privacy policy, because you are sending from the Sprout pool and there is no transaction version that supports both Sprout and Orchard.'),
]:
opid = self.nodes[2].z_sendmany(sproutAddr, recipients, 1, ZIP_317_FEE, policy)
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', msg)
#
# Test AllowRevealedAmounts policy
#
# Sending some funds to the Orchard pool in n0account ...
n0ua1 = self.nodes[0].z_getaddressforaccount(n0account, ["orchard"])['address']
fee = conventional_fee(4)
recipients = [{"address": n0ua1, "amount": Decimal('5')}]
# Should fail under default and 'FullPrivacy' policies ...
revealed_amounts_msg = 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', revealed_amounts_msg)
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee, 'FullPrivacy')
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', revealed_amounts_msg)
# Should succeed under 'AllowRevealedAmounts'. The change will go to Orchard.
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, fee, 'AllowRevealedAmounts')
wait_and_assert_operationid_status(self.nodes[1], opid)
n0sapling_balance = n0ua0_balance
n0orchard_balance = Decimal('5')
n1orchard_balance = n1sapling_balance - Decimal('5') - fee
del n0ua0_balance
del n1sapling_balance
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
assert_equal(self.nodes[0].z_getbalanceforaccount(n0account)['pools'], {
'sapling': {'valueZat': n0sapling_balance * COIN},
'orchard': {'valueZat': n0orchard_balance * COIN},
})
assert_equal(self.nodes[1].z_getbalanceforaccount(n1account)['pools'], {
'orchard': {'valueZat': n1orchard_balance * COIN},
})
# A total that requires selecting from both pools should fail under default and
# FullPrivacy policies...
fee = conventional_fee(3)
recipients = [{"address": n1ua0, "amount": Decimal('15')}]
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee)
wait_and_assert_operationid_status(self.nodes[0], opid, 'failed', revealed_amounts_msg)
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee, 'FullPrivacy')
wait_and_assert_operationid_status(self.nodes[0], opid, 'failed', revealed_amounts_msg)
# Should succeed under 'AllowRevealedAmounts'
# All funds should be received to the Orchard pool, and all change should
# be optimistically shielded.
fee = conventional_fee(6)
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, fee, 'AllowRevealedAmounts')
wait_and_assert_operationid_status(self.nodes[0], opid)
n0orchard_balance += n0sapling_balance - Decimal('15') - fee
n1orchard_balance += Decimal('15')
del n0sapling_balance
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
assert_equal(self.nodes[0].z_getbalanceforaccount(n0account)['pools'], {
'orchard': {'valueZat': n0orchard_balance * COIN},
})
assert_equal(self.nodes[1].z_getbalanceforaccount(n1account)['pools'], {
'orchard': {'valueZat': n1orchard_balance * COIN},
})
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
#
# Test transparent change
#
fee = conventional_fee(3)
recipients = [{"address": n0ua1, "amount": Decimal('4')}]
# Should fail because this generates transparent change, but we dont have
# `AllowRevealedRecipients`
opid = self.nodes[2].z_sendmany(n2taddr, recipients, 1, fee, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', "This transaction would have transparent change, which is not enabled by default because it will publicly reveal the change address and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway.")
# Should succeed once we include `AllowRevealedRecipients`
opid = self.nodes[2].z_sendmany(n2taddr, recipients, 1, fee, 'AllowFullyTransparent')
wait_and_assert_operationid_status(self.nodes[2], opid)
n0orchard_balance += Decimal('4')
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
assert_equal(self.nodes[0].z_getbalanceforaccount(n0account)['pools'], {
'orchard': {'valueZat': n0orchard_balance * COIN},
})
if __name__ == '__main__':
WalletZSendmanyTest().main()