Skip to content

Commit 2bfabb3

Browse files
committed
blockheaders include block height, nodes validate it
The block height validation is gated by the startup option `con_blockheightinheader`, accesible in custom chains only using option `chain=<name>`. Only if set to true will this field be (de)serialized and evaluated in consensus logic. Otherwise the field is simply ignored, other than being stored in memory as the default value 0.
1 parent 8f6bedf commit 2bfabb3

23 files changed

+180
-129
lines changed

src/chain.h

+6
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ class CBlockIndex
210210
int32_t nVersion;
211211
uint256 hashMerkleRoot;
212212
uint32_t nTime;
213+
uint32_t block_height;
213214
uint32_t nBits;
214215
uint32_t nNonce;
215216

@@ -238,6 +239,7 @@ class CBlockIndex
238239
nVersion = 0;
239240
hashMerkleRoot = uint256();
240241
nTime = 0;
242+
block_height = 0;
241243
nBits = 0;
242244
nNonce = 0;
243245
}
@@ -254,6 +256,7 @@ class CBlockIndex
254256
nVersion = block.nVersion;
255257
hashMerkleRoot = block.hashMerkleRoot;
256258
nTime = block.nTime;
259+
block_height = block.block_height;
257260
nBits = block.nBits;
258261
nNonce = block.nNonce;
259262
}
@@ -284,6 +287,7 @@ class CBlockIndex
284287
block.hashPrevBlock = pprev->GetBlockHash();
285288
block.hashMerkleRoot = hashMerkleRoot;
286289
block.nTime = nTime;
290+
block.block_height = block_height;
287291
block.nBits = nBits;
288292
block.nNonce = nNonce;
289293
return block;
@@ -403,6 +407,7 @@ class CDiskBlockIndex : public CBlockIndex
403407
READWRITE(hashPrev);
404408
READWRITE(hashMerkleRoot);
405409
READWRITE(nTime);
410+
READWRITE(block_height);
406411
READWRITE(nBits);
407412
READWRITE(nNonce);
408413
}
@@ -414,6 +419,7 @@ class CDiskBlockIndex : public CBlockIndex
414419
block.hashPrevBlock = hashPrev;
415420
block.hashMerkleRoot = hashMerkleRoot;
416421
block.nTime = nTime;
422+
block.block_height = block_height;
417423
block.nBits = nBits;
418424
block.nNonce = nNonce;
419425
return block.GetHash();

src/chainparams.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ class CCustomParams : public CRegTestParams {
435435
// No subsidy for custom chains by default
436436
consensus.genesis_subsidy = args.GetArg("-con_blocksubsidy", 0);
437437

438+
// Note: This global is needed to avoid circular dependency
439+
// Defaults to true for custom chains.
440+
g_con_blockheightinheader = gArgs.GetBoolArg("-con_blockheightinheader", true);
441+
438442
// All non-zero coinbase outputs must go to this scriptPubKey
439443
std::vector<unsigned char> man_bytes = ParseHex(gArgs.GetArg("-con_mandatorycoinbase", ""));
440444
consensus.mandatory_coinbase_destination = CScript(man_bytes.begin(), man_bytes.end()); // Blank script allows any coinbase destination

src/chainparamsbase.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ void SetupChainParamsBaseOptions()
2626
gArgs.AddArg("-con_mandatorycoinbase", "All non-zero valued coinbase outputs must go to this scriptPubKey, if set.", false, OptionsCategory::ELEMENTS);
2727
gArgs.AddArg("-con_blocksubsidy", "Defines the amount of block subsidy to start with, at genesis block.", false, OptionsCategory::ELEMENTS);
2828
gArgs.AddArg("-con_connect_coinbase", "Connect outputs in genesis block to utxo database.", false, OptionsCategory::ELEMENTS);
29+
gArgs.AddArg("-con_blockheightinheader", "Whether the chain includes the block height directly in the header, for easier validation of block height in low-resource environments. (default: true)", false, OptionsCategory::CHAINPARAMS);
2930
}
3031

3132
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;

