Merge bitcoin/bitcoin#23319: rpc: Return fee and prevout (utxos) to getrawtransaction

f86697163e rpc: Return fee and prevout(s) to getrawtransaction (Douglas Chimento)

Pull request description:

  Add fee response in BTC to getrawtransaction #23264

  ### For Reviewers

  * Verbose arg is now an int
  * Verbose = 2 includes a `fee` field and `prevout`
  * [./test/functional/rpc_rawtransaction.py](./test/functional/rpc_rawtransaction.py) contains a new test to validate fields of new verbosity 2 (not the values)

  ```
  bitcoin-cli -chain=test getrawtransaction 9ae533f7da9be4a34997db78343a8d8d6d6186b6bba3959e56f416a5c70e7de4 2 000000000000001d442e556146d5f2841d85150c200e8d8b8a4b5005b13878f6
  ```
  ```
    "in_active_chain": true,
    "txid": "9ae533f7da9be4a34997db78343a8d8d6d6186b6bba3959e56f416a5c70e7de4",
    "hash": "7f23e3f3a0a256ddea1d35ffd43e9afdd67cc68389ef1a804bb20c76abd6863e",
   ....
    "vin": [
      {
        "txid": "23fc75d6d74f6f97e225839af69ff36a612fe04db58a4414ec4828d1749a05a0",
        "vout": 0,
        "scriptSig": {
          "asm": "",
          "hex": ""
        },
        "prevout": {
          "generated": false,
          "height": 2099486,
          "value": 0.00017764,
          "scriptPubKey": {
            "asm": "0 7846ce1ced3253d8bd43008db2ca364cc722f5a2",
            "hex": "00147846ce1ced3253d8bd43008db2ca364cc722f5a2",
            "address": "tb1q0prvu88dxffa302rqzxm9j3kfnrj9adzk49mlp",
            "type": "witness_v0_keyhash"
          }
        },
        "sequence": 4294967295
      },
  ...
   "fee": 0.00000762
  }
  ```

ACKs for top commit:
  achow101:
    ACK f86697163e
  aureleoules:
    ACK f86697163e
  hernanmarino:
    re ACK f86697163e
  pablomartin4btc:
    re-tACK f86697163e

Tree-SHA512: 591fdc285d74fa7803e04ad01c7b70bc20fac6b1369e7bd5b8e2cde9b750ea52d6c70d79225b74bef4f4bbc0fb960877778017184e146119da4a55f9593d1224
This commit is contained in:
Andrew Chow
2022-12-13 17:59:26 -05:00
3 changed files with 164 additions and 30 deletions

View File

@ -14,6 +14,7 @@ Test the following RPCs:
from collections import OrderedDict
from decimal import Decimal
from itertools import product
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
@ -81,6 +82,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
self.getrawtransaction_tests()
self.getrawtransaction_verbosity_tests()
self.createrawtransaction_tests()
self.sendrawtransaction_tests()
self.sendrawtransaction_testmempoolaccept_tests()
@ -116,6 +118,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# 4. valid parameters - supply txid and 1 for verbose.
# We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
assert_equal(self.nodes[n].getrawtransaction(txId, 1)["hex"], tx['hex'])
assert_equal(self.nodes[n].getrawtransaction(txId, 2)["hex"], tx['hex'])
# 5. valid parameters - supply txid and True for non-verbose
assert_equal(self.nodes[n].getrawtransaction(txId, True)["hex"], tx['hex'])
@ -126,13 +129,14 @@ class RawTransactionsTest(BitcoinTestFramework):
# 6. invalid parameters - supply txid and invalid boolean values (strings) for verbose
for value in ["True", "False"]:
assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txid=txId, verbose=value)
assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txid=txId, verbose=value)
assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txid=txId, verbosity=value)
# 7. invalid parameters - supply txid and empty array
assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txId, [])
assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txId, [])
# 8. invalid parameters - supply txid and empty dict
assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txId, {})
assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txId, {})
# Make a tx by sending, then generate 2 blocks; block1 has the tx in it
tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid']
@ -145,9 +149,10 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(gottx['in_active_chain'], True)
if n == 0:
self.log.info("Test getrawtransaction with -txindex, without blockhash: 'in_active_chain' should be absent")
gottx = self.nodes[n].getrawtransaction(txid=tx, verbose=True)
assert_equal(gottx['txid'], tx)
assert 'in_active_chain' not in gottx
for v in [1,2]:
gottx = self.nodes[n].getrawtransaction(txid=tx, verbosity=v)
assert_equal(gottx['txid'], tx)
assert 'in_active_chain' not in gottx
else:
self.log.info("Test getrawtransaction without -txindex, without blockhash: expect the call to raise")
assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, txid=tx, verbose=True)
@ -172,6 +177,70 @@ class RawTransactionsTest(BitcoinTestFramework):
block = self.nodes[0].getblock(self.nodes[0].getblockhash(0))
assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot'])
def getrawtransaction_verbosity_tests(self):
tx = self.wallet.send_self_transfer(from_node=self.nodes[1])['txid']
[block1] = self.generate(self.nodes[1], 1)
fields = [
'blockhash',
'blocktime',
'confirmations',
'hash',
'hex',
'in_active_chain',
'locktime',
'size',
'time',
'txid',
'vin',
'vout',
'vsize',
'weight',
]
prevout_fields = [
'generated',
'height',
'value',
'scriptPubKey',
]
script_pub_key_fields = [
'address',
'asm',
'hex',
'type',
]
# node 0 & 2 with verbosity 1 & 2
for n, v in product([0, 2], [1, 2]):
self.log.info(f"Test getrawtransaction_verbosity {v} {'with' if n == 0 else 'without'} -txindex, with blockhash")
gottx = self.nodes[n].getrawtransaction(txid=tx, verbosity=v, blockhash=block1)
missing_fields = set(fields).difference(gottx.keys())
if missing_fields:
raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction")
assert(len(gottx['vin']) > 0)
if v == 1:
assert('fee' not in gottx)
assert('prevout' not in gottx['vin'][0])
if v == 2:
assert(isinstance(gottx['fee'], Decimal))
assert('prevout' in gottx['vin'][0])
prevout = gottx['vin'][0]['prevout']
script_pub_key = prevout['scriptPubKey']
missing_fields = set(prevout_fields).difference(prevout.keys())
if missing_fields:
raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction")
missing_fields = set(script_pub_key_fields).difference(script_pub_key.keys())
if missing_fields:
raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction")
# check verbosity 2 without blockhash but with txindex
assert('fee' in self.nodes[0].getrawtransaction(txid=tx, verbosity=2))
# check that coinbase has no fee or does not throw any errors for verbosity 2
coin_base = self.nodes[1].getblock(block1)['tx'][0]
gottx = self.nodes[1].getrawtransaction(txid=coin_base, verbosity=2, blockhash=block1)
assert('fee' not in gottx)
def createrawtransaction_tests(self):
self.log.info("Test createrawtransaction")
# Test `createrawtransaction` required parameters