2016-03-19 16:58:06 -03:00
#!/usr/bin/env python3
2021-07-28 07:57:16 -04:00
# Copyright (c) 2014-2021 The Bitcoin Core developers
2015-02-03 22:59:41 -03:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-17 20:34:40 -03:00
""" Test gettxoutproof and verifytxoutproof RPCs. """
2015-02-03 22:59:41 -03:00
2021-06-15 18:32:18 -04:00
from test_framework . messages import (
CMerkleBlock ,
2021-06-20 08:01:36 -04:00
from_hex ,
2021-06-15 18:32:18 -04:00
)
2015-05-02 07:53:35 -03:00
from test_framework . test_framework import BitcoinTestFramework
2021-06-15 18:32:18 -04:00
from test_framework . util import (
assert_equal ,
assert_raises_rpc_error ,
)
2020-09-09 05:38:19 -03:00
from test_framework . wallet import MiniWallet
2015-02-03 22:59:41 -03:00
class MerkleBlockTest ( BitcoinTestFramework ) :
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
2020-09-09 05:38:19 -03:00
self . num_nodes = 2
self . extra_args = [
[ ] ,
[ " -txindex " ] ,
]
2015-02-03 22:59:41 -03:00
def run_test ( self ) :
2020-09-09 05:38:19 -03:00
miniwallet = MiniWallet ( self . nodes [ 0 ] )
2015-02-03 22:59:41 -03:00
chain_height = self . nodes [ 1 ] . getblockcount ( )
2023-01-13 07:21:33 -03:00
assert_equal ( chain_height , 200 )
2020-09-09 05:38:19 -03:00
txid1 = miniwallet . send_self_transfer ( from_node = self . nodes [ 0 ] ) [ ' txid ' ]
txid2 = miniwallet . send_self_transfer ( from_node = self . nodes [ 0 ] ) [ ' txid ' ]
2017-02-10 13:04:13 -03:00
# This will raise an exception because the transaction is not yet in a block
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 5 , " Transaction not yet in block " , self . nodes [ 0 ] . gettxoutproof , [ txid1 ] )
2015-02-03 22:59:41 -03:00
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2015-02-03 22:59:41 -03:00
blockhash = self . nodes [ 0 ] . getblockhash ( chain_height + 1 )
txlist = [ ]
blocktxn = self . nodes [ 0 ] . getblock ( blockhash , True ) [ " tx " ]
txlist . append ( blocktxn [ 1 ] )
txlist . append ( blocktxn [ 2 ] )
2020-09-09 05:38:19 -03:00
assert_equal ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid1 ] ) ) , [ txid1 ] )
assert_equal ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid1 , txid2 ] ) ) , txlist )
assert_equal ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid1 , txid2 ] , blockhash ) ) , txlist )
2015-02-03 22:59:41 -03:00
2021-11-15 09:19:58 -03:00
txin_spent = miniwallet . get_utxo ( txid = txid2 ) # Get the change from txid2
2020-09-09 05:38:19 -03:00
tx3 = miniwallet . send_self_transfer ( from_node = self . nodes [ 0 ] , utxo_to_spend = txin_spent )
txid3 = tx3 [ ' txid ' ]
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2015-02-03 22:59:41 -03:00
txid_spent = txin_spent [ " txid " ]
2020-09-09 05:38:19 -03:00
txid_unspent = txid1 # Input was change from txid2, so txid1 should be unspent
2015-02-03 22:59:41 -03:00
2018-06-08 14:16:07 -04:00
# Invalid txids
2020-09-09 05:38:19 -03:00
assert_raises_rpc_error ( - 8 , " txid must be of length 64 (not 32, for ' 00000000000000000000000000000000 ' ) " , self . nodes [ 0 ] . gettxoutproof , [ " 00000000000000000000000000000000 " ] , blockhash )
assert_raises_rpc_error ( - 8 , " txid must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " , self . nodes [ 0 ] . gettxoutproof , [ " ZZZ0000000000000000000000000000000000000000000000000000000000000 " ] , blockhash )
2018-06-08 14:16:07 -04:00
# Invalid blockhashes
2020-09-09 05:38:19 -03:00
assert_raises_rpc_error ( - 8 , " blockhash must be of length 64 (not 32, for ' 00000000000000000000000000000000 ' ) " , self . nodes [ 0 ] . gettxoutproof , [ txid_spent ] , " 00000000000000000000000000000000 " )
assert_raises_rpc_error ( - 8 , " blockhash must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " , self . nodes [ 0 ] . gettxoutproof , [ txid_spent ] , " ZZZ0000000000000000000000000000000000000000000000000000000000000 " )
2016-01-17 08:03:56 -03:00
# We can't find the block from a fully-spent tx
2020-09-09 05:38:19 -03:00
assert_raises_rpc_error ( - 5 , " Transaction not yet in block " , self . nodes [ 0 ] . gettxoutproof , [ txid_spent ] )
2017-02-10 13:04:13 -03:00
# We can get the proof if we specify the block
2020-09-09 05:38:19 -03:00
assert_equal ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid_spent ] , blockhash ) ) , [ txid_spent ] )
2017-02-10 13:04:13 -03:00
# We can't get the proof if we specify a non-existent block
2020-09-09 05:38:19 -03:00
assert_raises_rpc_error ( - 5 , " Block not found " , self . nodes [ 0 ] . gettxoutproof , [ txid_spent ] , " 0000000000000000000000000000000000000000000000000000000000000000 " )
2017-02-10 13:04:13 -03:00
# We can get the proof if the transaction is unspent
2020-09-09 05:38:19 -03:00
assert_equal ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid_unspent ] ) ) , [ txid_unspent ] )
2017-02-10 13:04:13 -03:00
# We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter.
2020-09-09 05:38:19 -03:00
assert_equal ( sorted ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid1 , txid2 ] ) ) ) , sorted ( txlist ) )
assert_equal ( sorted ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 0 ] . gettxoutproof ( [ txid2 , txid1 ] ) ) ) , sorted ( txlist ) )
2017-02-10 13:04:13 -03:00
# We can always get a proof if we have a -txindex
2020-09-09 05:38:19 -03:00
assert_equal ( self . nodes [ 0 ] . verifytxoutproof ( self . nodes [ 1 ] . gettxoutproof ( [ txid_spent ] ) ) , [ txid_spent ] )
2017-02-10 13:04:13 -03:00
# We can't get a proof if we specify transactions from different blocks
2020-09-09 05:38:19 -03:00
assert_raises_rpc_error ( - 5 , " Not all transactions found in specified or retrieved block " , self . nodes [ 0 ] . gettxoutproof , [ txid1 , txid3 ] )
2020-09-09 05:48:45 -03:00
# Test empty list
2020-10-11 21:45:05 -03:00
assert_raises_rpc_error ( - 8 , " Parameter ' txids ' cannot be empty " , self . nodes [ 0 ] . gettxoutproof , [ ] )
2020-08-31 10:14:50 -04:00
# Test duplicate txid
assert_raises_rpc_error ( - 8 , ' Invalid parameter, duplicated txid ' , self . nodes [ 0 ] . gettxoutproof , [ txid1 , txid1 ] )
2017-02-10 13:04:13 -03:00
2018-06-20 16:03:25 -04:00
# Now we'll try tweaking a proof.
2020-09-09 05:38:19 -03:00
proof = self . nodes [ 1 ] . gettxoutproof ( [ txid1 , txid2 ] )
2018-06-20 16:03:25 -04:00
assert txid1 in self . nodes [ 0 ] . verifytxoutproof ( proof )
assert txid2 in self . nodes [ 1 ] . verifytxoutproof ( proof )
2021-06-20 08:01:36 -04:00
tweaked_proof = from_hex ( CMerkleBlock ( ) , proof )
2018-06-20 16:03:25 -04:00
# Make sure that our serialization/deserialization is working
2021-06-15 18:32:18 -04:00
assert txid1 in self . nodes [ 0 ] . verifytxoutproof ( tweaked_proof . serialize ( ) . hex ( ) )
2018-06-20 16:03:25 -04:00
# Check to see if we can go up the merkle tree and pass this off as a
# single-transaction block
tweaked_proof . txn . nTransactions = 1
tweaked_proof . txn . vHash = [ tweaked_proof . header . hashMerkleRoot ]
tweaked_proof . txn . vBits = [ True ] + [ False ] * 7
for n in self . nodes :
2021-06-15 18:32:18 -04:00
assert not n . verifytxoutproof ( tweaked_proof . serialize ( ) . hex ( ) )
2018-06-20 16:03:25 -04:00
# TODO: try more variants, eg transactions at different depths, and
# verify that the proofs are invalid
2015-02-03 22:59:41 -03:00
if __name__ == ' __main__ ' :
MerkleBlockTest ( ) . main ( )