src/consensus/params.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ struct Params {
7878
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
7979
uint256 nMinimumChainWork;
8080
uint256 defaultAssumeValid;
81-
8281
// Elements-specific chainparams
8382
CScript mandatory_coinbase_destination;
8483
CAmount genesis_subsidy;
8584
bool connect_genesis_outputs;
85+
// g_con_blockheightinheader global hack instead of proper arg due to circular dep
8686
};
8787
} // namespace Consensus
8888

src/miner.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
168168
// Fill in header
169169
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
170170
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
171+
if (g_con_blockheightinheader) {
172+
pblock->block_height = nHeight;
173+
}
171174
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
172175
pblock->nNonce = 0;
173176
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);

src/primitives/block.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <utilstrencodings.h>
1111
#include <crypto/common.h>
1212

13+
bool g_con_blockheightinheader = false;
14+
1315
uint256 CBlockHeader::GetHash() const
1416
{
1517
return SerializeHash(*this);

src/primitives/block.h

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <serialize.h>
1111
#include <uint256.h>
1212

13+
extern bool g_con_blockheightinheader;
14+
1315
/** Nodes collect new transactions into a block, hash them into a hash tree,
1416
* and scan through nonce values to make the block's hash satisfy proof-of-work
1517
* requirements. When they solve the proof-of-work, they broadcast the block
@@ -25,6 +27,9 @@ class CBlockHeader
2527
uint256 hashPrevBlock;
2628
uint256 hashMerkleRoot;
2729
uint32_t nTime;
30+
// Height in header as well as in coinbase for easier hsm validation
31+
// Is set for serialization with `-con_blockheightinheader=1`
32+
uint32_t block_height;
2833
uint32_t nBits;
2934
uint32_t nNonce;
3035

@@ -41,6 +46,9 @@ class CBlockHeader
4146
READWRITE(hashPrevBlock);
4247
READWRITE(hashMerkleRoot);
4348
READWRITE(nTime);
49+
if (g_con_blockheightinheader) {
50+
READWRITE(block_height);
51+
}
4452
READWRITE(nBits);
4553
READWRITE(nNonce);
4654
}
@@ -51,6 +59,7 @@ class CBlockHeader
5159
hashPrevBlock.SetNull();
5260
hashMerkleRoot.SetNull();
5361
nTime = 0;
62+
block_height = 0;
5463
nBits = 0;
5564
nNonce = 0;
5665
}
@@ -111,6 +120,7 @@ class CBlock : public CBlockHeader
111120
block.hashPrevBlock = hashPrevBlock;
112121
block.hashMerkleRoot = hashMerkleRoot;
113122
block.nTime = nTime;
123+
block.block_height = block_height;
114124
block.nBits = nBits;
115125
block.nNonce = nNonce;
116126
return block;

src/txdb.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
263263
CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash());
264264
pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
265265
pindexNew->nHeight = diskindex.nHeight;
266+
pindexNew->block_height = diskindex.block_height;
266267
pindexNew->nFile = diskindex.nFile;
267268
pindexNew->nDataPos = diskindex.nDataPos;
268269
pindexNew->nUndoPos = diskindex.nUndoPos;

src/validation.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -3271,6 +3271,11 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
32713271
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
32723272
return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early");
32733273

3274+
// Check height in header against prev
3275+
if (g_con_blockheightinheader && (uint32_t)nHeight != block.block_height)
3276+
return state.Invalid(error("%s: block height in header is incorrect", __func__),
3277+
REJECT_INVALID, "bad-header-height");
3278+
32743279
// Check timestamp
32753280
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
32763281
return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");

test/bitcoin_functional/functional/test_framework/util.py

+1
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ def initialize_datadir(dirname, n, chain):
308308
f.write("con_blocksubsidy=5000000000\n")
309309
f.write("con_connect_coinbase=0\n")
310310
f.write("anyonecanspendaremine=0\n")
311+
f.write("con_blockheightinheader=0\n")
311312
os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
312313
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
313314
return datadir

0 commit comments

Comments
 (0)