Changeset View
Changeset View
Standalone View
Standalone View
test/functional/mining_basic.py
Show All 37 Lines | |||||
class MiningTest(BitcoinTestFramework): | class MiningTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.setup_clean_chain = False | self.setup_clean_chain = False | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
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=b2x(block.serialize()))) | |||||
assert_equal(result_str_2, node.submitblock( | |||||
hexdata=b2x(block.serialize()))) | |||||
self.log.info('getmininginfo') | self.log.info('getmininginfo') | ||||
mining_info = node.getmininginfo() | mining_info = node.getmininginfo() | ||||
assert_equal(mining_info['blocks'], 200) | assert_equal(mining_info['blocks'], 200) | ||||
assert_equal(mining_info['chain'], 'regtest') | assert_equal(mining_info['chain'], 'regtest') | ||||
assert_equal(mining_info['currentblocksize'], 0) | assert_equal(mining_info['currentblocksize'], 0) | ||||
assert_equal(mining_info['currentblocktx'], 0) | assert_equal(mining_info['currentblocktx'], 0) | ||||
assert_equal(mining_info['difficulty'], | assert_equal(mining_info['difficulty'], | ||||
Decimal('4.656542373906925E-10')) | Decimal('4.656542373906925E-10')) | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.log.info("getblocktemplate: Test truncated final transaction") | self.log.info("getblocktemplate: Test truncated final transaction") | ||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, | assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, | ||||
{'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) | {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) | ||||
self.log.info("getblocktemplate: Test duplicate transaction") | self.log.info("getblocktemplate: Test duplicate transaction") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_block.vtx.append(bad_block.vtx[0]) | bad_block.vtx.append(bad_block.vtx[0]) | ||||
assert_template(node, bad_block, 'bad-txns-duplicate') | 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") | self.log.info("getblocktemplate: Test invalid transaction") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_tx = copy.deepcopy(bad_block.vtx[0]) | bad_tx = copy.deepcopy(bad_block.vtx[0]) | ||||
bad_tx.vin[0].prevout.hash = 255 | bad_tx.vin[0].prevout.hash = 255 | ||||
bad_tx.rehash() | bad_tx.rehash() | ||||
bad_block.vtx.append(bad_tx) | bad_block.vtx.append(bad_tx) | ||||
assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') | assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') | ||||
assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') | |||||
self.log.info("getblocktemplate: Test nonfinal transaction") | self.log.info("getblocktemplate: Test nonfinal transaction") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_block.vtx[0].nLockTime = 2 ** 32 - 1 | bad_block.vtx[0].nLockTime = 2 ** 32 - 1 | ||||
bad_block.vtx[0].rehash() | bad_block.vtx[0].rehash() | ||||
assert_template(node, bad_block, 'bad-txns-nonfinal') | assert_template(node, bad_block, 'bad-txns-nonfinal') | ||||
assert_submitblock(bad_block, 'bad-txns-nonfinal') | |||||
self.log.info("getblocktemplate: Test bad tx count") | self.log.info("getblocktemplate: Test bad tx count") | ||||
# The tx count is immediately after the block header | # The tx count is immediately after the block header | ||||
TX_COUNT_OFFSET = 80 | TX_COUNT_OFFSET = 80 | ||||
bad_block_sn = bytearray(block.serialize()) | bad_block_sn = bytearray(block.serialize()) | ||||
assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) | assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) | ||||
bad_block_sn[TX_COUNT_OFFSET] += 1 | bad_block_sn[TX_COUNT_OFFSET] += 1 | ||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, | assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, | ||||
{'data': b2x(bad_block_sn), 'mode': 'proposal'}) | {'data': b2x(bad_block_sn), 'mode': 'proposal'}) | ||||
self.log.info("getblocktemplate: Test bad bits") | self.log.info("getblocktemplate: Test bad bits") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_block.nBits = 469762303 # impossible in the real world | bad_block.nBits = 469762303 # impossible in the real world | ||||
assert_template(node, bad_block, 'bad-diffbits') | assert_template(node, bad_block, 'bad-diffbits') | ||||
self.log.info("getblocktemplate: Test bad merkle root") | self.log.info("getblocktemplate: Test bad merkle root") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_block.hashMerkleRoot += 1 | bad_block.hashMerkleRoot += 1 | ||||
assert_template(node, bad_block, 'bad-txnmrklroot', False) | assert_template(node, bad_block, 'bad-txnmrklroot', False) | ||||
assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') | |||||
self.log.info("getblocktemplate: Test bad timestamps") | self.log.info("getblocktemplate: Test bad timestamps") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_block.nTime = 2 ** 31 - 1 | bad_block.nTime = 2 ** 31 - 1 | ||||
assert_template(node, bad_block, 'time-too-new') | assert_template(node, bad_block, 'time-too-new') | ||||
assert_submitblock(bad_block, 'time-too-new', 'time-too-new') | |||||
bad_block.nTime = 0 | bad_block.nTime = 0 | ||||
assert_template(node, bad_block, 'time-too-old') | 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") | self.log.info("getblocktemplate: Test not best block") | ||||
bad_block = copy.deepcopy(block) | bad_block = copy.deepcopy(block) | ||||
bad_block.hashPrevBlock = 123 | bad_block.hashPrevBlock = 123 | ||||
assert_template(node, bad_block, 'inconclusive-not-best-prevblk') | 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') | self.log.info('submitheader tests') | ||||
assert_raises_rpc_error(-22, 'Block header decode failed', | assert_raises_rpc_error(-22, 'Block header decode failed', | ||||
lambda: node.submitheader(hexdata='xx' * 80)) | lambda: node.submitheader(hexdata='xx' * 80)) | ||||
assert_raises_rpc_error(-22, 'Block header decode failed', | assert_raises_rpc_error(-22, 'Block header decode failed', | ||||
lambda: node.submitheader(hexdata='ff' * 78)) | lambda: node.submitheader(hexdata='ff' * 78)) | ||||
assert_raises_rpc_error(-25, 'Must submit previous header', | assert_raises_rpc_error(-25, 'Must submit previous header', | ||||
lambda: node.submitheader(hexdata='ff' * 80)) | lambda: node.submitheader(hexdata='ff' * 80)) | ||||
block.nTime += 1 | |||||
block.solve() | block.solve() | ||||
def chain_tip(b_hash, *, status='headers-only', branchlen=1): | def chain_tip(b_hash, *, status='headers-only', branchlen=1): | ||||
return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status} | return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status} | ||||
assert chain_tip(block.hash) not in node.getchaintips() | assert chain_tip(block.hash) not in node.getchaintips() | ||||
node.submitheader(hexdata=b2x(block.serialize())) | node.submitheader(hexdata=b2x(block.serialize())) | ||||
assert chain_tip(block.hash) in node.getchaintips() | assert chain_tip(block.hash) in node.getchaintips() | ||||
# Noop | # Noop | ||||
node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) | node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) | ||||
assert chain_tip(block.hash) in node.getchaintips() | assert chain_tip(block.hash) in node.getchaintips() | ||||
bad_block_root = copy.deepcopy(block) | bad_block_root = copy.deepcopy(block) | ||||
bad_block_root.hashMerkleRoot += 2 | bad_block_root.hashMerkleRoot += 2 | ||||
bad_block_root.solve() | bad_block_root.solve() | ||||
assert chain_tip(bad_block_root.hash) not in node.getchaintips() | assert chain_tip(bad_block_root.hash) not in node.getchaintips() | ||||
node.submitheader(hexdata=b2x( | node.submitheader(hexdata=b2x( | ||||
CBlockHeader(bad_block_root).serialize())) | CBlockHeader(bad_block_root).serialize())) | ||||
assert chain_tip(bad_block_root.hash) in node.getchaintips() | assert chain_tip(bad_block_root.hash) in node.getchaintips() | ||||
# Should still reject invalid blocks, even if we have the header: | # Should still reject invalid blocks, even if we have the header: | ||||
assert_equal(node.submitblock(hexdata=b2x( | assert_equal(node.submitblock(hexdata=b2x( | ||||
bad_block_root.serialize())), 'invalid') | bad_block_root.serialize())), 'bad-txnmrklroot') | ||||
assert_equal(node.submitblock(hexdata=b2x( | |||||
bad_block_root.serialize())), 'bad-txnmrklroot') | |||||
assert chain_tip(bad_block_root.hash) in node.getchaintips() | assert chain_tip(bad_block_root.hash) in node.getchaintips() | ||||
# We know the header for this invalid block, so should just return early without error: | # We know the header for this invalid block, so should just return early without error: | ||||
node.submitheader(hexdata=b2x( | node.submitheader(hexdata=b2x( | ||||
CBlockHeader(bad_block_root).serialize())) | CBlockHeader(bad_block_root).serialize())) | ||||
assert chain_tip(bad_block_root.hash) in node.getchaintips() | assert chain_tip(bad_block_root.hash) in node.getchaintips() | ||||
bad_block_lock = copy.deepcopy(block) | bad_block_lock = copy.deepcopy(block) | ||||
bad_block_lock.vtx[0].nLockTime = 2**32 - 1 | bad_block_lock.vtx[0].nLockTime = 2**32 - 1 | ||||
bad_block_lock.vtx[0].rehash() | bad_block_lock.vtx[0].rehash() | ||||
bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() | bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() | ||||
bad_block_lock.solve() | bad_block_lock.solve() | ||||
assert_equal(node.submitblock(hexdata=b2x( | assert_equal(node.submitblock(hexdata=b2x( | ||||
bad_block_lock.serialize())), 'invalid') | bad_block_lock.serialize())), 'bad-txns-nonfinal') | ||||
assert_equal(node.submitblock(hexdata=b2x( | |||||
bad_block_lock.serialize())), 'duplicate-invalid') | |||||
# Build a "good" block on top of the submitted bad block | # Build a "good" block on top of the submitted bad block | ||||
bad_block2 = copy.deepcopy(block) | bad_block2 = copy.deepcopy(block) | ||||
bad_block2.hashPrevBlock = bad_block_lock.sha256 | bad_block2.hashPrevBlock = bad_block_lock.sha256 | ||||
bad_block2.solve() | bad_block2.solve() | ||||
assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader( | assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader( | ||||
hexdata=b2x(CBlockHeader(bad_block2).serialize()))) | hexdata=b2x(CBlockHeader(bad_block2).serialize()))) | ||||
# Should reject invalid header right away | # Should reject invalid header right away | ||||
Show All 16 Lines | def run_test(self): | ||||
node.generate(10) | node.generate(10) | ||||
assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader( | assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader( | ||||
hexdata=b2x(CBlockHeader(bad_block_time).serialize()))) | hexdata=b2x(CBlockHeader(bad_block_time).serialize()))) | ||||
assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader( | assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader( | ||||
hexdata=b2x(CBlockHeader(bad_block2).serialize()))) | hexdata=b2x(CBlockHeader(bad_block2).serialize()))) | ||||
node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) | node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) | ||||
node.submitheader(hexdata=b2x( | node.submitheader(hexdata=b2x( | ||||
CBlockHeader(bad_block_root).serialize())) | CBlockHeader(bad_block_root).serialize())) | ||||
# valid | |||||
assert_equal(node.submitblock(hexdata=b2x( | |||||
block.serialize())), 'duplicate') | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
MiningTest().main() | MiningTest().main() |