Test RandomX P2P header validation

This commit is contained in:
Capri 2026-09-17 13:05:42 +00:00
commit 0411c96b45
Signed by: VextaProject
SSH key fingerprint: SHA256:739BZv8LvIDY2tR7lXLy/IQ6MaGTQtYcG9GNvCjmpVM

View file

@ -10,7 +10,13 @@ import threading
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR
from test_framework.authproxy import JSONRPCException from test_framework.authproxy import JSONRPCException
from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS
from test_framework.messages import CBlock, CBlockHeader, from_hex from test_framework.messages import (
CBlock,
CBlockHeader,
from_hex,
msg_headers,
)
from test_framework.p2p import P2PDataStore
from test_framework.test_framework import DigiByteTestFramework from test_framework.test_framework import DigiByteTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
@ -26,16 +32,17 @@ BLOCK_VERSION_RANDOMX = 4 << 8
class RandomXGetBlockTemplateTest(DigiByteTestFramework): class RandomXGetBlockTemplateTest(DigiByteTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 2 self.num_nodes = 3
self.setup_clean_chain = True self.setup_clean_chain = True
self.supports_cli = False self.supports_cli = False
def run_test(self): def run_test(self):
node = self.nodes[0] node = self.nodes[0]
# Keep node 1 isolated at genesis so RandomX blocks can later be # Keep nodes 1 and 2 isolated at genesis for independent
# submitted to it explicitly through submitblock. # RandomX validation-path tests later in this test.
self.stop_node(1) self.stop_node(1)
self.stop_node(2)
self.log.info("RandomX GBT is rejected before activation") self.log.info("RandomX GBT is rejected before activation")
assert_raises_rpc_error( assert_raises_rpc_error(
@ -412,7 +419,7 @@ class RandomXGetBlockTemplateTest(DigiByteTestFramework):
self.log.info("submitheader rejects invalid RandomX proof of work") self.log.info("submitheader rejects invalid RandomX proof of work")
invalid_header_block = from_hex(CBlock(), randomx_proposal_block) invalid_header_block = from_hex(CBlock(), randomx_proposal_block)
original_nonce = invalid_header_block.nNonce original_nonce = invalid_header_block.nNonce
saw_high_hash = False high_hash_nonce = None
for nonce_offset in range(1, 33): for nonce_offset in range(1, 33):
invalid_header_block.nNonce = ( invalid_header_block.nNonce = (
@ -425,34 +432,68 @@ class RandomXGetBlockTemplateTest(DigiByteTestFramework):
) )
except JSONRPCException as e: except JSONRPCException as e:
if "high-hash" in e.error["message"]: if "high-hash" in e.error["message"]:
saw_high_hash = True high_hash_nonce = invalid_header_block.nNonce
break break
raise raise
assert saw_high_hash assert high_hash_nonce is not None
assert_equal(submit_node.getblockcount(), 0) assert_equal(submit_node.getblockcount(), 0)
self.log.info("submitblock rejects invalid RandomX proof of work") self.log.info("submitblock rejects invalid RandomX proof of work")
self.start_node(
2,
extra_args=["-testactivationheight=randomx@1"],
)
invalid_submit_node = self.nodes[2]
assert_equal(invalid_submit_node.getblockcount(), 0)
invalid_block = from_hex(CBlock(), randomx_proposal_block) invalid_block = from_hex(CBlock(), randomx_proposal_block)
original_nonce = invalid_block.nNonce invalid_block.nNonce = high_hash_nonce
saw_submitblock_high_hash = False
# Use a separate nonce range from the submitheader test so these assert_equal(
# candidate block hashes have not already been marked invalid. invalid_submit_node.submitblock(
for nonce_offset in range(1000, 1032):
invalid_block.nNonce = (
original_nonce + nonce_offset
) & 0xffffffff
result = submit_node.submitblock(
invalid_block.serialize().hex() invalid_block.serialize().hex()
),
"high-hash",
) )
if result == "high-hash": self.stop_node(2)
saw_submitblock_high_hash = True assert_equal(submit_node.getblockcount(), 0)
break
assert saw_submitblock_high_hash self.log.info("P2P headers accepts a valid RandomX header")
p2p_peer = submit_node.add_p2p_connection(P2PDataStore())
valid_p2p_block = from_hex(CBlock(), randomx_proposal_block)
p2p_peer.send_message(
msg_headers([CBlockHeader(valid_p2p_block)])
)
p2p_peer.sync_with_ping()
valid_p2p_header = submit_node.getblockheader(
randomx_proposal_hash
)
assert_equal(valid_p2p_header["hash"], randomx_proposal_hash)
assert_equal(valid_p2p_header["pow_algo_id"], 1)
assert_equal(valid_p2p_header["pow_algo"], "randomx")
assert_equal(submit_node.getblockcount(), 0)
self.log.info("P2P headers rejects invalid RandomX difficulty")
invalid_p2p_block = from_hex(CBlock(), randomx_proposal_block)
invalid_p2p_block.nBits ^= 1
invalid_p2p_hash = invalid_p2p_block.rehash()
invalid_p2p_hash_hex = f"{invalid_p2p_hash:064x}"
p2p_peer.send_message(
msg_headers([CBlockHeader(invalid_p2p_block)])
)
p2p_peer.sync_with_ping()
assert_raises_rpc_error(
-5,
"Block not found",
submit_node.getblockheader,
invalid_p2p_hash_hex,
)
assert_equal(submit_node.getblockcount(), 0) assert_equal(submit_node.getblockcount(), 0)
self.log.info("Submit the mixed SHA256D and RandomX headers first") self.log.info("Submit the mixed SHA256D and RandomX headers first")