Changeset View
Changeset View
Standalone View
Standalone View
test/functional/wallet_basic.py
Show All 14 Lines | from test_framework.util import ( | ||||
count_bytes, | count_bytes, | ||||
) | ) | ||||
from test_framework.wallet_util import test_address | from test_framework.wallet_util import test_address | ||||
FAR_IN_THE_FUTURE = 2000000000 | FAR_IN_THE_FUTURE = 2000000000 | ||||
class WalletTest(BitcoinTestFramework): | class WalletTest(BitcoinTestFramework): | ||||
WELLINGTON_FAR_FUTURE = f"-wellingtonactivationtime={int(9e9)}" | WELLINGTON_FAR_FUTURE = f"-wellingtonactivationtime={int(9e9)}" | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 4 | self.num_nodes = 4 | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
self.extra_args = [ | self.extra_args = [ | ||||
[ | [ | ||||
"-acceptnonstdtxn=1", | "-acceptnonstdtxn=1", | ||||
Show All 13 Lines | def setup_network(self): | ||||
self.setup_nodes() | self.setup_nodes() | ||||
# Only need nodes 0-2 running at start of test | # Only need nodes 0-2 running at start of test | ||||
self.stop_node(3) | self.stop_node(3) | ||||
self.connect_nodes(0, 1) | self.connect_nodes(0, 1) | ||||
self.connect_nodes(1, 2) | self.connect_nodes(1, 2) | ||||
self.connect_nodes(0, 2) | self.connect_nodes(0, 2) | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
def check_fee_amount(self, curr_balance, | def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): | ||||
balance_with_fee, fee_per_byte, tx_size): | |||||
"""Return curr_balance after asserting the fee was in range""" | """Return curr_balance after asserting the fee was in range""" | ||||
fee = balance_with_fee - curr_balance | fee = balance_with_fee - curr_balance | ||||
assert_fee_amount(fee, tx_size, fee_per_byte * 1000) | assert_fee_amount(fee, tx_size, fee_per_byte * 1000) | ||||
return curr_balance | return curr_balance | ||||
def run_test(self): | def run_test(self): | ||||
# Check that there's no UTXO on none of the nodes | # Check that there's no UTXO on none of the nodes | ||||
assert_equal(len(self.nodes[0].listunspent()), 0) | assert_equal(len(self.nodes[0].listunspent()), 0) | ||||
assert_equal(len(self.nodes[1].listunspent()), 0) | assert_equal(len(self.nodes[1].listunspent()), 0) | ||||
assert_equal(len(self.nodes[2].listunspent()), 0) | assert_equal(len(self.nodes[2].listunspent()), 0) | ||||
self.log.info("Mining blocks...") | self.log.info("Mining blocks...") | ||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op) | self.generate(self.nodes[0], 1, sync_fun=self.no_op) | ||||
walletinfo = self.nodes[0].getwalletinfo() | walletinfo = self.nodes[0].getwalletinfo() | ||||
assert_equal(walletinfo['immature_balance'], 50000000) | assert_equal(walletinfo["immature_balance"], 50000000) | ||||
assert_equal(walletinfo['balance'], 0) | assert_equal(walletinfo["balance"], 0) | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
self.generate(self.nodes[1], 101, | self.generate( | ||||
sync_fun=lambda: self.sync_all(self.nodes[0:3])) | self.nodes[1], 101, sync_fun=lambda: self.sync_all(self.nodes[0:3]) | ||||
) | |||||
assert_equal(self.nodes[0].getbalance(), 50000000) | assert_equal(self.nodes[0].getbalance(), 50000000) | ||||
assert_equal(self.nodes[1].getbalance(), 50000000) | assert_equal(self.nodes[1].getbalance(), 50000000) | ||||
assert_equal(self.nodes[2].getbalance(), 0) | assert_equal(self.nodes[2].getbalance(), 0) | ||||
# Check that only first and second nodes have UTXOs | # Check that only first and second nodes have UTXOs | ||||
utxos = self.nodes[0].listunspent() | utxos = self.nodes[0].listunspent() | ||||
assert_equal(len(utxos), 1) | assert_equal(len(utxos), 1) | ||||
assert_equal(len(self.nodes[1].listunspent()), 1) | assert_equal(len(self.nodes[1].listunspent()), 1) | ||||
assert_equal(len(self.nodes[2].listunspent()), 0) | assert_equal(len(self.nodes[2].listunspent()), 0) | ||||
self.log.info("test gettxout") | self.log.info("test gettxout") | ||||
confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] | confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] | ||||
# First, outputs that are unspent both in the chain and in the | # First, outputs that are unspent both in the chain and in the | ||||
# mempool should appear with or without include_mempool | # mempool should appear with or without include_mempool | ||||
txout = self.nodes[0].gettxout( | txout = self.nodes[0].gettxout( | ||||
txid=confirmed_txid, n=confirmed_index, include_mempool=False) | txid=confirmed_txid, n=confirmed_index, include_mempool=False | ||||
assert_equal(txout['value'], 50000000) | ) | ||||
assert_equal(txout["value"], 50000000) | |||||
txout = self.nodes[0].gettxout( | txout = self.nodes[0].gettxout( | ||||
txid=confirmed_txid, n=confirmed_index, include_mempool=True) | txid=confirmed_txid, n=confirmed_index, include_mempool=True | ||||
assert_equal(txout['value'], 50000000) | ) | ||||
assert_equal(txout["value"], 50000000) | |||||
# Send 21,000,000 XEC from 0 to 2 using sendtoaddress call. | # Send 21,000,000 XEC from 0 to 2 using sendtoaddress call. | ||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11000000) | self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11000000) | ||||
mempool_txid = self.nodes[0].sendtoaddress( | mempool_txid = self.nodes[0].sendtoaddress( | ||||
self.nodes[2].getnewaddress(), 10000000) | self.nodes[2].getnewaddress(), 10000000 | ||||
) | |||||
self.log.info("test gettxout (second part)") | self.log.info("test gettxout (second part)") | ||||
# utxo spent in mempool should be visible if you exclude mempool | # utxo spent in mempool should be visible if you exclude mempool | ||||
# but invisible if you include mempool | # but invisible if you include mempool | ||||
txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False) | txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False) | ||||
assert_equal(txout['value'], 50000000) | assert_equal(txout["value"], 50000000) | ||||
txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True) | txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True) | ||||
assert txout is None | assert txout is None | ||||
# new utxo from mempool should be invisible if you exclude mempool | # new utxo from mempool should be invisible if you exclude mempool | ||||
# but visible if you include mempool | # but visible if you include mempool | ||||
txout = self.nodes[0].gettxout(mempool_txid, 0, False) | txout = self.nodes[0].gettxout(mempool_txid, 0, False) | ||||
assert txout is None | assert txout is None | ||||
txout1 = self.nodes[0].gettxout(mempool_txid, 0, True) | txout1 = self.nodes[0].gettxout(mempool_txid, 0, True) | ||||
txout2 = self.nodes[0].gettxout(mempool_txid, 1, True) | txout2 = self.nodes[0].gettxout(mempool_txid, 1, True) | ||||
# note the mempool tx will have randomly assigned indices | # note the mempool tx will have randomly assigned indices | ||||
# but 10 will go to node2 and the rest will go to node0 | # but 10 will go to node2 and the rest will go to node0 | ||||
balance = self.nodes[0].getbalance() | balance = self.nodes[0].getbalance() | ||||
assert_equal({txout1['value'], txout2['value']}, | assert_equal({txout1["value"], txout2["value"]}, {10000000, balance}) | ||||
{10000000, balance}) | |||||
walletinfo = self.nodes[0].getwalletinfo() | walletinfo = self.nodes[0].getwalletinfo() | ||||
assert_equal(walletinfo['immature_balance'], 0) | assert_equal(walletinfo["immature_balance"], 0) | ||||
# Have node0 mine a block, thus it will collect its own fee. | # Have node0 mine a block, thus it will collect its own fee. | ||||
self.generate( | self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | |||||
# Exercise locking of unspent outputs | # Exercise locking of unspent outputs | ||||
unspent_0 = self.nodes[2].listunspent()[0] | unspent_0 = self.nodes[2].listunspent()[0] | ||||
unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} | unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} | ||||
assert_raises_rpc_error(-8, "Invalid parameter, expected locked output", | assert_raises_rpc_error( | ||||
self.nodes[2].lockunspent, True, [unspent_0]) | -8, | ||||
"Invalid parameter, expected locked output", | |||||
self.nodes[2].lockunspent, | |||||
True, | |||||
[unspent_0], | |||||
) | |||||
self.nodes[2].lockunspent(False, [unspent_0]) | self.nodes[2].lockunspent(False, [unspent_0]) | ||||
assert_raises_rpc_error(-8, "Invalid parameter, output already locked", | assert_raises_rpc_error( | ||||
self.nodes[2].lockunspent, False, [unspent_0]) | -8, | ||||
assert_raises_rpc_error(-6, "Insufficient funds", | "Invalid parameter, output already locked", | ||||
self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20000000) | self.nodes[2].lockunspent, | ||||
False, | |||||
[unspent_0], | |||||
) | |||||
assert_raises_rpc_error( | |||||
-6, | |||||
"Insufficient funds", | |||||
self.nodes[2].sendtoaddress, | |||||
self.nodes[2].getnewaddress(), | |||||
20000000, | |||||
) | |||||
assert_equal([unspent_0], self.nodes[2].listlockunspent()) | assert_equal([unspent_0], self.nodes[2].listlockunspent()) | ||||
self.nodes[2].lockunspent(True, [unspent_0]) | self.nodes[2].lockunspent(True, [unspent_0]) | ||||
assert_equal(len(self.nodes[2].listlockunspent()), 0) | assert_equal(len(self.nodes[2].listlockunspent()), 0) | ||||
assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')", | assert_raises_rpc_error( | ||||
self.nodes[2].lockunspent, False, | -8, | ||||
[{"txid": "0000000000000000000000000000000000", "vout": 0}]) | ( | ||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", | "txid must be of length 64 (not 34, for" | ||||
self.nodes[2].lockunspent, False, | " '0000000000000000000000000000000000')" | ||||
[{"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", "vout": 0}]) | ), | ||||
assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", | self.nodes[2].lockunspent, | ||||
self.nodes[2].lockunspent, False, | False, | ||||
[{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0}]) | [{"txid": "0000000000000000000000000000000000", "vout": 0}], | ||||
assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds", | ) | ||||
self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}]) | assert_raises_rpc_error( | ||||
-8, | |||||
( | |||||
"txid must be hexadecimal string (not" | |||||
" 'ZZZ0000000000000000000000000000000000000000000000000000000000000')" | |||||
), | |||||
self.nodes[2].lockunspent, | |||||
False, | |||||
[ | |||||
{ | |||||
"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", | |||||
"vout": 0, | |||||
} | |||||
], | |||||
) | |||||
assert_raises_rpc_error( | |||||
-8, | |||||
"Invalid parameter, unknown transaction", | |||||
self.nodes[2].lockunspent, | |||||
False, | |||||
[ | |||||
{ | |||||
"txid": "0000000000000000000000000000000000000000000000000000000000000000", | |||||
"vout": 0, | |||||
} | |||||
], | |||||
) | |||||
assert_raises_rpc_error( | |||||
-8, | |||||
"Invalid parameter, vout index out of bounds", | |||||
self.nodes[2].lockunspent, | |||||
False, | |||||
[{"txid": unspent_0["txid"], "vout": 999}], | |||||
) | |||||
# The lock on a manually selected output is ignored | # The lock on a manually selected output is ignored | ||||
unspent_0 = self.nodes[1].listunspent()[0] | unspent_0 = self.nodes[1].listunspent()[0] | ||||
self.nodes[1].lockunspent(False, [unspent_0]) | self.nodes[1].lockunspent(False, [unspent_0]) | ||||
tx = self.nodes[1].createrawtransaction( | tx = self.nodes[1].createrawtransaction( | ||||
[unspent_0], {self.nodes[1].getnewaddress(): 1000000}) | [unspent_0], {self.nodes[1].getnewaddress(): 1000000} | ||||
tx = self.nodes[1].fundrawtransaction(tx)['hex'] | ) | ||||
tx = self.nodes[1].fundrawtransaction(tx)["hex"] | |||||
self.nodes[1].fundrawtransaction(tx, {"lockUnspents": True}) | self.nodes[1].fundrawtransaction(tx, {"lockUnspents": True}) | ||||
# fundrawtransaction can lock an input | # fundrawtransaction can lock an input | ||||
self.nodes[1].lockunspent(True, [unspent_0]) | self.nodes[1].lockunspent(True, [unspent_0]) | ||||
assert_equal(len(self.nodes[1].listlockunspent()), 0) | assert_equal(len(self.nodes[1].listlockunspent()), 0) | ||||
tx = self.nodes[1].fundrawtransaction( | tx = self.nodes[1].fundrawtransaction(tx, {"lockUnspents": True})["hex"] | ||||
tx, {"lockUnspents": True})['hex'] | |||||
assert_equal(len(self.nodes[1].listlockunspent()), 1) | assert_equal(len(self.nodes[1].listlockunspent()), 1) | ||||
# Send transaction | # Send transaction | ||||
tx = self.nodes[1].signrawtransactionwithwallet(tx)["hex"] | tx = self.nodes[1].signrawtransactionwithwallet(tx)["hex"] | ||||
self.nodes[1].sendrawtransaction(tx) | self.nodes[1].sendrawtransaction(tx) | ||||
assert_equal(len(self.nodes[1].listlockunspent()), 0) | assert_equal(len(self.nodes[1].listlockunspent()), 0) | ||||
# Have node1 generate 100 blocks (so node0 can recover the fee) | # Have node1 generate 100 blocks (so node0 can recover the fee) | ||||
self.generate(self.nodes[1], 100, | self.generate( | ||||
sync_fun=lambda: self.sync_all(self.nodes[0:3])) | self.nodes[1], 100, sync_fun=lambda: self.sync_all(self.nodes[0:3]) | ||||
) | |||||
# node0 should end up with 100 btc in block rewards plus fees, but | # node0 should end up with 100 btc in block rewards plus fees, but | ||||
# minus the 21 plus fees sent to node2 | # minus the 21 plus fees sent to node2 | ||||
assert_equal(self.nodes[0].getbalance(), 100000000 - 21000000) | assert_equal(self.nodes[0].getbalance(), 100000000 - 21000000) | ||||
assert_equal(self.nodes[2].getbalance(), 21000000) | assert_equal(self.nodes[2].getbalance(), 21000000) | ||||
# Node0 should have two unspent outputs. | # Node0 should have two unspent outputs. | ||||
# Create a couple of transactions to send them to node2, submit them through | # Create a couple of transactions to send them to node2, submit them through | ||||
# node1, and make sure both node0 and node2 pick them up properly: | # node1, and make sure both node0 and node2 pick them up properly: | ||||
node0utxos = self.nodes[0].listunspent(1) | node0utxos = self.nodes[0].listunspent(1) | ||||
assert_equal(len(node0utxos), 2) | assert_equal(len(node0utxos), 2) | ||||
# create both transactions | # create both transactions | ||||
txns_to_send = [] | txns_to_send = [] | ||||
for utxo in node0utxos: | for utxo in node0utxos: | ||||
inputs = [] | inputs = [] | ||||
outputs = {} | outputs = {} | ||||
inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) | inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) | ||||
outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3000000 | outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3000000 | ||||
raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) | raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) | ||||
txns_to_send.append( | txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx)) | ||||
self.nodes[0].signrawtransactionwithwallet(raw_tx)) | |||||
# Have node 1 (miner) send the transactions | # Have node 1 (miner) send the transactions | ||||
self.nodes[1].sendrawtransaction( | self.nodes[1].sendrawtransaction(hexstring=txns_to_send[0]["hex"], maxfeerate=0) | ||||
hexstring=txns_to_send[0]["hex"], maxfeerate=0) | self.nodes[1].sendrawtransaction(hexstring=txns_to_send[1]["hex"], maxfeerate=0) | ||||
self.nodes[1].sendrawtransaction( | |||||
hexstring=txns_to_send[1]["hex"], maxfeerate=0) | |||||
# Have node1 mine a block to confirm transactions: | # Have node1 mine a block to confirm transactions: | ||||
self.generate( | self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | |||||
assert_equal(self.nodes[0].getbalance(), 0) | assert_equal(self.nodes[0].getbalance(), 0) | ||||
assert_equal(self.nodes[2].getbalance(), 94000000) | assert_equal(self.nodes[2].getbalance(), 94000000) | ||||
# Verify that a spent output cannot be locked anymore | # Verify that a spent output cannot be locked anymore | ||||
spent_0 = {"txid": node0utxos[0]["txid"], | spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]} | ||||
"vout": node0utxos[0]["vout"]} | assert_raises_rpc_error( | ||||
assert_raises_rpc_error(-8, "Invalid parameter, expected unspent output", | -8, | ||||
self.nodes[0].lockunspent, False, [spent_0]) | "Invalid parameter, expected unspent output", | ||||
self.nodes[0].lockunspent, | |||||
False, | |||||
[spent_0], | |||||
) | |||||
# Send 10,000,000 XEC normal | # Send 10,000,000 XEC normal | ||||
old_balance = self.nodes[2].getbalance() | old_balance = self.nodes[2].getbalance() | ||||
address = self.nodes[0].getnewaddress("test") | address = self.nodes[0].getnewaddress("test") | ||||
fee_per_byte = Decimal('1000') / 1000 | fee_per_byte = Decimal("1000") / 1000 | ||||
self.nodes[2].settxfee(fee_per_byte * 1000) | self.nodes[2].settxfee(fee_per_byte * 1000) | ||||
txid = self.nodes[2].sendtoaddress(address, 10000000, "", "", False) | txid = self.nodes[2].sendtoaddress(address, 10000000, "", "", False) | ||||
self.generate( | self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ctx = FromHex(CTransaction(), self.nodes[2].gettransaction(txid)["hex"]) | ||||
ctx = FromHex(CTransaction(), | |||||
self.nodes[2].gettransaction(txid)['hex']) | node_2_bal = self.check_fee_amount( | ||||
self.nodes[2].getbalance(), | |||||
node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), old_balance - Decimal('10000000'), | old_balance - Decimal("10000000"), | ||||
fee_per_byte, ctx.billable_size()) | fee_per_byte, | ||||
assert_equal(self.nodes[0].getbalance(), Decimal('10000000')) | ctx.billable_size(), | ||||
) | |||||
assert_equal(self.nodes[0].getbalance(), Decimal("10000000")) | |||||
# Send 10,000,000 XEC with subtract fee from amount | # Send 10,000,000 XEC with subtract fee from amount | ||||
txid = self.nodes[2].sendtoaddress(address, 10000000, "", "", True) | txid = self.nodes[2].sendtoaddress(address, 10000000, "", "", True) | ||||
self.generate( | self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | node_2_bal -= Decimal("10000000") | ||||
node_2_bal -= Decimal('10000000') | |||||
assert_equal(self.nodes[2].getbalance(), node_2_bal) | assert_equal(self.nodes[2].getbalance(), node_2_bal) | ||||
node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal( | node_0_bal = self.check_fee_amount( | ||||
'20000000'), fee_per_byte, count_bytes(self.nodes[2].gettransaction(txid)['hex'])) | self.nodes[0].getbalance(), | ||||
Decimal("20000000"), | |||||
fee_per_byte, | |||||
count_bytes(self.nodes[2].gettransaction(txid)["hex"]), | |||||
) | |||||
self.log.info("Test sendmany") | self.log.info("Test sendmany") | ||||
# Sendmany 10,000,000 XEC | # Sendmany 10,000,000 XEC | ||||
txid = self.nodes[2].sendmany('', {address: 10000000}, 0, "", []) | txid = self.nodes[2].sendmany("", {address: 10000000}, 0, "", []) | ||||
self.generate( | self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | node_0_bal += Decimal("10000000") | ||||
node_0_bal += Decimal('10000000') | ctx = FromHex(CTransaction(), self.nodes[2].gettransaction(txid)["hex"]) | ||||
ctx = FromHex(CTransaction(), | node_2_bal = self.check_fee_amount( | ||||
self.nodes[2].gettransaction(txid)['hex']) | self.nodes[2].getbalance(), | ||||
node_2_bal = self.check_fee_amount(self.nodes[2].getbalance( | node_2_bal - Decimal("10000000"), | ||||
), node_2_bal - Decimal('10000000'), fee_per_byte, ctx.billable_size()) | fee_per_byte, | ||||
ctx.billable_size(), | |||||
) | |||||
assert_equal(self.nodes[0].getbalance(), node_0_bal) | assert_equal(self.nodes[0].getbalance(), node_0_bal) | ||||
# Sendmany 10,000,000 XEC with subtract fee from amount | # Sendmany 10,000,000 XEC with subtract fee from amount | ||||
txid = self.nodes[2].sendmany( | txid = self.nodes[2].sendmany("", {address: 10000000}, 0, "", [address]) | ||||
'', {address: 10000000}, 0, "", [address]) | self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.generate( | node_2_bal -= Decimal("10000000") | ||||
self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | |||||
node_2_bal -= Decimal('10000000') | |||||
assert_equal(self.nodes[2].getbalance(), node_2_bal) | assert_equal(self.nodes[2].getbalance(), node_2_bal) | ||||
ctx = FromHex(CTransaction(), | ctx = FromHex(CTransaction(), self.nodes[2].gettransaction(txid)["hex"]) | ||||
self.nodes[2].gettransaction(txid)['hex']) | node_0_bal = self.check_fee_amount( | ||||
node_0_bal = self.check_fee_amount(self.nodes[0].getbalance( | self.nodes[0].getbalance(), | ||||
), node_0_bal + Decimal('10000000'), fee_per_byte, ctx.billable_size()) | node_0_bal + Decimal("10000000"), | ||||
fee_per_byte, | |||||
ctx.billable_size(), | |||||
) | |||||
self.start_node(3, self.extra_args[3]) | self.start_node(3, self.extra_args[3]) | ||||
self.connect_nodes(0, 3) | self.connect_nodes(0, 3) | ||||
self.sync_all() | self.sync_all() | ||||
# check if we can list zero value tx as available coins | # check if we can list zero value tx as available coins | ||||
# 1. create raw_tx | # 1. create raw_tx | ||||
# 2. hex-changed one output to 0.0 | # 2. hex-changed one output to 0.0 | ||||
# 3. sign and send | # 3. sign and send | ||||
# 4. check if recipient (node0) can list the zero value tx | # 4. check if recipient (node0) can list the zero value tx | ||||
usp = self.nodes[1].listunspent( | usp = self.nodes[1].listunspent(query_options={"minimumAmount": "49998000"})[0] | ||||
query_options={'minimumAmount': '49998000'})[0] | inputs = [{"txid": usp["txid"], "vout": usp["vout"]}] | ||||
inputs = [{"txid": usp['txid'], "vout": usp['vout']}] | outputs = { | ||||
outputs = {self.nodes[1].getnewaddress(): 49998000, | self.nodes[1].getnewaddress(): 49998000, | ||||
self.nodes[0].getnewaddress(): 11110000} | self.nodes[0].getnewaddress(): 11110000, | ||||
} | |||||
rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace( | |||||
"c0833842", "00000000") # replace 11.11 with 0.0 (int32) | rawTx = ( | ||||
self.nodes[1] | |||||
.createrawtransaction(inputs, outputs) | |||||
.replace("c0833842", "00000000") | |||||
) # replace 11.11 with 0.0 (int32) | |||||
signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(rawTx) | signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(rawTx) | ||||
decoded_raw_tx = self.nodes[1].decoderawtransaction( | decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx["hex"]) | ||||
signed_raw_tx['hex']) | zero_value_txid = decoded_raw_tx["txid"] | ||||
zero_value_txid = decoded_raw_tx['txid'] | self.nodes[1].sendrawtransaction(signed_raw_tx["hex"]) | ||||
self.nodes[1].sendrawtransaction(signed_raw_tx['hex']) | |||||
self.sync_all() | self.sync_all() | ||||
self.generate(self.nodes[1], 1) # mine a block | self.generate(self.nodes[1], 1) # mine a block | ||||
# zero value tx must be in listunspents output | # zero value tx must be in listunspents output | ||||
unspent_txs = self.nodes[0].listunspent() | unspent_txs = self.nodes[0].listunspent() | ||||
found = False | found = False | ||||
for uTx in unspent_txs: | for uTx in unspent_txs: | ||||
if uTx['txid'] == zero_value_txid: | if uTx["txid"] == zero_value_txid: | ||||
found = True | found = True | ||||
assert_equal(uTx['amount'], Decimal('0')) | assert_equal(uTx["amount"], Decimal("0")) | ||||
assert found | assert found | ||||
# do some -walletbroadcast tests | # do some -walletbroadcast tests | ||||
self.stop_nodes() | self.stop_nodes() | ||||
self.start_node(0, self.extra_args[0] + ["-walletbroadcast=0"]) | self.start_node(0, self.extra_args[0] + ["-walletbroadcast=0"]) | ||||
self.start_node(1, self.extra_args[1] + ["-walletbroadcast=0"]) | self.start_node(1, self.extra_args[1] + ["-walletbroadcast=0"]) | ||||
self.start_node(2, self.extra_args[2] + ["-walletbroadcast=0"]) | self.start_node(2, self.extra_args[2] + ["-walletbroadcast=0"]) | ||||
self.connect_nodes(0, 1) | self.connect_nodes(0, 1) | ||||
self.connect_nodes(1, 2) | self.connect_nodes(1, 2) | ||||
self.connect_nodes(0, 2) | self.connect_nodes(0, 2) | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
txid_not_broadcast = self.nodes[0].sendtoaddress( | txid_not_broadcast = self.nodes[0].sendtoaddress( | ||||
self.nodes[2].getnewaddress(), 2000000) | self.nodes[2].getnewaddress(), 2000000 | ||||
) | |||||
tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) | tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) | ||||
# mine a block, tx should not be in there | # mine a block, tx should not be in there | ||||
self.generate( | self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | |||||
# should not be changed because tx was not broadcasted | # should not be changed because tx was not broadcasted | ||||
assert_equal(self.nodes[2].getbalance(), node_2_bal) | assert_equal(self.nodes[2].getbalance(), node_2_bal) | ||||
# now broadcast from another node, mine a block, sync, and check the | # now broadcast from another node, mine a block, sync, and check the | ||||
# balance | # balance | ||||
self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex']) | self.nodes[1].sendrawtransaction(tx_obj_not_broadcast["hex"]) | ||||
self.generate( | self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | |||||
node_2_bal += 2000000 | node_2_bal += 2000000 | ||||
tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) | tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) | ||||
assert_equal(self.nodes[2].getbalance(), node_2_bal) | assert_equal(self.nodes[2].getbalance(), node_2_bal) | ||||
# create another tx | # create another tx | ||||
txid_not_broadcast = self.nodes[0].sendtoaddress( | txid_not_broadcast = self.nodes[0].sendtoaddress( | ||||
self.nodes[2].getnewaddress(), 2000000) | self.nodes[2].getnewaddress(), 2000000 | ||||
) | |||||
# restart the nodes with -walletbroadcast=1 | # restart the nodes with -walletbroadcast=1 | ||||
self.stop_nodes() | self.stop_nodes() | ||||
self.start_node(0, self.extra_args[0]) | self.start_node(0, self.extra_args[0]) | ||||
self.start_node(1, self.extra_args[1]) | self.start_node(1, self.extra_args[1]) | ||||
self.start_node(2, self.extra_args[2]) | self.start_node(2, self.extra_args[2]) | ||||
self.connect_nodes(0, 1) | self.connect_nodes(0, 1) | ||||
self.connect_nodes(1, 2) | self.connect_nodes(1, 2) | ||||
self.connect_nodes(0, 2) | self.connect_nodes(0, 2) | ||||
self.sync_blocks(self.nodes[0:3]) | self.sync_blocks(self.nodes[0:3]) | ||||
self.generate( | self.generate( | ||||
self.nodes[0], 1, sync_fun=lambda: self.sync_blocks(self.nodes[0:3])) | self.nodes[0], 1, sync_fun=lambda: self.sync_blocks(self.nodes[0:3]) | ||||
) | |||||
node_2_bal += 2000000 | node_2_bal += 2000000 | ||||
# tx should be added to balance because after restarting the nodes tx | # tx should be added to balance because after restarting the nodes tx | ||||
# should be broadcasted | # should be broadcasted | ||||
assert_equal(self.nodes[2].getbalance(), node_2_bal) | assert_equal(self.nodes[2].getbalance(), node_2_bal) | ||||
# send a tx with value in a string (PR#6380 +) | # send a tx with value in a string (PR#6380 +) | ||||
txid = self.nodes[0].sendtoaddress( | txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2000000") | ||||
self.nodes[2].getnewaddress(), "2000000") | |||||
tx_obj = self.nodes[0].gettransaction(txid) | tx_obj = self.nodes[0].gettransaction(txid) | ||||
assert_equal(tx_obj['amount'], Decimal('-2000000')) | assert_equal(tx_obj["amount"], Decimal("-2000000")) | ||||
txid = self.nodes[0].sendtoaddress( | txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "10000") | ||||
self.nodes[2].getnewaddress(), "10000") | |||||
tx_obj = self.nodes[0].gettransaction(txid) | tx_obj = self.nodes[0].gettransaction(txid) | ||||
assert_equal(tx_obj['amount'], Decimal('-10000')) | assert_equal(tx_obj["amount"], Decimal("-10000")) | ||||
# check if JSON parser can handle scientific notation in strings | # check if JSON parser can handle scientific notation in strings | ||||
txid = self.nodes[0].sendtoaddress( | txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e3") | ||||
self.nodes[2].getnewaddress(), "1e3") | |||||
tx_obj = self.nodes[0].gettransaction(txid) | tx_obj = self.nodes[0].gettransaction(txid) | ||||
assert_equal(tx_obj['amount'], Decimal('-1000')) | assert_equal(tx_obj["amount"], Decimal("-1000")) | ||||
# General checks for errors from incorrect inputs | # General checks for errors from incorrect inputs | ||||
# This will raise an exception because the amount is negative | # This will raise an exception because the amount is negative | ||||
assert_raises_rpc_error(-3, | assert_raises_rpc_error( | ||||
-3, | |||||
"Amount out of range", | "Amount out of range", | ||||
self.nodes[0].sendtoaddress, | self.nodes[0].sendtoaddress, | ||||
self.nodes[2].getnewaddress(), | self.nodes[2].getnewaddress(), | ||||
"-1") | "-1", | ||||
) | |||||
# This will raise an exception because the amount type is wrong | # This will raise an exception because the amount type is wrong | ||||
assert_raises_rpc_error(-3, "Invalid amount", | assert_raises_rpc_error( | ||||
self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") | -3, | ||||
"Invalid amount", | |||||
self.nodes[0].sendtoaddress, | |||||
self.nodes[2].getnewaddress(), | |||||
"1f-4", | |||||
) | |||||
# This will raise an exception since generate does not accept a string | # This will raise an exception since generate does not accept a string | ||||
assert_raises_rpc_error(-1, "not an integer", | assert_raises_rpc_error(-1, "not an integer", self.generate, self.nodes[0], "2") | ||||
self.generate, self.nodes[0], "2") | |||||
# This will raise an exception for the invalid private key format | # This will raise an exception for the invalid private key format | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
"Invalid private key encoding", | -5, "Invalid private key encoding", self.nodes[0].importprivkey, "invalid" | ||||
self.nodes[0].importprivkey, | ) | ||||
"invalid") | |||||
# This will raise an exception for importing an address with the PS2H | # This will raise an exception for importing an address with the PS2H | ||||
# flag | # flag | ||||
temp_address = self.nodes[1].getnewaddress() | temp_address = self.nodes[1].getnewaddress() | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
-5, | |||||
"Cannot use the p2sh flag with an address - use a script instead", | "Cannot use the p2sh flag with an address - use a script instead", | ||||
self.nodes[0].importaddress, | self.nodes[0].importaddress, | ||||
temp_address, | temp_address, | ||||
"label", | "label", | ||||
False, | False, | ||||
True) | True, | ||||
) | |||||
# This will raise an exception for attempting to dump the private key | # This will raise an exception for attempting to dump the private key | ||||
# of an address you do not own | # of an address you do not own | ||||
assert_raises_rpc_error(-4, | assert_raises_rpc_error( | ||||
"Private key for address", | -4, "Private key for address", self.nodes[0].dumpprivkey, temp_address | ||||
self.nodes[0].dumpprivkey, | ) | ||||
temp_address) | |||||
# This will raise an exception for attempting to get the private key of | # This will raise an exception for attempting to get the private key of | ||||
# an invalid Bitcoin address | # an invalid Bitcoin address | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
"Invalid Bitcoin address", | -5, "Invalid Bitcoin address", self.nodes[0].dumpprivkey, "invalid" | ||||
self.nodes[0].dumpprivkey, | ) | ||||
"invalid") | |||||
# This will raise an exception for attempting to set a label for an | # This will raise an exception for attempting to set a label for an | ||||
# invalid Bitcoin address | # invalid Bitcoin address | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
-5, | |||||
"Invalid Bitcoin address", | "Invalid Bitcoin address", | ||||
self.nodes[0].setlabel, | self.nodes[0].setlabel, | ||||
"invalid address", | "invalid address", | ||||
"label") | "label", | ||||
) | |||||
# This will raise an exception for importing an invalid address | # This will raise an exception for importing an invalid address | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
-5, | |||||
"Invalid Bitcoin address or script", | "Invalid Bitcoin address or script", | ||||
self.nodes[0].importaddress, | self.nodes[0].importaddress, | ||||
"invalid") | "invalid", | ||||
) | |||||
# This will raise an exception for attempting to import a pubkey that | # This will raise an exception for attempting to import a pubkey that | ||||
# isn't in hex | # isn't in hex | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
"Pubkey must be a hex string", | -5, "Pubkey must be a hex string", self.nodes[0].importpubkey, "not hex" | ||||
self.nodes[0].importpubkey, | ) | ||||
"not hex") | |||||
# This will raise an exception for importing an invalid pubkey | # This will raise an exception for importing an invalid pubkey | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error( | ||||
-5, | |||||
"Pubkey is not a valid public key", | "Pubkey is not a valid public key", | ||||
self.nodes[0].importpubkey, | self.nodes[0].importpubkey, | ||||
"5361746f736869204e616b616d6f746f") | "5361746f736869204e616b616d6f746f", | ||||
) | |||||
# Import address and private key to check correct behavior of spendable unspents | # Import address and private key to check correct behavior of spendable unspents | ||||
# 1. Send some coins to generate new UTXO | # 1. Send some coins to generate new UTXO | ||||
address_to_import = self.nodes[2].getnewaddress() | address_to_import = self.nodes[2].getnewaddress() | ||||
txid = self.nodes[0].sendtoaddress(address_to_import, 1000000) | txid = self.nodes[0].sendtoaddress(address_to_import, 1000000) | ||||
self.generate( | self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | ||||
self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | |||||
# 2. Import address from node2 to node1 | # 2. Import address from node2 to node1 | ||||
self.nodes[1].importaddress(address_to_import) | self.nodes[1].importaddress(address_to_import) | ||||
# 3. Validate that the imported address is watch-only on node1 | # 3. Validate that the imported address is watch-only on node1 | ||||
assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"] | assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"] | ||||
# 4. Check that the unspents after import are not spendable | # 4. Check that the unspents after import are not spendable | ||||
assert_array_result(self.nodes[1].listunspent(), | assert_array_result( | ||||
self.nodes[1].listunspent(), | |||||
{"address": address_to_import}, | {"address": address_to_import}, | ||||
{"spendable": False}) | {"spendable": False}, | ||||
) | |||||
# 5. Import private key of the previously imported address on node1 | # 5. Import private key of the previously imported address on node1 | ||||
priv_key = self.nodes[2].dumpprivkey(address_to_import) | priv_key = self.nodes[2].dumpprivkey(address_to_import) | ||||
self.nodes[1].importprivkey(priv_key) | self.nodes[1].importprivkey(priv_key) | ||||
# 6. Check that the unspents are now spendable on node1 | # 6. Check that the unspents are now spendable on node1 | ||||
assert_array_result(self.nodes[1].listunspent(), | assert_array_result( | ||||
self.nodes[1].listunspent(), | |||||
{"address": address_to_import}, | {"address": address_to_import}, | ||||
{"spendable": True}) | {"spendable": True}, | ||||
) | |||||
# Mine a block from node0 to an address from node1 | # Mine a block from node0 to an address from node1 | ||||
coinbase_addr = self.nodes[1].getnewaddress() | coinbase_addr = self.nodes[1].getnewaddress() | ||||
block_hash = self.generatetoaddress( | block_hash = self.generatetoaddress( | ||||
self.nodes[0], 1, coinbase_addr, sync_fun=lambda: self.sync_all(self.nodes[0:3]))[0] | self.nodes[0], | ||||
coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0] | 1, | ||||
coinbase_addr, | |||||
sync_fun=lambda: self.sync_all(self.nodes[0:3]), | |||||
)[0] | |||||
coinbase_txid = self.nodes[0].getblock(block_hash)["tx"][0] | |||||
# Check that the txid and balance is found by node1 | # Check that the txid and balance is found by node1 | ||||
self.nodes[1].gettransaction(coinbase_txid) | self.nodes[1].gettransaction(coinbase_txid) | ||||
# check if wallet or blockchain maintenance changes the balance | # check if wallet or blockchain maintenance changes the balance | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
blocks = self.generate( | blocks = self.generate( | ||||
self.nodes[0], 2, sync_fun=lambda: self.sync_all(self.nodes[0:3])) | self.nodes[0], 2, sync_fun=lambda: self.sync_all(self.nodes[0:3]) | ||||
) | |||||
balance_nodes = [self.nodes[i].getbalance() for i in range(3)] | balance_nodes = [self.nodes[i].getbalance() for i in range(3)] | ||||
block_count = self.nodes[0].getblockcount() | block_count = self.nodes[0].getblockcount() | ||||
# Check modes: | # Check modes: | ||||
# - True: unicode escaped as \u.... | # - True: unicode escaped as \u.... | ||||
# - False: unicode directly as UTF-8 | # - False: unicode directly as UTF-8 | ||||
for mode in [True, False]: | for mode in [True, False]: | ||||
self.nodes[0].rpc.ensure_ascii = mode | self.nodes[0].rpc.ensure_ascii = mode | ||||
# unicode check: Basic Multilingual Plane, Supplementary Plane | # unicode check: Basic Multilingual Plane, Supplementary Plane | ||||
# respectively | # respectively | ||||
for label in [u'рыба', u'𝅘𝅥𝅯']: | for label in ["рыба", "𝅘𝅥𝅯"]: | ||||
addr = self.nodes[0].getnewaddress() | addr = self.nodes[0].getnewaddress() | ||||
self.nodes[0].setlabel(addr, label) | self.nodes[0].setlabel(addr, label) | ||||
test_address(self.nodes[0], addr, labels=[label]) | test_address(self.nodes[0], addr, labels=[label]) | ||||
assert label in self.nodes[0].listlabels() | assert label in self.nodes[0].listlabels() | ||||
# restore to default | # restore to default | ||||
self.nodes[0].rpc.ensure_ascii = True | self.nodes[0].rpc.ensure_ascii = True | ||||
# maintenance tests | # maintenance tests | ||||
maintenance = [ | maintenance = [ | ||||
'-rescan', | "-rescan", | ||||
'-reindex', | "-reindex", | ||||
] | ] | ||||
chainlimit = 6 | chainlimit = 6 | ||||
for m in maintenance: | for m in maintenance: | ||||
self.log.info(f"check {m}") | self.log.info(f"check {m}") | ||||
self.stop_nodes() | self.stop_nodes() | ||||
# set lower ancestor limit for later | # set lower ancestor limit for later | ||||
self.start_node( | self.start_node( | ||||
0, self.extra_args[0] + [m, f"-limitancestorcount={str(chainlimit)}"]) | 0, self.extra_args[0] + [m, f"-limitancestorcount={str(chainlimit)}"] | ||||
) | |||||
self.start_node( | self.start_node( | ||||
1, self.extra_args[1] + [m, f"-limitancestorcount={str(chainlimit)}"]) | 1, self.extra_args[1] + [m, f"-limitancestorcount={str(chainlimit)}"] | ||||
) | |||||
self.start_node( | self.start_node( | ||||
2, self.extra_args[2] + [m, f"-limitancestorcount={str(chainlimit)}"]) | 2, self.extra_args[2] + [m, f"-limitancestorcount={str(chainlimit)}"] | ||||
if m == '-reindex': | ) | ||||
if m == "-reindex": | |||||
# reindex will leave rpc warm up "early"; Wait for it to finish | # reindex will leave rpc warm up "early"; Wait for it to finish | ||||
self.wait_until( | self.wait_until( | ||||
lambda: [block_count] * 3 == | lambda: [block_count] * 3 | ||||
[self.nodes[i].getblockcount() for i in range(3)]) | == [self.nodes[i].getblockcount() for i in range(3)] | ||||
assert_equal(balance_nodes, | ) | ||||
[self.nodes[i].getbalance() for i in range(3)]) | assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)]) | ||||
# Exercise listsinceblock with the last two blocks | # Exercise listsinceblock with the last two blocks | ||||
coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0]) | coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0]) | ||||
assert_equal(coinbase_tx_1["lastblock"], blocks[1]) | assert_equal(coinbase_tx_1["lastblock"], blocks[1]) | ||||
assert_equal(len(coinbase_tx_1["transactions"]), 1) | assert_equal(len(coinbase_tx_1["transactions"]), 1) | ||||
assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) | assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) | ||||
assert_equal(len(self.nodes[0].listsinceblock( | assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0) | ||||
blocks[1])["transactions"]), 0) | |||||
# ==Check that wallet prefers to use coins that don't exceed mempool li | # ==Check that wallet prefers to use coins that don't exceed mempool li | ||||
# Get all non-zero utxos together | # Get all non-zero utxos together | ||||
chain_addrs = [self.nodes[0].getnewaddress( | chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()] | ||||
), self.nodes[0].getnewaddress()] | |||||
singletxid = self.nodes[0].sendtoaddress( | singletxid = self.nodes[0].sendtoaddress( | ||||
chain_addrs[0], self.nodes[0].getbalance(), "", "", True) | chain_addrs[0], self.nodes[0].getbalance(), "", "", True | ||||
) | |||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op) | self.generate(self.nodes[0], 1, sync_fun=self.no_op) | ||||
node0_balance = self.nodes[0].getbalance() | node0_balance = self.nodes[0].getbalance() | ||||
# Split into two chains | # Split into two chains | ||||
rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], { | rawtx = self.nodes[0].createrawtransaction( | ||||
chain_addrs[0]: node0_balance / 2 - Decimal('10000'), chain_addrs[1]: node0_balance / 2 - Decimal('10000')}) | [{"txid": singletxid, "vout": 0}], | ||||
{ | |||||
chain_addrs[0]: node0_balance / 2 - Decimal("10000"), | |||||
chain_addrs[1]: node0_balance / 2 - Decimal("10000"), | |||||
}, | |||||
) | |||||
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | ||||
singletxid = self.nodes[0].sendrawtransaction( | singletxid = self.nodes[0].sendrawtransaction( | ||||
hexstring=signedtx["hex"], maxfeerate=0) | hexstring=signedtx["hex"], maxfeerate=0 | ||||
) | |||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op) | self.generate(self.nodes[0], 1, sync_fun=self.no_op) | ||||
# Make a long chain of unconfirmed payments without hitting mempool limit | # Make a long chain of unconfirmed payments without hitting mempool limit | ||||
# Each tx we make leaves only one output of change on a chain 1 longer | # Each tx we make leaves only one output of change on a chain 1 longer | ||||
# Since the amount to send is always much less than the outputs, we only ever need one output | # Since the amount to send is always much less than the outputs, we only ever need one output | ||||
# So we should be able to generate exactly chainlimit txs for each | # So we should be able to generate exactly chainlimit txs for each | ||||
# original output | # original output | ||||
sending_addr = self.nodes[1].getnewaddress() | sending_addr = self.nodes[1].getnewaddress() | ||||
txid_list = [] | txid_list = [] | ||||
for _ in range(chainlimit * 2): | for _ in range(chainlimit * 2): | ||||
txid_list.append(self.nodes[0].sendtoaddress( | txid_list.append( | ||||
sending_addr, Decimal('10000'))) | self.nodes[0].sendtoaddress(sending_addr, Decimal("10000")) | ||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2) | ) | ||||
assert_equal(self.nodes[0].getmempoolinfo()["size"], chainlimit * 2) | |||||
assert_equal(len(txid_list), chainlimit * 2) | assert_equal(len(txid_list), chainlimit * 2) | ||||
# Without walletrejectlongchains, we will still generate a txid | # Without walletrejectlongchains, we will still generate a txid | ||||
# The tx will be stored in the wallet but not accepted to the mempool | # The tx will be stored in the wallet but not accepted to the mempool | ||||
extra_txid = self.nodes[0].sendtoaddress( | extra_txid = self.nodes[0].sendtoaddress(sending_addr, Decimal("10000")) | ||||
sending_addr, Decimal('10000')) | |||||
assert extra_txid not in self.nodes[0].getrawmempool() | assert extra_txid not in self.nodes[0].getrawmempool() | ||||
assert extra_txid in [tx["txid"] | assert extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()] | ||||
for tx in self.nodes[0].listtransactions()] | |||||
self.nodes[0].abandontransaction(extra_txid) | self.nodes[0].abandontransaction(extra_txid) | ||||
total_txs = len(self.nodes[0].listtransactions("*", 99999)) | total_txs = len(self.nodes[0].listtransactions("*", 99999)) | ||||
# Try with walletrejectlongchains | # Try with walletrejectlongchains | ||||
# Double chain limit but require combining inputs, so we pass | # Double chain limit but require combining inputs, so we pass | ||||
# SelectCoinsMinConf | # SelectCoinsMinConf | ||||
self.stop_node(0) | self.stop_node(0) | ||||
self.start_node(0, | self.start_node( | ||||
self.extra_args[0] + ["-walletrejectlongchains", | 0, | ||||
f"-limitancestorcount={str(2 * chainlimit)}"]) | self.extra_args[0] | ||||
+ ["-walletrejectlongchains", f"-limitancestorcount={str(2 * chainlimit)}"], | |||||
) | |||||
# wait until the wallet has submitted all transactions to the mempool | # wait until the wallet has submitted all transactions to the mempool | ||||
self.wait_until( | self.wait_until(lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2) | ||||
lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2) | |||||
# Prevent potential race condition when calling wallet RPCs right after | # Prevent potential race condition when calling wallet RPCs right after | ||||
# restart | # restart | ||||
self.nodes[0].syncwithvalidationinterfacequeue() | self.nodes[0].syncwithvalidationinterfacequeue() | ||||
node0_balance = self.nodes[0].getbalance() | node0_balance = self.nodes[0].getbalance() | ||||
# With walletrejectlongchains we will not create the tx and store it in | # With walletrejectlongchains we will not create the tx and store it in | ||||
# our wallet. | # our wallet. | ||||
assert_raises_rpc_error(-6, "Transaction has too long of a mempool chain", | assert_raises_rpc_error( | ||||
self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('10000')) | -6, | ||||
"Transaction has too long of a mempool chain", | |||||
self.nodes[0].sendtoaddress, | |||||
sending_addr, | |||||
node0_balance - Decimal("10000"), | |||||
) | |||||
# Verify nothing new in wallet | # Verify nothing new in wallet | ||||
assert_equal(total_txs, len( | assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999))) | ||||
self.nodes[0].listtransactions("*", 99999))) | |||||
# Test getaddressinfo on external address. Note that these addresses | # Test getaddressinfo on external address. Note that these addresses | ||||
# are taken from disablewallet.py | # are taken from disablewallet.py | ||||
assert_raises_rpc_error(-5, "Invalid address", | assert_raises_rpc_error( | ||||
self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") | -5, | ||||
"Invalid address", | |||||
self.nodes[0].getaddressinfo, | |||||
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", | |||||
) | |||||
address_info = self.nodes[0].getaddressinfo( | address_info = self.nodes[0].getaddressinfo( | ||||
"mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") | "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ" | ||||
assert_equal(address_info['address'], | ) | ||||
"ecregtest:qp8rs4qyd3aazk22eyzwg7fmdfzmxm02pyprkfhvm4") | assert_equal( | ||||
assert_equal(address_info["scriptPubKey"], | address_info["address"], | ||||
"76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac") | "ecregtest:qp8rs4qyd3aazk22eyzwg7fmdfzmxm02pyprkfhvm4", | ||||
) | |||||
assert_equal( | |||||
address_info["scriptPubKey"], | |||||
"76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac", | |||||
) | |||||
assert not address_info["ismine"] | assert not address_info["ismine"] | ||||
assert not address_info["iswatchonly"] | assert not address_info["iswatchonly"] | ||||
assert not address_info["isscript"] | assert not address_info["isscript"] | ||||
assert not address_info["ischange"] | assert not address_info["ischange"] | ||||
# Test getaddressinfo 'ischange' field on change address. | # Test getaddressinfo 'ischange' field on change address. | ||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op) | self.generate(self.nodes[0], 1, sync_fun=self.no_op) | ||||
destination = self.nodes[1].getnewaddress() | destination = self.nodes[1].getnewaddress() | ||||
txid = self.nodes[0].sendtoaddress(destination, 123000) | txid = self.nodes[0].sendtoaddress(destination, 123000) | ||||
tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded'] | tx = self.nodes[0].gettransaction(txid=txid, verbose=True)["decoded"] | ||||
output_addresses = [vout['scriptPubKey']['addresses'][0] | output_addresses = [vout["scriptPubKey"]["addresses"][0] for vout in tx["vout"]] | ||||
for vout in tx["vout"]] | |||||
assert len(output_addresses) > 1 | assert len(output_addresses) > 1 | ||||
for address in output_addresses: | for address in output_addresses: | ||||
ischange = self.nodes[0].getaddressinfo(address)['ischange'] | ischange = self.nodes[0].getaddressinfo(address)["ischange"] | ||||
assert_equal(ischange, address != destination) | assert_equal(ischange, address != destination) | ||||
if ischange: | if ischange: | ||||
change = address | change = address | ||||
self.nodes[0].setlabel(change, 'foobar') | self.nodes[0].setlabel(change, "foobar") | ||||
assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False) | assert_equal(self.nodes[0].getaddressinfo(change)["ischange"], False) | ||||
# Test gettransaction response with different arguments. | # Test gettransaction response with different arguments. | ||||
self.log.info( | self.log.info("Testing gettransaction response with different arguments...") | ||||
"Testing gettransaction response with different arguments...") | self.nodes[0].setlabel(change, "baz") | ||||
self.nodes[0].setlabel(change, 'baz') | |||||
baz = self.nodes[0].listtransactions(label="baz", count=1)[0] | baz = self.nodes[0].listtransactions(label="baz", count=1)[0] | ||||
expected_receive_vout = {"label": "baz", | expected_receive_vout = { | ||||
"label": "baz", | |||||
"address": baz["address"], | "address": baz["address"], | ||||
"amount": baz["amount"], | "amount": baz["amount"], | ||||
"category": baz["category"], | "category": baz["category"], | ||||
"vout": baz["vout"]} | "vout": baz["vout"], | ||||
expected_fields = frozenset({'amount', | } | ||||
'confirmations', | expected_fields = frozenset( | ||||
'details', | { | ||||
'fee', | "amount", | ||||
'hex', | "confirmations", | ||||
'time', | "details", | ||||
'timereceived', | "fee", | ||||
'trusted', | "hex", | ||||
'txid', | "time", | ||||
'walletconflicts'}) | "timereceived", | ||||
"trusted", | |||||
"txid", | |||||
"walletconflicts", | |||||
} | |||||
) | |||||
verbose_field = "decoded" | verbose_field = "decoded" | ||||
expected_verbose_fields = expected_fields | {verbose_field} | expected_verbose_fields = expected_fields | {verbose_field} | ||||
self.log.debug("Testing gettransaction response without verbose") | self.log.debug("Testing gettransaction response without verbose") | ||||
tx = self.nodes[0].gettransaction(txid=txid) | tx = self.nodes[0].gettransaction(txid=txid) | ||||
assert_equal(set(tx), expected_fields) | assert_equal(set(tx), expected_fields) | ||||
assert_array_result( | assert_array_result( | ||||
tx["details"], { | tx["details"], {"category": "receive"}, expected_receive_vout | ||||
"category": "receive"}, expected_receive_vout) | ) | ||||
self.log.debug( | self.log.debug("Testing gettransaction response with verbose set to False") | ||||
"Testing gettransaction response with verbose set to False") | |||||
tx = self.nodes[0].gettransaction(txid=txid, verbose=False) | tx = self.nodes[0].gettransaction(txid=txid, verbose=False) | ||||
assert_equal(set(tx), expected_fields) | assert_equal(set(tx), expected_fields) | ||||
assert_array_result( | assert_array_result( | ||||
tx["details"], { | tx["details"], {"category": "receive"}, expected_receive_vout | ||||
"category": "receive"}, expected_receive_vout) | ) | ||||
self.log.debug( | self.log.debug("Testing gettransaction response with verbose set to True") | ||||
"Testing gettransaction response with verbose set to True") | |||||
tx = self.nodes[0].gettransaction(txid=txid, verbose=True) | tx = self.nodes[0].gettransaction(txid=txid, verbose=True) | ||||
assert_equal(set(tx), expected_verbose_fields) | assert_equal(set(tx), expected_verbose_fields) | ||||
assert_array_result( | assert_array_result( | ||||
tx["details"], { | tx["details"], {"category": "receive"}, expected_receive_vout | ||||
"category": "receive"}, expected_receive_vout) | ) | ||||
assert_equal( | assert_equal(tx[verbose_field], self.nodes[0].decoderawtransaction(tx["hex"])) | ||||
tx[verbose_field], | |||||
self.nodes[0].decoderawtransaction( | |||||
tx["hex"])) | |||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
WalletTest().main() | WalletTest().main() |