2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2019-12-30 10:39:22 +01:00
# Copyright (c) 2014-2019 The Bitcoin Core developers
2015-02-04 02:59:41 +01:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Test gettxoutproof and verifytxoutproof RPCs. """
2015-02-04 02:59:41 +01:00
2018-07-07 00:10:35 +02:00
from test_framework . messages import CMerkleBlock , FromHex , ToHex
2015-05-02 12:53:35 +02:00
from test_framework . test_framework import BitcoinTestFramework
2018-07-07 00:10:35 +02:00
from test_framework . util import assert_equal , assert_raises_rpc_error , connect_nodes
2015-02-04 02:59:41 +01:00
class MerkleBlockTest ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
2017-08-24 17:11:56 +02:00
self . num_nodes = 4
2016-05-14 13:01:31 +02:00
self . setup_clean_chain = True
2017-04-03 15:34:04 +02:00
# Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing
self . extra_args = [ [ ] , [ ] , [ ] , [ " -txindex " ] ]
2015-02-04 02:59:41 +01:00
2018-09-09 19:32:37 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2015-02-04 02:59:41 +01:00
def setup_network ( self ) :
2017-04-03 15:34:04 +02:00
self . setup_nodes ( )
2015-02-04 02:59:41 +01:00
connect_nodes ( self . nodes [ 0 ] , 1 )
connect_nodes ( self . nodes [ 0 ] , 2 )
connect_nodes ( self . nodes [ 0 ] , 3 )
self . sync_all ( )
def run_test ( self ) :
2017-03-08 00:46:17 +01:00
self . log . info ( " Mining blocks... " )
2015-02-04 02:59:41 +01:00
self . nodes [ 0 ] . generate ( 105 )
self . sync_all ( )
chain_height = self . nodes [ 1 ] . getblockcount ( )
assert_equal ( chain_height , 105 )
assert_equal ( self . nodes [ 1 ] . getbalance ( ) , 0 )
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 0 )
node0utxos = self . nodes [ 0 ] . listunspent ( 1 )
2015-12-06 20:24:02 +01:00
tx1 = self . nodes [ 0 ] . createrawtransaction ( [ node0utxos . pop ( ) ] , { self . nodes [ 1 ] . getnewaddress ( ) : 49.99 } )
2017-09-06 01:49:18 +02:00
txid1 = self . nodes [ 0 ] . sendrawtransaction ( self . nodes [ 0 ] . signrawtransactionwithwallet ( tx1 ) [ " hex " ] )
2015-12-06 20:24:02 +01:00
tx2 = self . nodes [ 0 ] . createrawtransaction ( [ node0utxos . pop ( ) ] , { self . nodes [ 1 ] . getnewaddress ( ) : 49.99 } )
2017-09-06 01:49:18 +02:00
txid2 = self . nodes [ 0 ] . sendrawtransaction ( self . nodes [ 0 ] . signrawtransactionwithwallet ( tx2 ) [ " hex " ] )
2017-02-10 17:04:13 +01:00
# This will raise an exception because the transaction is not yet in a block
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 5 , " Transaction not yet in block " , self . nodes [ 0 ] . gettxoutproof , [ txid1 ] )
2015-02-04 02:59:41 +01:00
self . nodes [ 0 ] . generate ( 1 )
blockhash = self . nodes [ 0 ] . getblockhash ( chain_height + 1 )
self . sync_all ( )
txlist = [ ]
blocktxn = self . nodes [ 0 ] . getblock ( blockhash , True ) [ " tx " ]
txlist . append ( blocktxn [ 1 ] )
txlist . append ( blocktxn [ 2 ] )
assert_equal ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid1 ] ) ) , [ txid1 ] )
assert_equal ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid1 , txid2 ] ) ) , txlist )
assert_equal ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid1 , txid2 ] , blockhash ) ) , txlist )
txin_spent = self . nodes [ 1 ] . listunspent ( 1 ) . pop ( )
2015-12-06 20:24:02 +01:00
tx3 = self . nodes [ 1 ] . createrawtransaction ( [ txin_spent ] , { self . nodes [ 0 ] . getnewaddress ( ) : 49.98 } )
2017-09-06 01:49:18 +02:00
txid3 = self . nodes [ 0 ] . sendrawtransaction ( self . nodes [ 1 ] . signrawtransactionwithwallet ( tx3 ) [ " hex " ] )
2015-02-04 02:59:41 +01:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
txid_spent = txin_spent [ " txid " ]
txid_unspent = txid1 if txin_spent [ " txid " ] != txid1 else txid2
2018-06-08 20:16:07 +02:00
# Invalid txids
assert_raises_rpc_error ( - 8 , " txid must be of length 64 (not 32, for ' 00000000000000000000000000000000 ' ) " , self . nodes [ 2 ] . gettxoutproof , [ " 00000000000000000000000000000000 " ] , blockhash )
assert_raises_rpc_error ( - 8 , " txid must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " , self . nodes [ 2 ] . gettxoutproof , [ " ZZZ0000000000000000000000000000000000000000000000000000000000000 " ] , blockhash )
# Invalid blockhashes
assert_raises_rpc_error ( - 8 , " blockhash must be of length 64 (not 32, for ' 00000000000000000000000000000000 ' ) " , self . nodes [ 2 ] . gettxoutproof , [ txid_spent ] , " 00000000000000000000000000000000 " )
assert_raises_rpc_error ( - 8 , " blockhash must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " , self . nodes [ 2 ] . gettxoutproof , [ txid_spent ] , " ZZZ0000000000000000000000000000000000000000000000000000000000000 " )
2016-01-17 12:03:56 +01:00
# We can't find the block from a fully-spent tx
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 5 , " Transaction not yet in block " , self . nodes [ 2 ] . gettxoutproof , [ txid_spent ] )
2017-02-10 17:04:13 +01:00
# We can get the proof if we specify the block
2015-02-04 02:59:41 +01:00
assert_equal ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid_spent ] , blockhash ) ) , [ txid_spent ] )
2017-02-10 17:04:13 +01:00
# We can't get the proof if we specify a non-existent block
2018-06-08 20:16:07 +02:00
assert_raises_rpc_error ( - 5 , " Block not found " , self . nodes [ 2 ] . gettxoutproof , [ txid_spent ] , " 0000000000000000000000000000000000000000000000000000000000000000 " )
2017-02-10 17:04:13 +01:00
# We can get the proof if the transaction is unspent
2015-02-04 02:59:41 +01:00
assert_equal ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid_unspent ] ) ) , [ txid_unspent ] )
2017-02-10 17:04:13 +01: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.
assert_equal ( sorted ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid1 , txid2 ] ) ) ) , sorted ( txlist ) )
assert_equal ( sorted ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 2 ] . gettxoutproof ( [ txid2 , txid1 ] ) ) ) , sorted ( txlist ) )
# We can always get a proof if we have a -txindex
2015-02-04 02:59:41 +01:00
assert_equal ( self . nodes [ 2 ] . verifytxoutproof ( self . nodes [ 3 ] . gettxoutproof ( [ txid_spent ] ) ) , [ txid_spent ] )
2017-02-10 17:04:13 +01:00
# We can't get a proof if we specify transactions from different blocks
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 5 , " Not all transactions found in specified or retrieved block " , self . nodes [ 2 ] . gettxoutproof , [ txid1 , txid3 ] )
2017-02-10 17:04:13 +01:00
2018-06-20 22:03:25 +02:00
# Now we'll try tweaking a proof.
proof = self . nodes [ 3 ] . gettxoutproof ( [ txid1 , txid2 ] )
assert txid1 in self . nodes [ 0 ] . verifytxoutproof ( proof )
assert txid2 in self . nodes [ 1 ] . verifytxoutproof ( proof )
tweaked_proof = FromHex ( CMerkleBlock ( ) , proof )
# Make sure that our serialization/deserialization is working
assert txid1 in self . nodes [ 2 ] . verifytxoutproof ( ToHex ( tweaked_proof ) )
# 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 :
assert not n . verifytxoutproof ( ToHex ( tweaked_proof ) )
# TODO: try more variants, eg transactions at different depths, and
# verify that the proofs are invalid
2015-02-04 02:59:41 +01:00
if __name__ == ' __main__ ' :
MerkleBlockTest ( ) . main ( )