From e90d4437a213691f0565cd0253ecb403767273a7 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 26 Oct 2021 16:20:22 -0400 Subject: [PATCH] qa: add qa test for createauxblock --- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/createauxblock.py | 171 +++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 qa/rpc-tests/createauxblock.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 8f4784836..373477a10 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -151,6 +151,7 @@ testScripts = [ # 'p2p-versionbits-warning.py', 'preciousblock.py', 'importprunedfunds.py', + 'createauxblock.py', 'signmessages.py', # 'nulldummy.py', 'import-rescan.py', diff --git a/qa/rpc-tests/createauxblock.py b/qa/rpc-tests/createauxblock.py new file mode 100644 index 000000000..e4c02aea5 --- /dev/null +++ b/qa/rpc-tests/createauxblock.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# Copyright (c) 2021 The Dogecoin Core Developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""CreateAuxBlock QA test. + +# Tests createauxblock and submitauxblock RPC endpoints +""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +from test_framework import scrypt_auxpow as auxpow + +class CreateAuxBlockTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 2 + self.is_network_split = False + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-txindex"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug"])) + connect_nodes_bi(self.nodes, 0, 1) + self.sync_all() + + def run_test(self): + # Generate an initial chain + self.nodes[0].generate(100) + self.sync_all() + # Generate a block so that we are not "downloading blocks". + self.nodes[1].generate(1) + self.sync_all() + + dummy_p2pkh_addr = "mmMP9oKFdADezYzduwJFcLNmmi8JHUKdx9" + dummy_p2sh_addr = "2Mwvgpd2H7wDPXx8jWe3Vqiciix6JqSbsyz" + + # Compare basic data of createauxblock to getblocktemplate. + auxblock = self.nodes[0].createauxblock(dummy_p2pkh_addr) + blocktemplate = self.nodes[0].getblocktemplate() + assert_equal(auxblock["coinbasevalue"], blocktemplate["coinbasevalue"]) + assert_equal(auxblock["bits"], blocktemplate["bits"]) + assert_equal(auxblock["height"], blocktemplate["height"]) + assert_equal(auxblock["previousblockhash"], blocktemplate["previousblockhash"]) + + # Compare target and take byte order into account. + target = auxblock["target"] + reversedTarget = auxpow.reverseHex(target) + assert_equal(reversedTarget, blocktemplate["target"]) + + # Verify data that can be found in another way. + assert_equal(auxblock["chainid"], 98) + 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 = self.nodes[0].createauxblock(dummy_p2pkh_addr) + assert_equal(auxblock2, auxblock) + + # Calling with an invalid address must fail + try: + auxblock2 = self.nodes[0].createauxblock("x") + raise AssertionError("invalid address accepted") + except JSONRPCException as exc: + assert_equal(exc.error["code"], -8) + + # Calling with a different address ... + dummy_addr2 = self.nodes[0].getnewaddress() + auxblock3 = self.nodes[0].createauxblock(dummy_addr2) + + # ... must give another block because the coinbase recipient differs ... + # TODO: Need to cache per address on createauxblock for this to work + # assert auxblock3["hash"] != auxblock["hash"] + + # ... but must have retained the same parameterization otherwise + assert_equal(auxblock["coinbasevalue"], auxblock3["coinbasevalue"]) + assert_equal(auxblock["bits"], auxblock3["bits"]) + assert_equal(auxblock["height"], auxblock3["height"]) + assert_equal(auxblock["previousblockhash"], auxblock3["previousblockhash"]) + assert_equal(auxblock["chainid"], auxblock3["chainid"]) + assert_equal(auxblock["target"], auxblock3["target"]) + + # If we receive a new block, the template cache must be emptied. + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + auxblock4 = self.nodes[0].createauxblock(dummy_p2pkh_addr) + assert auxblock["hash"] != auxblock4["hash"] + try: + self.nodes[0].submitauxblock(auxblock["hash"], "x") + raise AssertionError("invalid block hash accepted") + except JSONRPCException as exc: + assert_equal(exc.error["code"], -8) + + # Invalid format for auxpow. + try: + self.nodes[0].submitauxblock(auxblock4["hash"], "x") + raise AssertionError("malformed auxpow accepted") + except JSONRPCException as exc: + assert_equal(exc.error["code"], -1) + + # 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 = self.nodes[0].createauxblock(dummy_p2pkh_addr) + reversedTarget = auxpow.reverseHex(auxblock["target"]) + + # Compute invalid auxpow. + apow = auxpow.computeAuxpowWithChainId(auxblock["hash"], reversedTarget, "98", False) + res = self.nodes[0].submitauxblock(auxblock["hash"], apow) + assert not res + + # Compute and submit valid auxpow. + apow = auxpow.computeAuxpowWithChainId(auxblock["hash"], reversedTarget, "98", True) + res = self.nodes[0].submitauxblock(auxblock["hash"], apow) + assert res + + # Make sure that the block is 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"]) + + # check the mined block and transaction + self.check_mined_block(auxblock, apow, dummy_p2pkh_addr, Decimal("500000"), txid) + + # Mine to a p2sh address + auxblock2 = self.nodes[0].createauxblock(dummy_p2sh_addr) + reversedTarget = auxpow.reverseHex(auxblock2["target"]) + apow = auxpow.computeAuxpowWithChainId(auxblock2["hash"], reversedTarget, "98", True) + res = self.nodes[0].submitauxblock(auxblock2["hash"], apow) + assert res + + self.sync_all() + + # check the mined block + self.check_mined_block(auxblock2, apow, dummy_p2sh_addr, Decimal("500000")) + + def check_mined_block(self, auxblock, apow, addr, min_value, txid=None): + # 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["parentblock"], apow[-160:]) + + # Call getrawtransaction and verify the coinbase tx + coinbasetx = self.nodes[0].getrawtransaction(data["tx"][0], True) + + assert coinbasetx["vout"][0]["value"] >= min_value + assert_equal(coinbasetx["vout"][0]["scriptPubKey"]["addresses"][0], addr) + + # Make sure the coinbase contains the block height + coinbase = coinbasetx["vin"][0]["coinbase"] + assert_equal("01%02x01" % auxblock["height"], coinbase[0:6]) + + # Make sure our transaction got mined, if any + if not txid is None: + assert txid in data["tx"] + +if __name__ == "__main__": + CreateAuxBlockTest().main()