wallet_simulaterawtx.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2021-present The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 """Test simulaterawtransaction. 6 """ 7 8 from decimal import Decimal 9 from test_framework.blocktools import COINBASE_MATURITY 10 from test_framework.test_framework import BitcoinTestFramework 11 from test_framework.util import ( 12 assert_approx, 13 assert_equal, 14 assert_raises_rpc_error, 15 ) 16 17 class SimulateTxTest(BitcoinTestFramework): 18 def set_test_params(self): 19 self.setup_clean_chain = True 20 self.num_nodes = 1 21 22 def skip_test_if_missing_module(self): 23 self.skip_if_no_wallet() 24 25 def setup_network(self, split=False): 26 self.setup_nodes() 27 28 def run_test(self): 29 node = self.nodes[0] 30 31 self.generate(node, 1, sync_fun=self.no_op) # Leave IBD 32 33 node.createwallet(wallet_name='w0') 34 node.createwallet(wallet_name='w1') 35 node.createwallet(wallet_name='w2', disable_private_keys=True) 36 w0 = node.get_wallet_rpc('w0') 37 w1 = node.get_wallet_rpc('w1') 38 w2 = node.get_wallet_rpc('w2') 39 40 self.generatetoaddress(node, COINBASE_MATURITY + 1, w0.getnewaddress()) 41 assert_equal(w0.getbalance(), 50.0) 42 assert_equal(w1.getbalance(), 0.0) 43 44 address1 = w1.getnewaddress() 45 address2 = w1.getnewaddress() 46 47 # Add address1 as watch-only to w2 48 import_res = w2.importdescriptors([{"desc": w1.getaddressinfo(address1)["desc"], "timestamp": "now"}]) 49 assert_equal(import_res[0]["success"], True) 50 51 tx1 = node.createrawtransaction([], [{address1: 5.0}]) 52 tx2 = node.createrawtransaction([], [{address2: 10.0}]) 53 54 # w0 should be unaffected, w2 should see +5 for tx1 55 assert_equal(w0.simulaterawtransaction([tx1])["balance_change"], 0.0) 56 assert_equal(w2.simulaterawtransaction([tx1])["balance_change"], 5.0) 57 58 # w1 should see +5 balance for tx1 59 assert_equal(w1.simulaterawtransaction([tx1])["balance_change"], 5.0) 60 61 # w0 should be unaffected, w2 should see +5 for both transactions 62 assert_equal(w0.simulaterawtransaction([tx1, tx2])["balance_change"], 0.0) 63 assert_equal(w2.simulaterawtransaction([tx1, tx2])["balance_change"], 5.0) 64 65 # w1 should see +15 balance for both transactions 66 assert_equal(w1.simulaterawtransaction([tx1, tx2])["balance_change"], 15.0) 67 68 # w0 funds transaction; it should now see a decrease in (tx fee and payment), and w1 should see the same as above 69 funding = w0.fundrawtransaction(tx1) 70 tx1 = funding["hex"] 71 tx1changepos = funding["changepos"] 72 bitcoin_fee = Decimal(funding["fee"]) 73 74 # w0 sees fee + 5 btc decrease, w2 sees + 5 btc 75 assert_approx(w0.simulaterawtransaction([tx1])["balance_change"], -(Decimal("5") + bitcoin_fee)) 76 assert_approx(w2.simulaterawtransaction([tx1])["balance_change"], Decimal("5")) 77 78 # w1 sees same as before 79 assert_equal(w1.simulaterawtransaction([tx1])["balance_change"], 5.0) 80 81 # same inputs (tx) more than once should error 82 assert_raises_rpc_error(-8, "Transaction(s) are spending the same output more than once", w0.simulaterawtransaction, [tx1,tx1]) 83 84 tx1ob = node.decoderawtransaction(tx1) 85 tx1hex = tx1ob["txid"] 86 tx1vout = 1 - tx1changepos 87 # tx3 spends new w1 UTXO paying to w0 88 tx3 = node.createrawtransaction([{"txid": tx1hex, "vout": tx1vout}], {w0.getnewaddress(): 4.9999}) 89 # tx4 spends new w1 UTXO paying to w1 90 tx4 = node.createrawtransaction([{"txid": tx1hex, "vout": tx1vout}], {w1.getnewaddress(): 4.9999}) 91 92 # on their own, both should fail due to missing input(s) 93 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w0.simulaterawtransaction, [tx3]) 94 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w1.simulaterawtransaction, [tx3]) 95 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w0.simulaterawtransaction, [tx4]) 96 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w1.simulaterawtransaction, [tx4]) 97 98 # they should succeed when including tx1: 99 # wallet tx3 tx4 100 # w0 -5 - bitcoin_fee + 4.9999 -5 - bitcoin_fee 101 # w1 0 +4.9999 102 assert_approx(w0.simulaterawtransaction([tx1, tx3])["balance_change"], -Decimal("5") - bitcoin_fee + Decimal("4.9999")) 103 assert_approx(w1.simulaterawtransaction([tx1, tx3])["balance_change"], 0) 104 assert_approx(w0.simulaterawtransaction([tx1, tx4])["balance_change"], -Decimal("5") - bitcoin_fee) 105 assert_approx(w1.simulaterawtransaction([tx1, tx4])["balance_change"], Decimal("4.9999")) 106 107 # they should fail if attempting to include both tx3 and tx4 108 assert_raises_rpc_error(-8, "Transaction(s) are spending the same output more than once", w0.simulaterawtransaction, [tx1, tx3, tx4]) 109 assert_raises_rpc_error(-8, "Transaction(s) are spending the same output more than once", w1.simulaterawtransaction, [tx1, tx3, tx4]) 110 111 # send tx1 to avoid reusing same UTXO below 112 node.sendrawtransaction(w0.signrawtransactionwithwallet(tx1)["hex"]) 113 self.generate(node, 1, sync_fun=self.no_op) # Confirm tx to trigger error below 114 self.sync_all() 115 116 # w0 funds transaction 2; it should now see a decrease in (tx fee and payment), and w1 should see the same as above 117 funding = w0.fundrawtransaction(tx2) 118 tx2 = funding["hex"] 119 bitcoin_fee2 = Decimal(funding["fee"]) 120 assert_approx(w0.simulaterawtransaction([tx2])["balance_change"], -(Decimal("10") + bitcoin_fee2)) 121 assert_approx(w1.simulaterawtransaction([tx2])["balance_change"], +(Decimal("10"))) 122 assert_approx(w2.simulaterawtransaction([tx2])["balance_change"], 0) 123 124 # w0-w2 error due to tx1 already being mined 125 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w0.simulaterawtransaction, [tx1, tx2]) 126 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w1.simulaterawtransaction, [tx1, tx2]) 127 assert_raises_rpc_error(-8, "One or more transaction inputs are missing or have been spent already", w2.simulaterawtransaction, [tx1, tx2]) 128 129 if __name__ == '__main__': 130 SimulateTxTest(__file__).main()