|
4 | 4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
5 | 5 | """Test the wallet balance RPC methods."""
|
6 | 6 | from decimal import Decimal
|
| 7 | +import struct |
7 | 8 |
|
8 | 9 | from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE as ADDRESS_WATCHONLY
|
9 | 10 | from test_framework.test_framework import BitcoinTestFramework
|
10 | 11 | from test_framework.util import (
|
11 | 12 | assert_equal,
|
12 | 13 | assert_raises_rpc_error,
|
| 14 | + connect_nodes_bi, |
| 15 | + sync_blocks, |
13 | 16 | )
|
14 | 17 |
|
15 | 18 |
|
16 | 19 | def create_transactions(node, address, amt, fees):
|
17 | 20 | # Create and sign raw transactions from node to address for amt.
|
18 | 21 | # Creates a transaction for each fee and returns an array
|
19 | 22 | # of the raw transactions.
|
20 |
| - utxos = node.listunspent(0) |
| 23 | + utxos = [u for u in node.listunspent(0) if u['spendable']] |
21 | 24 |
|
22 | 25 | # Create transactions
|
23 | 26 | inputs = []
|
24 | 27 | ins_total = 0
|
25 | 28 | for utxo in utxos:
|
26 | 29 | inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]})
|
27 | 30 | ins_total += utxo['amount']
|
28 |
| - if ins_total > amt: |
| 31 | + if ins_total + max(fees) > amt: |
29 | 32 | break
|
30 | 33 |
|
31 | 34 | txs = []
|
32 | 35 | for fee in fees:
|
33 | 36 | outputs = {address: amt, node.getrawchangeaddress(): ins_total - amt - fee}
|
34 | 37 | raw_tx = node.createrawtransaction(inputs, outputs, 0, True)
|
35 | 38 | raw_tx = node.signrawtransactionwithwallet(raw_tx)
|
| 39 | + assert_equal(raw_tx['complete'], True) |
36 | 40 | txs.append(raw_tx)
|
37 | 41 |
|
38 | 42 | return txs
|
@@ -152,9 +156,47 @@ def test_balances(*, fee_node_1=0):
|
152 | 156 | # Create 3 more wallet txs, where the last is not accepted to the
|
153 | 157 | # mempool because it is the third descendant of the tx above
|
154 | 158 | for _ in range(3):
|
| 159 | + # Set amount high enough such that all coins are spent by each tx |
155 | 160 | txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 99)
|
| 161 | + |
| 162 | + self.log.info('Check that wallet txs not in the mempool are untrusted') |
156 | 163 | assert txid not in self.nodes[0].getrawmempool()
|
| 164 | + assert_equal(self.nodes[0].gettransaction(txid)['trusted'], False) |
| 165 | + assert_equal(self.nodes[0].getbalance(minconf=0), 0) |
| 166 | + |
| 167 | + self.log.info("Test replacement and reorg of non-mempool tx") |
| 168 | + tx_orig = self.nodes[0].gettransaction(txid)['hex'] |
| 169 | + # Increase fee by 1 coin |
| 170 | + tx_replace = tx_orig.replace( |
| 171 | + struct.pack("<q", 99 * 10**8).hex(), |
| 172 | + struct.pack("<q", 98 * 10**8).hex(), |
| 173 | + ) |
| 174 | + tx_replace = self.nodes[0].signrawtransactionwithwallet(tx_replace)['hex'] |
| 175 | + # Total balance is given by the sum of outputs of the tx |
| 176 | + total_amount = sum([o['value'] for o in self.nodes[0].decoderawtransaction(tx_replace)['vout']]) |
| 177 | + self.sync_all() |
| 178 | + self.nodes[1].sendrawtransaction(hexstring=tx_replace, maxfeerate=0) |
| 179 | + |
| 180 | + # Now confirm tx_replace |
| 181 | + block_reorg = self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY)[0] |
| 182 | + self.sync_all() |
| 183 | + assert_equal(self.nodes[0].getbalance(minconf=0), total_amount) |
| 184 | + |
| 185 | + self.log.info('Put txs back into mempool of node 1 (not node 0)') |
| 186 | + self.nodes[0].invalidateblock(block_reorg) |
| 187 | + self.nodes[1].invalidateblock(block_reorg) |
157 | 188 | assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
|
| 189 | + self.nodes[0].generatetoaddress(1, ADDRESS_WATCHONLY) |
| 190 | + assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted |
| 191 | + |
| 192 | + # Now confirm tx_orig |
| 193 | + self.restart_node(1, ['-persistmempool=0']) |
| 194 | + connect_nodes_bi(self.nodes, 0, 1) |
| 195 | + sync_blocks(self.nodes) |
| 196 | + self.nodes[1].sendrawtransaction(tx_orig) |
| 197 | + self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY) |
| 198 | + self.sync_all() |
| 199 | + assert_equal(self.nodes[0].getbalance(minconf=0), total_amount + 1) # The reorg recovered our fee of 1 coin |
158 | 200 |
|
159 | 201 |
|
160 | 202 | if __name__ == '__main__':
|
|
0 commit comments