aboutsummaryrefslogtreecommitdiff
path: root/test/functional/auxpow_mining.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/auxpow_mining.py')
-rwxr-xr-xtest/functional/auxpow_mining.py188
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 ()