wallet_simulaterawtx.py raw

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