mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
mining: only pad with OP_0 at heights <= 16
Drop the include_dummy_extranonce branch from the OP_0 padding
condition in CreateNewBlock(), so that the dummy extraNonce is
only appended when consensus actually requires it (heights <= 16,
where the BIP34 height push alone would yield a 1-byte scriptSig
and trigger bad-cb-length).
The include_dummy_extranonce option struct field is now unused by
the miner and is removed in the next commit. Callers still set it,
so that this commit compiles.
Regenerate the hardcoded coinbase / block hashes throughout the
unit and functional test suites and update the regtest assumeutxo
snapshot in chainparams.
Additional side-effects:
- Without the dummy extranonce, coinbase scriptSigs are 1 byte
shorter at heights > 16, making every block 1 byte smaller.
This shifts where block files wrap and therefore where pruning
boundaries land.
- feature_assumeutxo malleation cases:
- case 1: error message changes due to UTXO reordering, similar
to 8f2078af6a
- case 4: the corruption byte is swapped from \x82 to \x83
because \x82 happened to be the actual value at that
offset in the new snapshot.
This commit is contained in:
@@ -607,9 +607,9 @@ public:
|
||||
m_assumeutxo_data = {
|
||||
{ // For use by unit tests
|
||||
.height = 110,
|
||||
.hash_serialized = AssumeutxoHash{uint256{"b952555c8ab81fec46f3d4253b7af256d766ceb39fb7752b9d18cdf4a0141327"}},
|
||||
.hash_serialized = AssumeutxoHash{uint256{"86e9a1205b418b16dde3a18a78c730e30137e28466bda5dbf6b33ab8fc05447c"}},
|
||||
.m_chain_tx_count = 111,
|
||||
.blockhash = uint256{"6affe030b7965ab538f820a56ef56c8149b7dc1d1c144af57113be080db7c397"},
|
||||
.blockhash = uint256{"135eec25a6fb277884e5824e7aa7d052c4868161c99a5122170b5266f86c273d"},
|
||||
},
|
||||
{
|
||||
// For use by fuzz target src/test/fuzz/utxo_snapshot.cpp
|
||||
@@ -621,9 +621,9 @@ public:
|
||||
{
|
||||
// For use by test/functional/feature_assumeutxo.py and test/functional/tool_bitcoin_chainstate.py
|
||||
.height = 299,
|
||||
.hash_serialized = AssumeutxoHash{uint256{"d2b051ff5e8eef46520350776f4100dd710a63447a8e01d917e92e79751a63e2"}},
|
||||
.hash_serialized = AssumeutxoHash{uint256{"106b2c56233e378a824cf0d5ff2be42ed32c72f1605c9be288d00942908a40ac"}},
|
||||
.m_chain_tx_count = 334,
|
||||
.blockhash = uint256{"7cc695046fec709f8c9394b6f928f81e81fd3ac20977bb68760fa1faa7916ea2"},
|
||||
.blockhash = uint256{"0c552ced4721c249a389eb9b08cb8da261cd46f0e7b5f9d064d48f3113406853"},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -189,12 +189,11 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
|
||||
// and in a typical setup a pool name or realistic extraNonce already makes
|
||||
// the scriptSig long enough.
|
||||
coinbase_tx.script_sig_prefix = coinbaseTx.vin[0].scriptSig;
|
||||
if (nHeight <= 16 || m_options.include_dummy_extranonce) {
|
||||
if (nHeight <= 16) {
|
||||
// For blocks at heights <= 16, the BIP34-encoded height alone is only
|
||||
// one byte. Consensus requires coinbase scriptSigs to be at least two
|
||||
// bytes long (bad-cb-length), so an OP_0 is always appended at those
|
||||
// heights. At greater heights it is only added when the caller
|
||||
// requests it (e.g. RPC and test code that rely on stable hashes).
|
||||
// heights.
|
||||
coinbaseTx.vin[0].scriptSig << OP_0;
|
||||
}
|
||||
Assert(nHeight > 0);
|
||||
|
||||
@@ -75,6 +75,8 @@ struct BlockCreateOptions {
|
||||
/**
|
||||
* Whether to include an OP_0 as a dummy extraNonce in the template's coinbase
|
||||
*
|
||||
* This option is ignored and no longer has any effect.
|
||||
*
|
||||
* TODO: this can be dropped after regenerating hardcoded block and transaction
|
||||
* hashes in the test suite.
|
||||
*/
|
||||
|
||||
@@ -107,8 +107,12 @@ FUZZ_TARGET(utxo_total_supply)
|
||||
// Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high.
|
||||
// Up to 300 seems reasonable.
|
||||
int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 300);
|
||||
// Always pad with OP_0 at the end to avoid bad-cb-length error
|
||||
const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0;
|
||||
// Avoid bad-cb-length error at heights <= 16. Pad the BIP34-encoded height
|
||||
// with OP_0 to satisfy the minimum 2-byte coinbase scriptSig length.
|
||||
CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height;
|
||||
if (duplicate_coinbase_height <= 16) {
|
||||
duplicate_coinbase_script << OP_0;
|
||||
}
|
||||
// Mine the first block with this duplicate
|
||||
current_block = PrepareNextBlock();
|
||||
StoreLastTxo();
|
||||
|
||||
@@ -390,7 +390,7 @@ TestChain100Setup::TestChain100Setup(
|
||||
LOCK(::cs_main);
|
||||
assert(
|
||||
m_node.chainman->ActiveChain().Tip()->GetBlockHash().ToString() ==
|
||||
"0c8c5f79505775a0f6aed6aca2350718ceb9c6f2c878667864d5c7a6d8ffa2a6");
|
||||
"0ee6e270d6594249e548110619f7bd690695beb219b915da4a2e84e2b61ed60f");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,11 +142,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
|
||||
}
|
||||
|
||||
const auto out110 = *params->AssumeutxoForHeight(110);
|
||||
BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "b952555c8ab81fec46f3d4253b7af256d766ceb39fb7752b9d18cdf4a0141327");
|
||||
BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "86e9a1205b418b16dde3a18a78c730e30137e28466bda5dbf6b33ab8fc05447c");
|
||||
BOOST_CHECK_EQUAL(out110.m_chain_tx_count, 111U);
|
||||
|
||||
const auto out110_2 = *params->AssumeutxoForBlockhash(uint256{"6affe030b7965ab538f820a56ef56c8149b7dc1d1c144af57113be080db7c397"});
|
||||
BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "b952555c8ab81fec46f3d4253b7af256d766ceb39fb7752b9d18cdf4a0141327");
|
||||
const auto out110_2 = *params->AssumeutxoForBlockhash(uint256{"135eec25a6fb277884e5824e7aa7d052c4868161c99a5122170b5266f86c273d"});
|
||||
BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "86e9a1205b418b16dde3a18a78c730e30137e28466bda5dbf6b33ab8fc05447c");
|
||||
BOOST_CHECK_EQUAL(out110_2.m_chain_tx_count, 111U);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,12 +139,12 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
self.log.info(" - snapshot file with alternated but parsable UTXO data results in different hash")
|
||||
cases = [
|
||||
# (content, offset, wrong_hash, custom_message)
|
||||
[b"\xff" * 32, 0, "77874d48d932a5cb7a7f770696f5224ff05746fdcf732a58270b45da0f665934", None], # wrong outpoint hash
|
||||
[(2).to_bytes(1, "little"), 32, None, "Bad snapshot format or truncated snapshot after deserializing 1 coins."], # wrong txid coins count
|
||||
[b"\xff" * 32, 0, "43bb0fcc50f3789d50ce41891810bbc571d549a32e2bbaabfc036f73ae2a23f9", None], # wrong outpoint hash
|
||||
[(2).to_bytes(1, "little"), 32, None, "Bad snapshot data after deserializing 2 coins."], # wrong txid coins count
|
||||
[b"\xfd\xff\xff", 32, None, "Mismatch in coins count in snapshot metadata and actual snapshot data"], # txid coins count exceeds coins left
|
||||
[b"\x01", 33, "9f562925721e4f97e6fde5b590dbfede51e2204a68639525062ad064545dd0ea", None], # wrong outpoint index
|
||||
[b"\x82", 34, "161393f07f8ad71760b3910a914f677f2cb166e5bcf5354e50d46b78c0422d15", None], # wrong coin code VARINT
|
||||
[b"\x80", 34, "e6fae191ef851554467b68acff01ca09ad0a2e48c9b3dfea46cf7d35a7fd0ad0", None], # another wrong coin code
|
||||
[b"\x01", 33, "0b0e96fd9a556157a68d9e6fe6e2c7562169c730347565ce0725032f824bea34", None], # wrong outpoint index
|
||||
[b"\x83", 34, "52084c732f760fbcfb804d854815a9145111e93d7c7f3f7bfcb0ade1ea4f6b99", None], # wrong coin code VARINT
|
||||
[b"\x80", 34, "c1a1308fcf04cbb14fc777467eb5e98e77a921b66eac4ad47877d0b6ed7b15b1", None], # another wrong coin code
|
||||
[b"\x84\x58", 34, None, "Bad snapshot data after deserializing 0 coins"], # wrong coin case with height 364 and coinbase 0
|
||||
[
|
||||
# compressed txout value + scriptpubkey
|
||||
@@ -163,12 +163,12 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
f.write(content)
|
||||
f.write(valid_snapshot_contents[(5 + 2 + 4 + 32 + 8 + offset + len(content)):])
|
||||
|
||||
msg = custom_message if custom_message is not None else f"Bad snapshot content hash: expected d2b051ff5e8eef46520350776f4100dd710a63447a8e01d917e92e79751a63e2, got {wrong_hash}."
|
||||
msg = custom_message if custom_message is not None else f"Bad snapshot content hash: expected 106b2c56233e378a824cf0d5ff2be42ed32c72f1605c9be288d00942908a40ac, got {wrong_hash}."
|
||||
expected_error(msg)
|
||||
|
||||
def test_headers_not_synced(self, valid_snapshot_path):
|
||||
for node in self.nodes[1:]:
|
||||
msg = "Unable to load UTXO snapshot: The base block header (7cc695046fec709f8c9394b6f928f81e81fd3ac20977bb68760fa1faa7916ea2) must appear in the headers chain. Make sure all headers are syncing, and call loadtxoutset again."
|
||||
msg = "Unable to load UTXO snapshot: The base block header (0c552ced4721c249a389eb9b08cb8da261cd46f0e7b5f9d064d48f3113406853) must appear in the headers chain. Make sure all headers are syncing, and call loadtxoutset again."
|
||||
assert_raises_rpc_error(-32603, msg, node.loadtxoutset, valid_snapshot_path)
|
||||
|
||||
def test_invalid_chainstate_scenarios(self):
|
||||
@@ -228,7 +228,7 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
block_hash = node.getblockhash(height)
|
||||
node.invalidateblock(block_hash)
|
||||
assert_equal(node.getblockcount(), height - 1)
|
||||
msg = "Unable to load UTXO snapshot: The base block header (7cc695046fec709f8c9394b6f928f81e81fd3ac20977bb68760fa1faa7916ea2) is part of an invalid chain."
|
||||
msg = "Unable to load UTXO snapshot: The base block header (0c552ced4721c249a389eb9b08cb8da261cd46f0e7b5f9d064d48f3113406853) is part of an invalid chain."
|
||||
assert_raises_rpc_error(-32603, msg, node.loadtxoutset, dump_output_path)
|
||||
node.reconsiderblock(block_hash)
|
||||
|
||||
@@ -469,7 +469,7 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
def check_dump_output(output):
|
||||
assert_equal(
|
||||
output['txoutset_hash'],
|
||||
"d2b051ff5e8eef46520350776f4100dd710a63447a8e01d917e92e79751a63e2")
|
||||
"106b2c56233e378a824cf0d5ff2be42ed32c72f1605c9be288d00942908a40ac")
|
||||
assert_equal(output["nchaintx"], blocks[SNAPSHOT_BASE_HEIGHT].chain_tx)
|
||||
|
||||
check_dump_output(dump_output)
|
||||
@@ -499,7 +499,7 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
dump_output4 = n0.dumptxoutset(path='utxos4.dat', rollback=prev_snap_height)
|
||||
assert_equal(
|
||||
dump_output4['txoutset_hash'],
|
||||
"45ac2777b6ca96588210e2a4f14b602b41ec37b8b9370673048cc0af434a1ec8")
|
||||
"147d1789e3e29a62e3e15b80078b50603afb5e1e70ea07001c94666afbde38df")
|
||||
assert_not_equal(sha256sum_file(dump_output['path']), sha256sum_file(dump_output4['path']))
|
||||
|
||||
# Use a hash instead of a height
|
||||
|
||||
@@ -100,7 +100,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
|
||||
pruneheight_new = node.pruneblockchain(400)
|
||||
# the prune heights used here and below are magic numbers that are determined by the
|
||||
# thresholds at which block files wrap, so they depend on disk serialization and default block file size.
|
||||
assert_equal(pruneheight_new, 248)
|
||||
assert_equal(pruneheight_new, 249)
|
||||
|
||||
self.log.info("check if we can access the tips blockfilter and coinstats when we have pruned some blocks")
|
||||
tip = self.nodes[0].getbestblockhash()
|
||||
@@ -117,8 +117,8 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
|
||||
assert node.gettxoutsetinfo(hash_type="muhash", hash_or_height=height_hash)['muhash']
|
||||
|
||||
# mine and sync index up to a height that will later be the pruneheight
|
||||
self.generate(self.nodes[0], 51)
|
||||
self.sync_index(height=751)
|
||||
self.generate(self.nodes[0], 54)
|
||||
self.sync_index(height=754)
|
||||
|
||||
self.restart_without_indices()
|
||||
|
||||
@@ -130,12 +130,12 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
|
||||
msg = "Querying specific block heights requires coinstatsindex"
|
||||
assert_raises_rpc_error(-8, msg, node.gettxoutsetinfo, "muhash", height_hash)
|
||||
|
||||
self.generate(self.nodes[0], 749)
|
||||
self.generate(self.nodes[0], 746)
|
||||
|
||||
self.log.info("prune exactly up to the indices best blocks while the indices are disabled")
|
||||
for i in range(3):
|
||||
pruneheight_2 = self.nodes[i].pruneblockchain(1000)
|
||||
assert_equal(pruneheight_2, 750)
|
||||
assert_equal(pruneheight_2, 753)
|
||||
# Restart the nodes again with the indices activated
|
||||
self.restart_node(i, extra_args=self.extra_args[i])
|
||||
|
||||
@@ -195,7 +195,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
|
||||
for node in self.nodes[:2]:
|
||||
with node.assert_debug_log(['limited pruning to height 2489']):
|
||||
pruneheight_new = node.pruneblockchain(2500)
|
||||
assert_equal(pruneheight_new, 2005)
|
||||
assert_equal(pruneheight_new, 2013)
|
||||
|
||||
self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario")
|
||||
with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']):
|
||||
|
||||
@@ -67,8 +67,8 @@ class UTXOSetHashTest(BitcoinTestFramework):
|
||||
assert_equal(finalized[::-1].hex(), node_muhash)
|
||||
|
||||
self.log.info("Test deterministic UTXO set hash results")
|
||||
assert_equal(node.gettxoutsetinfo()['hash_serialized_3'], "e0b4c80f2880985fdf1adc331ed0735ac207588f986c91c7c05e8cf5fe6780f0")
|
||||
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "8739b878f23030ef39a5547edc7b57f88d50fdaaf47314ff0524608deb13067e")
|
||||
assert_equal(node.gettxoutsetinfo()['hash_serialized_3'], "396058cfd7f3b4c05dcdfaf360be9986d859d2c8315082d3aadda6d2d16add25")
|
||||
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "97f341d4226cb4451dd9da2856759fb97659cfc238a0b8d5452e64af6032bd3d")
|
||||
|
||||
def run_test(self):
|
||||
self.test_muhash_implementation()
|
||||
|
||||
@@ -68,15 +68,15 @@ class DumptxoutsetTest(BitcoinTestFramework):
|
||||
# Blockhash should be deterministic based on mocked time.
|
||||
assert_equal(
|
||||
out['base_hash'],
|
||||
'6885775faa46290bedfa071f22d0598c93f1d7e01f24607c4dedd69b9baa4a8f')
|
||||
'220aee93f0f5409631f35488898258f0930952bd620063cb4d7d87f7c28a8f50')
|
||||
|
||||
# UTXO snapshot hash should be deterministic based on mocked time.
|
||||
assert_equal(
|
||||
sha256sum_file(str(expected_path)).hex(),
|
||||
'd9506d541437f5e2892d6b6ea173f55233de11601650c157a27d8f2b9d08cb6f')
|
||||
'e8c59b1bc1f19061c67eb7a392f4ea17eea83af58646ea2909e270546699c36c')
|
||||
|
||||
assert_equal(
|
||||
out['txoutset_hash'], 'd4453995f4f20db7bb3a604afd10d7128e8ee11159cde56d5b2fd7f55be7c74c')
|
||||
out['txoutset_hash'], '771d773b5c27b6f35f598ce764652a2cf28fbc268341eb1827844e416c629c7d')
|
||||
assert_equal(out['nchaintx'], 101)
|
||||
|
||||
# Specifying a path to an existing or invalid file will fail.
|
||||
|
||||
@@ -128,7 +128,7 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
|
||||
self.generate(self.nodes[0], 400, sync_fun=self.no_op)
|
||||
self.sync_blocks([self.nodes[0], pruned_node])
|
||||
pruneheight = pruned_node.pruneblockchain(300)
|
||||
assert_equal(pruneheight, 248)
|
||||
assert_equal(pruneheight, 249)
|
||||
# Ensure the block is actually pruned
|
||||
pruned_block = self.nodes[0].getblockhash(2)
|
||||
assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)
|
||||
@@ -144,14 +144,14 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
|
||||
self.log.info("Fetched block persists after next pruning event")
|
||||
self.generate(self.nodes[0], 250, sync_fun=self.no_op)
|
||||
self.sync_blocks([self.nodes[0], pruned_node])
|
||||
pruneheight += 251
|
||||
pruneheight += 252
|
||||
assert_equal(pruned_node.pruneblockchain(700), pruneheight)
|
||||
assert_equal(pruned_node.getblock(pruned_block)["hash"], "196ee3a1a6db2353965081c48ef8e6b031cb2115d084bec6fec937e91a2c6277")
|
||||
|
||||
self.log.info("Fetched block can be pruned again when prune height exceeds the height of the tip at the time when the block was fetched")
|
||||
self.generate(self.nodes[0], 250, sync_fun=self.no_op)
|
||||
self.sync_blocks([self.nodes[0], pruned_node])
|
||||
pruneheight += 250
|
||||
pruneheight += 251
|
||||
assert_equal(pruned_node.pruneblockchain(1000), pruneheight)
|
||||
assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from test_framework.wallet import MiniWallet
|
||||
START_HEIGHT = 199
|
||||
# Hardcoded in regtest chainparams
|
||||
SNAPSHOT_BASE_BLOCK_HEIGHT = 299
|
||||
SNAPSHOT_BASE_BLOCK_HASH = "7cc695046fec709f8c9394b6f928f81e81fd3ac20977bb68760fa1faa7916ea2"
|
||||
SNAPSHOT_BASE_BLOCK_HASH = "0c552ced4721c249a389eb9b08cb8da261cd46f0e7b5f9d064d48f3113406853"
|
||||
|
||||
|
||||
class BitcoinChainstateTest(BitcoinTestFramework):
|
||||
|
||||
@@ -172,7 +172,7 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
|
||||
assert_equal(
|
||||
dump_output['txoutset_hash'],
|
||||
"d2b051ff5e8eef46520350776f4100dd710a63447a8e01d917e92e79751a63e2")
|
||||
"106b2c56233e378a824cf0d5ff2be42ed32c72f1605c9be288d00942908a40ac")
|
||||
assert_equal(dump_output["nchaintx"], 334)
|
||||
assert_equal(n0.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user