diff options
Diffstat (limited to 'test/functional/auxpow_mining.py')
| -rwxr-xr-x | test/functional/auxpow_mining.py | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/test/functional/auxpow_mining.py b/test/functional/auxpow_mining.py new file mode 100755 index 000000000..bb4568772 --- /dev/null +++ b/test/functional/auxpow_mining.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2018 Daniel Kraft +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Test the merge-mining RPC interface: +# getauxblock, createauxblock, submitauxblock + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +from test_framework.auxpow import reverseHex +from test_framework.auxpow_testing import ( + computeAuxpow, + getCoinbaseAddr, + mineAuxpowBlockWithMethods, +) + +class AuxpowMiningTest (BitcoinTestFramework): + + def set_test_params (self): + self.num_nodes = 2 + + def add_options (self, parser): + parser.add_argument ("--segwit", dest="segwit", default=False, + action="store_true", + help="Test behaviour with SegWit active") + + def run_test (self): + # Enable mock time to be out of IBD. + self.enable_mocktime () + + # Activate segwit if requested. + if self.options.segwit: + self.nodes[0].generate (500) + self.sync_all () + + # Test with getauxblock and createauxblock/submitauxblock. + self.test_getauxblock () + self.test_create_submit_auxblock () + + def test_common (self, create, submit): + """ + Common test code that is shared between the tests for getauxblock and the + createauxblock / submitauxblock method pair. + """ + + # Verify data that can be found in another way. + auxblock = create () + assert_equal (auxblock['chainid'], 1) + assert_equal (auxblock['height'], self.nodes[0].getblockcount () + 1) + assert_equal (auxblock['previousblockhash'], + self.nodes[0].getblockhash (auxblock['height'] - 1)) + + # Calling again should give the same block. + auxblock2 = create () + assert_equal (auxblock2, auxblock) + + # If we receive a new block, the old hash will be replaced. + self.sync_all () + self.nodes[1].generate (1) + self.sync_all () + auxblock2 = create () + assert auxblock['hash'] != auxblock2['hash'] + assert_raises_rpc_error (-8, 'block hash unknown', submit, + auxblock['hash'], "x") + + # Invalid format for auxpow. + assert_raises_rpc_error (-1, None, submit, + auxblock2['hash'], "x") + + # Invalidate the block again, send a transaction and query for the + # auxblock to solve that contains the transaction. + self.nodes[0].generate (1) + addr = self.nodes[1].getnewaddress () + txid = self.nodes[0].sendtoaddress (addr, 1) + self.sync_all () + assert_equal (self.nodes[1].getrawmempool (), [txid]) + auxblock = create () + target = reverseHex (auxblock['_target']) + + # Compute invalid auxpow. + apow = computeAuxpow (auxblock['hash'], target, False) + res = submit (auxblock['hash'], apow) + assert not res + + # Compute and submit valid auxpow. + apow = computeAuxpow (auxblock['hash'], target, True) + res = submit (auxblock['hash'], apow) + assert res + + # Make sure that the block is indeed accepted. + self.sync_all () + assert_equal (self.nodes[1].getrawmempool (), []) + height = self.nodes[1].getblockcount () + assert_equal (height, auxblock['height']) + assert_equal (self.nodes[1].getblockhash (height), auxblock['hash']) + + # Call getblock and verify the auxpow field. + data = self.nodes[1].getblock (auxblock['hash']) + assert 'auxpow' in data + auxJson = data['auxpow'] + assert_equal (auxJson['index'], 0) + assert_equal (auxJson['chainindex'], 0) + assert_equal (auxJson['merklebranch'], []) + assert_equal (auxJson['chainmerklebranch'], []) + assert_equal (auxJson['parentblock'], apow[-160:]) + + # Also previous blocks should have 'auxpow', since all blocks (also + # those generated by "generate") are merge-mined. + oldHash = self.nodes[1].getblockhash (100) + data = self.nodes[1].getblock (oldHash) + assert 'auxpow' in data + + # Check that it paid correctly to the first node. + t = self.nodes[0].listtransactions ("*", 1) + assert_equal (len (t), 1) + t = t[0] + assert_equal (t['category'], "immature") + assert_equal (t['blockhash'], auxblock['hash']) + assert t['generated'] + assert_greater_than_or_equal (t['amount'], Decimal ("1")) + assert_equal (t['confirmations'], 1) + + # Verify the coinbase script. Ensure that it includes the block height + # to make the coinbase tx unique. The expected block height is around + # 200, so that the serialisation of the CScriptNum ends in an extra 00. + # The vector has length 2, which makes up for 02XX00 as the serialised + # height. Check this. (With segwit, the height is different, so we skip + # this for simplicity.) + if not self.options.segwit: + blk = self.nodes[1].getblock (auxblock['hash']) + tx = self.nodes[1].getrawtransaction (blk['tx'][0], 1) + coinbase = tx['vin'][0]['coinbase'] + assert_equal ("02%02x00" % auxblock['height'], coinbase[0 : 6]) + + def test_getauxblock (self): + """ + Test the getauxblock method. + """ + + create = self.nodes[0].getauxblock + submit = self.nodes[0].getauxblock + self.test_common (create, submit) + + # Ensure that the payout address is changed from one block to the next. + hash1 = mineAuxpowBlockWithMethods (create, submit) + hash2 = mineAuxpowBlockWithMethods (create, submit) + self.sync_all () + addr1 = getCoinbaseAddr (self.nodes[1], hash1) + addr2 = getCoinbaseAddr (self.nodes[1], hash2) + assert addr1 != addr2 + info = self.nodes[0].getaddressinfo (addr1) + assert info['ismine'] + info = self.nodes[0].getaddressinfo (addr2) + assert info['ismine'] + + def test_create_submit_auxblock (self): + """ + Test the createauxblock / submitauxblock method pair. + """ + + # Check for errors with wrong parameters. + assert_raises_rpc_error (-1, None, self.nodes[0].createauxblock) + assert_raises_rpc_error (-5, "Invalid coinbase payout address", + self.nodes[0].createauxblock, + "this_an_invalid_address") + + # Fix a coinbase address and construct methods for it. + coinbaseAddr = self.nodes[0].getnewaddress () + def create (): + return self.nodes[0].createauxblock (coinbaseAddr) + submit = self.nodes[0].submitauxblock + + # Run common tests. + self.test_common (create, submit) + + # Ensure that the payout address is the one which we specify + hash1 = mineAuxpowBlockWithMethods (create, submit) + hash2 = mineAuxpowBlockWithMethods (create, submit) + self.sync_all () + addr1 = getCoinbaseAddr (self.nodes[1], hash1) + addr2 = getCoinbaseAddr (self.nodes[1], hash2) + assert_equal (addr1, coinbaseAddr) + assert_equal (addr2, coinbaseAddr) + +if __name__ == '__main__': + AuxpowMiningTest ().main () |