189 lines
6.6 KiB
Python
189 lines
6.6 KiB
Python
|
#!/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 ()
|