wallet_simulaterawtx.py raw

   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()
 131