Changeset View
Changeset View
Standalone View
Standalone View
test/functional/wallet_balance.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2018-2019 The Bitcoin Core developers | # Copyright (c) 2018-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test the wallet balance RPC methods.""" | """Test the wallet balance RPC methods.""" | ||||
from decimal import Decimal | from decimal import Decimal | ||||
import struct | |||||
from test_framework.address import ADDRESS_BCHREG_UNSPENDABLE as ADDRESS_WATCHONLY | from test_framework.address import ADDRESS_BCHREG_UNSPENDABLE as ADDRESS_WATCHONLY | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
assert_raises_rpc_error, | assert_raises_rpc_error, | ||||
connect_nodes_bi, | |||||
sync_blocks, | |||||
) | ) | ||||
def create_transactions(node, address, amt, fees): | def create_transactions(node, address, amt, fees): | ||||
# Create and sign raw transactions from node to address for amt. | # Create and sign raw transactions from node to address for amt. | ||||
# Creates a transaction for each fee and returns an array | # Creates a transaction for each fee and returns an array | ||||
# of the raw transactions. | # of the raw transactions. | ||||
utxos = [u for u in node.listunspent(0) if u['spendable']] | utxos = [u for u in node.listunspent(0) if u['spendable']] | ||||
# Create transactions | # Create transactions | ||||
inputs = [] | inputs = [] | ||||
ins_total = 0 | ins_total = 0 | ||||
for utxo in utxos: | for utxo in utxos: | ||||
inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) | inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) | ||||
ins_total += utxo['amount'] | ins_total += utxo['amount'] | ||||
if ins_total > amt: | if ins_total + max(fees) > amt: | ||||
break | break | ||||
txs = [] | txs = [] | ||||
for fee in fees: | for fee in fees: | ||||
outputs = { | outputs = { | ||||
address: amt, | address: amt, | ||||
node.getrawchangeaddress(): ins_total - | node.getrawchangeaddress(): ins_total - | ||||
amt - | amt - | ||||
fee} | fee} | ||||
raw_tx = node.createrawtransaction(inputs, outputs, 0) | raw_tx = node.createrawtransaction(inputs, outputs, 0) | ||||
raw_tx = node.signrawtransactionwithwallet(raw_tx) | raw_tx = node.signrawtransactionwithwallet(raw_tx) | ||||
assert_equal(raw_tx['complete'], True) | |||||
txs.append(raw_tx) | txs.append(raw_tx) | ||||
return txs | return txs | ||||
class WalletTest(BitcoinTestFramework): | class WalletTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.sync_all() | self.sync_all() | ||||
self.nodes[1].loadwallet('') | self.nodes[1].loadwallet('') | ||||
after = self.nodes[1].getunconfirmedbalance() | after = self.nodes[1].getunconfirmedbalance() | ||||
assert_equal(before + Decimal('0.1'), after) | assert_equal(before + Decimal('0.1'), after) | ||||
# Create 3 more wallet txs, where the last is not accepted to the | # Create 3 more wallet txs, where the last is not accepted to the | ||||
# mempool because it is the third descendant of the tx above | # mempool because it is the third descendant of the tx above | ||||
for _ in range(3): | for _ in range(3): | ||||
# Set amount high enough such that all coins are spent by each tx | |||||
txid = self.nodes[0].sendtoaddress( | txid = self.nodes[0].sendtoaddress( | ||||
self.nodes[0].getnewaddress(), 99) | self.nodes[0].getnewaddress(), 99) | ||||
self.log.info('Check that wallet txs not in the mempool are untrusted') | |||||
assert txid not in self.nodes[0].getrawmempool() | assert txid not in self.nodes[0].getrawmempool() | ||||
assert_equal(self.nodes[0].gettransaction(txid)['trusted'], False) | |||||
assert_equal(self.nodes[0].getbalance(minconf=0), 0) | |||||
self.log.info("Test replacement and reorg of non-mempool tx") | |||||
tx_orig = self.nodes[0].gettransaction(txid)['hex'] | |||||
# Increase fee by 1 coin | |||||
tx_replace = tx_orig.replace( | |||||
struct.pack("<q", 99 * 10**8).hex(), | |||||
struct.pack("<q", 98 * 10**8).hex(), | |||||
) | |||||
tx_replace = self.nodes[0].signrawtransactionwithwallet(tx_replace)[ | |||||
'hex'] | |||||
# Total balance is given by the sum of outputs of the tx | |||||
total_amount = sum( | |||||
[o['value'] for o in self.nodes[0].decoderawtransaction(tx_replace)['vout']]) | |||||
self.sync_all() | |||||
self.nodes[1].sendrawtransaction(hexstring=tx_replace, maxfeerate=0) | |||||
# Now confirm tx_replace | |||||
block_reorg = self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY)[0] | |||||
self.sync_all() | |||||
assert_equal(self.nodes[0].getbalance(minconf=0), total_amount) | |||||
self.log.info('Put txs back into mempool of node 1 (not node 0)') | |||||
self.nodes[0].invalidateblock(block_reorg) | |||||
self.nodes[1].invalidateblock(block_reorg) | |||||
# wallet txs not in the mempool are untrusted | # wallet txs not in the mempool are untrusted | ||||
assert_equal(self.nodes[0].getbalance(minconf=0), 0) | assert_equal(self.nodes[0].getbalance(minconf=0), 0) | ||||
self.nodes[0].generatetoaddress(1, ADDRESS_WATCHONLY) | |||||
# wallet txs not in the mempool are untrusted | |||||
assert_equal(self.nodes[0].getbalance(minconf=0), 0) | |||||
# Now confirm tx_orig | |||||
self.restart_node(1, ['-persistmempool=0']) | |||||
connect_nodes_bi(self.nodes[0], self.nodes[1]) | |||||
sync_blocks(self.nodes) | |||||
self.nodes[1].sendrawtransaction(tx_orig) | |||||
self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY) | |||||
self.sync_all() | |||||
# The reorg recovered our fee of 1 coin | |||||
assert_equal(self.nodes[0].getbalance(minconf=0), total_amount + 1) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
WalletTest().main() | WalletTest().main() |