mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-04 10:59:08 +02:00
Merge bitcoin/bitcoin#31981: Add checkBlock() to Mining interface
a18e572328test: more template verification tests (Sjors Provoost)10c908808ftest: move gbt proposal mode tests to new file (Sjors Provoost)94959b8deeAdd checkBlock to Mining interface (Sjors Provoost)6077157531ipc: drop BlockValidationState special handling (Sjors Provoost)74690f4ed8validation: refactor TestBlockValidity (Sjors Provoost) Pull request description: This PR adds the IPC equivalent of the `getblocktemplate` RPC in `proposal` mode. In order to do so it has `TestBlockValidity` return error reasons as a string instead of `BlockValidationState`. This avoids complexity in IPC code for handling the latter struct. The new Mining interface method is used in `miner_tests`. It's not used by the `getblocktemplate` and `generateblock` RPC calls, see https://github.com/bitcoin/bitcoin/pull/31981#discussion_r2096473337 The `inconclusive-not-best-prevblk` check is moved from RPC code to `TestBlockValidity`. Test coverage is increased by `mining_template_verification.py`. Superseedes #31564 ## Background ### Verifying block templates (no PoW) Stratum v2 allows miners to generate their own block template. Pools may wish (or need) to verify these templates. This typically involves comparing mempools, asking miners to providing missing transactions and then reconstructing the proposed block.[^0] This is not sufficient to ensure a proposed block is actually valid. In some schemes miners could take advantage of incomplete validation[^1]. The Stratum Reference Implementation (SRI), currently the only Stratum v2 implementation, collects all missing mempool transactions, but does not yet fully verify the block.[^2]. It could use the `getblocktemplate` RPC in `proposal` mode, but using IPC is more performant, as it avoids serialising up to 4 MB of transaction data as JSON. (although SRI could use this PR, the Template Provider role doesn't need it, so this is _not_ part of #31098) [^0]: https://github.com/stratum-mining/sv2-spec/blob/main/06-Job-Declaration-Protocol.md [^1]: https://delvingbitcoin.org/t/pplns-with-job-declaration/1099/45?u=sjors [^2]: https://github.com/stratum-mining/stratum/blob/v1.1.0/roles/jd-server/src/lib/job_declarator/message_handler.rs#L196 ACKs for top commit: davidgumberg: reACKa18e572328achow101: ACKa18e572328TheCharlatan: ACKa18e572328ryanofsky: Code review ACKa18e572328just adding another NONFATAL_UNREACHABLE since last review Tree-SHA512: 1a6c29f45a1666114f10f55aed155980b90104db27761c78aada4727ce3129e6ae7a522d90a56314bd767bd7944dfa46e85fb9f714370fc83e6a585be7b044f1
This commit is contained in:
@@ -5,8 +5,10 @@
|
||||
"""Test mining RPCs
|
||||
|
||||
- getmininginfo
|
||||
- getblocktemplate proposal mode
|
||||
- submitblock"""
|
||||
- getblocktemplate
|
||||
- submitblock
|
||||
|
||||
mining_template_verification.py tests getblocktemplate in proposal mode"""
|
||||
|
||||
import copy
|
||||
from decimal import Decimal
|
||||
@@ -54,18 +56,6 @@ VERSIONBITS_TOP_BITS = 0x20000000
|
||||
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
|
||||
DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB]
|
||||
|
||||
|
||||
def assert_template(node, block, expect, rehash=True):
|
||||
if rehash:
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
rsp = node.getblocktemplate(template_request={
|
||||
'data': block.serialize().hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
assert_equal(rsp, expect)
|
||||
|
||||
|
||||
class MiningTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 3
|
||||
@@ -225,8 +215,13 @@ class MiningTest(BitcoinTestFramework):
|
||||
block.nBits = int(tmpl["bits"], 16)
|
||||
block.nNonce = 0
|
||||
block.vtx = [create_coinbase(height=int(tmpl["height"]))]
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.solve()
|
||||
assert_template(node, block, None)
|
||||
assert_equal(node.getblocktemplate(template_request={
|
||||
'data': block.serialize().hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
}), None)
|
||||
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nTime = t
|
||||
@@ -376,12 +371,6 @@ class MiningTest(BitcoinTestFramework):
|
||||
self.wallet = MiniWallet(node)
|
||||
self.mine_chain()
|
||||
|
||||
def assert_submitblock(block, result_str_1, result_str_2=None):
|
||||
block.solve()
|
||||
result_str_2 = result_str_2 or 'duplicate-invalid'
|
||||
assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex()))
|
||||
assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex()))
|
||||
|
||||
self.log.info('getmininginfo')
|
||||
mining_info = node.getmininginfo()
|
||||
assert_equal(mining_info['blocks'], 200)
|
||||
@@ -433,103 +422,24 @@ class MiningTest(BitcoinTestFramework):
|
||||
block.nBits = int(tmpl["bits"], 16)
|
||||
block.nNonce = 0
|
||||
block.vtx = [coinbase_tx]
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
|
||||
self.log.info("getblocktemplate: segwit rule must be set")
|
||||
assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate, {})
|
||||
|
||||
self.log.info("getblocktemplate: Test valid block")
|
||||
assert_template(node, block, None)
|
||||
|
||||
self.log.info("submitblock: Test block decode failure")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex())
|
||||
|
||||
self.log.info("getblocktemplate: Test bad input hash for coinbase transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].vin[0].prevout.hash += 1
|
||||
assert_template(node, bad_block, 'bad-cb-missing')
|
||||
|
||||
self.log.info("submitblock: Test bad input hash for coinbase transaction")
|
||||
bad_block.solve()
|
||||
assert_equal("bad-cb-missing", node.submitblock(hexdata=bad_block.serialize().hex()))
|
||||
|
||||
self.log.info("submitblock: Test block with no transactions")
|
||||
no_tx_block = copy.deepcopy(block)
|
||||
no_tx_block.vtx.clear()
|
||||
no_tx_block.hashMerkleRoot = 0
|
||||
no_tx_block.solve()
|
||||
assert_equal("bad-blk-length", node.submitblock(hexdata=no_tx_block.serialize().hex()))
|
||||
|
||||
self.log.info("submitblock: Test empty block")
|
||||
assert_equal('high-hash', node.submitblock(hexdata=CBlock().serialize().hex()))
|
||||
|
||||
self.log.info("getblocktemplate: Test truncated final transaction")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
|
||||
'data': block.serialize()[:-1].hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
|
||||
self.log.info("getblocktemplate: Test duplicate transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx.append(bad_block.vtx[0])
|
||||
assert_template(node, bad_block, 'bad-txns-duplicate')
|
||||
assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate')
|
||||
|
||||
self.log.info("getblocktemplate: Test invalid transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_tx = copy.deepcopy(bad_block.vtx[0])
|
||||
bad_tx.vin[0].prevout.hash = 255
|
||||
bad_block.vtx.append(bad_tx)
|
||||
assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
|
||||
assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent')
|
||||
|
||||
self.log.info("getblocktemplate: Test nonfinal transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].nLockTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'bad-txns-nonfinal')
|
||||
assert_submitblock(bad_block, 'bad-txns-nonfinal')
|
||||
|
||||
self.log.info("getblocktemplate: Test bad tx count")
|
||||
# The tx count is immediately after the block header
|
||||
bad_block_sn = bytearray(block.serialize())
|
||||
assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
|
||||
bad_block_sn[BLOCK_HEADER_SIZE] += 1
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
|
||||
'data': bad_block_sn.hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
|
||||
self.log.info("getblocktemplate: Test bad bits")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nBits = 469762303 # impossible in the real world
|
||||
assert_template(node, bad_block, 'bad-diffbits')
|
||||
|
||||
self.log.info("getblocktemplate: Test bad merkle root")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashMerkleRoot += 1
|
||||
assert_template(node, bad_block, 'bad-txnmrklroot', False)
|
||||
assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot')
|
||||
|
||||
self.log.info("getblocktemplate: Test bad timestamps")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'time-too-new')
|
||||
assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
|
||||
bad_block.nTime = 0
|
||||
assert_template(node, bad_block, 'time-too-old')
|
||||
assert_submitblock(bad_block, 'time-too-old', 'time-too-old')
|
||||
|
||||
self.log.info("getblocktemplate: Test not best block")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashPrevBlock = 123
|
||||
assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
|
||||
assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found')
|
||||
|
||||
self.log.info('submitheader tests')
|
||||
assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
|
||||
assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2)))
|
||||
assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, bad_block).serialize().hex()))
|
||||
|
||||
missing_ancestor_block = copy.deepcopy(block)
|
||||
missing_ancestor_block.hashPrevBlock = 123
|
||||
assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, missing_ancestor_block).serialize().hex()))
|
||||
|
||||
block.nTime += 1
|
||||
block.solve()
|
||||
|
||||
302
test/functional/mining_template_verification.py
Executable file
302
test/functional/mining_template_verification.py
Executable file
@@ -0,0 +1,302 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2024-Present The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test getblocktemplate RPC in proposal mode
|
||||
|
||||
Generate several blocks and test them against the getblocktemplate RPC.
|
||||
"""
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import copy
|
||||
|
||||
from test_framework.blocktools import (
|
||||
create_block,
|
||||
create_coinbase,
|
||||
add_witness_commitment,
|
||||
)
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
from test_framework.messages import (
|
||||
BLOCK_HEADER_SIZE,
|
||||
uint256_from_compact,
|
||||
)
|
||||
|
||||
from test_framework.wallet import (
|
||||
MiniWallet,
|
||||
)
|
||||
|
||||
def assert_template(node, block, expect, *, rehash=True, submit=True, solve=True, expect_submit=None):
|
||||
if rehash:
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
|
||||
rsp = node.getblocktemplate(template_request={
|
||||
'data': block.serialize().hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
assert_equal(rsp, expect)
|
||||
# Only attempt to submit invalid templates
|
||||
if submit and expect is not None:
|
||||
# submitblock runs checks in a different order, so may not return
|
||||
# the same error
|
||||
if expect_submit is None:
|
||||
expect_submit = expect
|
||||
if solve:
|
||||
block.solve()
|
||||
assert_equal(node.submitblock(block.serialize().hex()), expect_submit)
|
||||
|
||||
class MiningTemplateVerificationTest(BitcoinTestFramework):
|
||||
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
|
||||
def valid_block_test(self, node, block):
|
||||
self.log.info("Valid block")
|
||||
assert_template(node, block, None)
|
||||
|
||||
def cb_missing_test(self, node, block):
|
||||
self.log.info("Bad input hash for coinbase transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].vin[0].prevout.hash += 1
|
||||
assert_template(node, bad_block, 'bad-cb-missing')
|
||||
|
||||
def block_without_transactions_test(self, node, block):
|
||||
self.log.info("Block with no transactions")
|
||||
|
||||
no_tx_block = copy.deepcopy(block)
|
||||
no_tx_block.vtx.clear()
|
||||
no_tx_block.hashMerkleRoot = 0
|
||||
no_tx_block.solve()
|
||||
assert_template(node, no_tx_block, 'bad-blk-length', rehash=False)
|
||||
|
||||
def truncated_final_transaction_test(self, node, block):
|
||||
self.log.info("Truncated final transaction")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate,
|
||||
template_request={
|
||||
"data": block.serialize()[:-1].hex(),
|
||||
"mode": "proposal",
|
||||
"rules": ["segwit"],
|
||||
}
|
||||
)
|
||||
|
||||
def duplicate_transaction_test(self, node, block):
|
||||
self.log.info("Duplicate transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx.append(bad_block.vtx[0])
|
||||
assert_template(node, bad_block, 'bad-txns-duplicate')
|
||||
|
||||
def thin_air_spending_test(self, node, block):
|
||||
self.log.info("Transaction that spends from thin air")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_tx = copy.deepcopy(bad_block.vtx[0])
|
||||
bad_tx.vin[0].prevout.hash = 255
|
||||
bad_block.vtx.append(bad_tx)
|
||||
assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
|
||||
|
||||
def non_final_transaction_test(self, node, block):
|
||||
self.log.info("Non-final transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].nLockTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'bad-txns-nonfinal')
|
||||
|
||||
def bad_tx_count_test(self, node, block):
|
||||
self.log.info("Bad tx count")
|
||||
# The tx count is immediately after the block header
|
||||
bad_block_sn = bytearray(block.serialize())
|
||||
assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
|
||||
bad_block_sn[BLOCK_HEADER_SIZE] += 1
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
|
||||
'data': bad_block_sn.hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
|
||||
def nbits_test(self, node, block):
|
||||
self.log.info("Extremely high nBits")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nBits = 469762303 # impossible in the real world
|
||||
assert_template(node, bad_block, "bad-diffbits", solve=False, expect_submit="high-hash")
|
||||
|
||||
self.log.info("Lowering nBits should make the block invalid")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nBits -= 1
|
||||
assert_template(node, bad_block, "bad-diffbits")
|
||||
|
||||
def merkle_root_test(self, node, block):
|
||||
self.log.info("Bad merkle root")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashMerkleRoot += 1
|
||||
assert_template(node, bad_block, 'bad-txnmrklroot', rehash=False)
|
||||
|
||||
def bad_timestamp_test(self, node, block):
|
||||
self.log.info("Bad timestamps")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'time-too-new')
|
||||
bad_block.nTime = 0
|
||||
assert_template(node, bad_block, 'time-too-old')
|
||||
|
||||
def current_tip_test(self, node, block):
|
||||
self.log.info("Block must build on the current tip")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashPrevBlock = 123
|
||||
bad_block.solve()
|
||||
|
||||
assert_template(node, bad_block, "inconclusive-not-best-prevblk", expect_submit="prev-blk-not-found")
|
||||
|
||||
def pow_test(self, node, block):
|
||||
'''Modifies block with the generated PoW'''
|
||||
self.log.info("Generate a block")
|
||||
target = uint256_from_compact(block.nBits)
|
||||
# Ensure that it doesn't meet the target by coincidence
|
||||
while block.sha256 <= target:
|
||||
block.nNonce += 1
|
||||
block.rehash()
|
||||
self.log.debug("Found a nonce")
|
||||
|
||||
self.log.info("A block template doesn't need PoW")
|
||||
assert_template(node, block, None)
|
||||
|
||||
self.log.info("Add proof of work")
|
||||
block.solve()
|
||||
assert_template(node, block, None)
|
||||
|
||||
def submit_test(self, node, block_0_height, block):
|
||||
self.log.info("getblocktemplate call in previous tests did not submit the block")
|
||||
assert_equal(node.getblockcount(), block_0_height + 1)
|
||||
|
||||
self.log.info("Submitting this block should succeed")
|
||||
assert_equal(node.submitblock(block.serialize().hex()), None)
|
||||
node.waitforblockheight(2)
|
||||
|
||||
def transaction_test(self, node, block_0_height, tx):
|
||||
self.log.info("make block template with a transaction")
|
||||
|
||||
block_1 = node.getblock(node.getblockhash(block_0_height + 1))
|
||||
block_2_hash = node.getblockhash(block_0_height + 2)
|
||||
|
||||
block_3 = create_block(
|
||||
int(block_2_hash, 16),
|
||||
create_coinbase(block_0_height + 3),
|
||||
block_1["mediantime"] + 1,
|
||||
txlist=[tx["hex"]],
|
||||
)
|
||||
assert_equal(len(block_3.vtx), 2)
|
||||
add_witness_commitment(block_3)
|
||||
block_3.solve()
|
||||
assert_template(node, block_3, None)
|
||||
|
||||
self.log.info("checking block validity did not update the UTXO set")
|
||||
# Call again to ensure the UTXO set wasn't updated
|
||||
assert_template(node, block_3, None)
|
||||
|
||||
def overspending_transaction_test(self, node, block_0_height, tx):
|
||||
self.log.info("Add an transaction that spends too much")
|
||||
|
||||
block_1 = node.getblock(node.getblockhash(block_0_height + 1))
|
||||
block_2_hash = node.getblockhash(block_0_height + 2)
|
||||
|
||||
bad_tx = copy.deepcopy(tx)
|
||||
bad_tx["tx"].vout[0].nValue = 10000000000
|
||||
bad_tx_hex = bad_tx["tx"].serialize().hex()
|
||||
assert_equal(
|
||||
node.testmempoolaccept([bad_tx_hex])[0]["reject-reason"],
|
||||
"bad-txns-in-belowout",
|
||||
)
|
||||
block_3 = create_block(
|
||||
int(block_2_hash, 16),
|
||||
create_coinbase(block_0_height + 3),
|
||||
block_1["mediantime"] + 1,
|
||||
txlist=[bad_tx_hex],
|
||||
)
|
||||
assert_equal(len(block_3.vtx), 2)
|
||||
add_witness_commitment(block_3)
|
||||
block_3.solve()
|
||||
|
||||
assert_template(node, block_3, "bad-txns-in-belowout")
|
||||
|
||||
def spend_twice_test(self, node, block_0_height, tx):
|
||||
block_1 = node.getblock(node.getblockhash(block_0_height + 1))
|
||||
block_2_hash = node.getblockhash(block_0_height + 2)
|
||||
|
||||
self.log.info("Can't spend coins twice")
|
||||
tx_hex = tx["tx"].serialize().hex()
|
||||
tx_2 = copy.deepcopy(tx)
|
||||
tx_2_hex = tx_2["tx"].serialize().hex()
|
||||
# Nothing wrong with these transactions individually
|
||||
assert_equal(node.testmempoolaccept([tx_hex])[0]["allowed"], True)
|
||||
assert_equal(node.testmempoolaccept([tx_2_hex])[0]["allowed"], True)
|
||||
# But can't be combined
|
||||
assert_equal(
|
||||
node.testmempoolaccept([tx_hex, tx_2_hex])[0]["package-error"],
|
||||
"package-contains-duplicates",
|
||||
)
|
||||
block_3 = create_block(
|
||||
int(block_2_hash, 16),
|
||||
create_coinbase(block_0_height + 3),
|
||||
block_1["mediantime"] + 1,
|
||||
txlist=[tx_hex, tx_2_hex],
|
||||
)
|
||||
assert_equal(len(block_3.vtx), 3)
|
||||
add_witness_commitment(block_3)
|
||||
|
||||
assert_template(node, block_3, "bad-txns-inputs-missingorspent", submit=False)
|
||||
|
||||
return block_3
|
||||
|
||||
def parallel_test(self, node, block_3):
|
||||
# Ensure that getblocktemplate can be called concurrently by many threads.
|
||||
self.log.info("Check blocks in parallel")
|
||||
check_50_blocks = lambda n: [
|
||||
assert_template(n, block_3, "bad-txns-inputs-missingorspent", submit=False)
|
||||
for _ in range(50)
|
||||
]
|
||||
rpcs = [node.cli for _ in range(6)]
|
||||
with ThreadPoolExecutor(max_workers=len(rpcs)) as threads:
|
||||
list(threads.map(check_50_blocks, rpcs))
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
block_0_height = node.getblockcount()
|
||||
self.generate(node, sync_fun=self.no_op, nblocks=1)
|
||||
block_1 = node.getblock(node.getbestblockhash())
|
||||
block_2 = create_block(
|
||||
int(block_1["hash"], 16),
|
||||
create_coinbase(block_0_height + 2),
|
||||
block_1["mediantime"] + 1,
|
||||
)
|
||||
|
||||
self.valid_block_test(node, block_2)
|
||||
self.cb_missing_test(node, block_2)
|
||||
self.block_without_transactions_test(node, block_2)
|
||||
self.truncated_final_transaction_test(node, block_2)
|
||||
self.duplicate_transaction_test(node, block_2)
|
||||
self.thin_air_spending_test(node, block_2)
|
||||
self.non_final_transaction_test(node, block_2)
|
||||
self.bad_tx_count_test(node, block_2)
|
||||
self.nbits_test(node, block_2)
|
||||
self.merkle_root_test(node, block_2)
|
||||
self.bad_timestamp_test(node, block_2)
|
||||
self.current_tip_test(node, block_2)
|
||||
# This sets the PoW for the next test
|
||||
self.pow_test(node, block_2)
|
||||
self.submit_test(node, block_0_height, block_2)
|
||||
|
||||
self.log.info("Generate a transaction")
|
||||
tx = MiniWallet(node).create_self_transfer()
|
||||
|
||||
self.transaction_test(node, block_0_height, tx)
|
||||
self.overspending_transaction_test(node, block_0_height, tx)
|
||||
block_3 = self.spend_twice_test(node, block_0_height, tx)
|
||||
self.parallel_test(node, block_3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
MiningTemplateVerificationTest(__file__).main()
|
||||
@@ -209,6 +209,7 @@ BASE_SCRIPTS = [
|
||||
'rpc_decodescript.py',
|
||||
'rpc_blockchain.py --v1transport',
|
||||
'rpc_blockchain.py --v2transport',
|
||||
'mining_template_verification.py',
|
||||
'rpc_deprecated.py',
|
||||
'wallet_disable.py',
|
||||
'wallet_change_address.py',
|
||||
|
||||
Reference in New Issue
Block a user