1#!/usr/bin/env python3 2# Copyright (c) 2014-2019 Daniel Kraft 3# Distributed under the MIT software license, see the accompanying 4# file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6# Test the merge-mining RPC interface: 7# getauxblock, createauxblock, submitauxblock 8 9from test_framework.test_framework import BitcoinTestFramework 10from test_framework.util import ( 11 assert_equal, 12 assert_greater_than_or_equal, 13 assert_raises_rpc_error, 14) 15 16from test_framework.auxpow import reverseHex 17from test_framework.auxpow_testing import ( 18 computeAuxpow, 19 getCoinbaseAddr, 20 mineAuxpowBlockWithMethods, 21) 22 23from decimal import Decimal 24 25class AuxpowMiningTest (BitcoinTestFramework): 26 27 def set_test_params (self): 28 self.num_nodes = 2 29 30 def add_options (self, parser): 31 parser.add_argument ("--segwit", dest="segwit", default=False, 32 action="store_true", 33 help="Test behaviour with SegWit active") 34 35 def run_test (self): 36 # Activate segwit if requested. 37 if self.options.segwit: 38 self.nodes[0].generate (500) 39 self.sync_all () 40 41 # Test with getauxblock and createauxblock/submitauxblock. 42 self.test_getauxblock () 43 self.test_create_submit_auxblock () 44 45 def test_common (self, create, submit): 46 """ 47 Common test code that is shared between the tests for getauxblock and the 48 createauxblock / submitauxblock method pair. 49 """ 50 51 # Verify data that can be found in another way. 52 auxblock = create () 53 assert_equal (auxblock['chainid'], 1) 54 assert_equal (auxblock['height'], self.nodes[0].getblockcount () + 1) 55 assert_equal (auxblock['previousblockhash'], 56 self.nodes[0].getblockhash (auxblock['height'] - 1)) 57 58 # Calling again should give the same block. 59 auxblock2 = create () 60 assert_equal (auxblock2, auxblock) 61 62 # If we receive a new block, the old hash will be replaced. 63 self.sync_all () 64 self.nodes[1].generate (1) 65 self.sync_all () 66 auxblock2 = create () 67 assert auxblock['hash'] != auxblock2['hash'] 68 assert_raises_rpc_error (-8, 'block hash unknown', submit, 69 auxblock['hash'], "x") 70 71 # Invalid format for auxpow. 72 assert_raises_rpc_error (-1, None, submit, 73 auxblock2['hash'], "x") 74 75 # Invalidate the block again, send a transaction and query for the 76 # auxblock to solve that contains the transaction. 77 self.nodes[0].generate (1) 78 addr = self.nodes[1].getnewaddress () 79 txid = self.nodes[0].sendtoaddress (addr, 1) 80 self.sync_all () 81 assert_equal (self.nodes[1].getrawmempool (), [txid]) 82 auxblock = create () 83 target = reverseHex (auxblock['_target']) 84 85 # Cross-check target value with GBT to make explicitly sure that it is 86 # correct (not just implicitly by successfully mining blocks for it 87 # later on). 88 gbt = self.nodes[0].getblocktemplate ({"rules": ["segwit"]}) 89 assert_equal (target, gbt['target'].encode ("ascii")) 90 91 # Compute invalid auxpow. 92 apow = computeAuxpow (auxblock['hash'], target, False) 93 res = submit (auxblock['hash'], apow) 94 assert not res 95 96 # Compute and submit valid auxpow. 97 apow = computeAuxpow (auxblock['hash'], target, True) 98 res = submit (auxblock['hash'], apow) 99 assert res 100 101 # Make sure that the block is indeed accepted. 102 self.sync_all () 103 assert_equal (self.nodes[1].getrawmempool (), []) 104 height = self.nodes[1].getblockcount () 105 assert_equal (height, auxblock['height']) 106 assert_equal (self.nodes[1].getblockhash (height), auxblock['hash']) 107 108 # Call getblock and verify the auxpow field. 109 data = self.nodes[1].getblock (auxblock['hash']) 110 assert 'auxpow' in data 111 auxJson = data['auxpow'] 112 assert_equal (auxJson['chainindex'], 0) 113 assert_equal (auxJson['merklebranch'], []) 114 assert_equal (auxJson['chainmerklebranch'], []) 115 assert_equal (auxJson['parentblock'], apow[-160:]) 116 117 # Also previous blocks should have 'auxpow', since all blocks (also 118 # those generated by "generate") are merge-mined. 119 oldHash = self.nodes[1].getblockhash (100) 120 data = self.nodes[1].getblock (oldHash) 121 assert 'auxpow' in data 122 123 # Check that it paid correctly to the first node. 124 t = self.nodes[0].listtransactions ("*", 1) 125 assert_equal (len (t), 1) 126 t = t[0] 127 assert_equal (t['category'], "immature") 128 assert_equal (t['blockhash'], auxblock['hash']) 129 assert t['generated'] 130 assert_greater_than_or_equal (t['amount'], Decimal ("1")) 131 assert_equal (t['confirmations'], 1) 132 133 # Verify the coinbase script. Ensure that it includes the block height 134 # to make the coinbase tx unique. The expected block height is around 135 # 200, so that the serialisation of the CScriptNum ends in an extra 00. 136 # The vector has length 2, which makes up for 02XX00 as the serialised 137 # height. Check this. (With segwit, the height is different, so we skip 138 # this for simplicity.) 139 if not self.options.segwit: 140 blk = self.nodes[1].getblock (auxblock['hash']) 141 tx = self.nodes[1].getrawtransaction (blk['tx'][0], True, blk['hash']) 142 coinbase = tx['vin'][0]['coinbase'] 143 assert_equal ("02%02x00" % auxblock['height'], coinbase[0 : 6]) 144 145 def test_getauxblock (self): 146 """ 147 Test the getauxblock method. 148 """ 149 150 create = self.nodes[0].getauxblock 151 submit = self.nodes[0].getauxblock 152 self.test_common (create, submit) 153 154 # Ensure that the payout address is changed from one block to the next. 155 hash1 = mineAuxpowBlockWithMethods (create, submit) 156 hash2 = mineAuxpowBlockWithMethods (create, submit) 157 self.sync_all () 158 addr1 = getCoinbaseAddr (self.nodes[1], hash1) 159 addr2 = getCoinbaseAddr (self.nodes[1], hash2) 160 assert addr1 != addr2 161 info = self.nodes[0].getaddressinfo (addr1) 162 assert info['ismine'] 163 info = self.nodes[0].getaddressinfo (addr2) 164 assert info['ismine'] 165 166 def test_create_submit_auxblock (self): 167 """ 168 Test the createauxblock / submitauxblock method pair. 169 """ 170 171 # Check for errors with wrong parameters. 172 assert_raises_rpc_error (-1, None, self.nodes[0].createauxblock) 173 assert_raises_rpc_error (-5, "Invalid coinbase payout address", 174 self.nodes[0].createauxblock, 175 "this_an_invalid_address") 176 177 # Fix a coinbase address and construct methods for it. 178 coinbaseAddr = self.nodes[0].getnewaddress () 179 def create (): 180 return self.nodes[0].createauxblock (coinbaseAddr) 181 submit = self.nodes[0].submitauxblock 182 183 # Run common tests. 184 self.test_common (create, submit) 185 186 # Ensure that the payout address is the one which we specify 187 hash1 = mineAuxpowBlockWithMethods (create, submit) 188 hash2 = mineAuxpowBlockWithMethods (create, submit) 189 self.sync_all () 190 addr1 = getCoinbaseAddr (self.nodes[1], hash1) 191 addr2 = getCoinbaseAddr (self.nodes[1], hash2) 192 assert_equal (addr1, coinbaseAddr) 193 assert_equal (addr2, coinbaseAddr) 194 195 # Ensure that different payout addresses will generate different auxblocks 196 auxblock1 = self.nodes[0].createauxblock(self.nodes[0].getnewaddress ()) 197 auxblock2 = self.nodes[0].createauxblock(self.nodes[0].getnewaddress ()) 198 assert auxblock1['hash'] != auxblock2['hash'] 199 200if __name__ == '__main__': 201 AuxpowMiningTest ().main () 202