Skip to content

Commit 15e8f6e

Browse files
committed
blockheaders include block height, nodes validate it
1 parent 48e3e95 commit 15e8f6e

31 files changed

+208
-162
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
@@ -432,6 +432,10 @@ class CCustomParams : public CRegTestParams {
432432
// No subsidy for custom chains by default
433433
consensus.genesis_subsidy = args.GetArg("-con_blocksubsidy", 0);
434434

435+
// Note: This global is needed to avoid circular dependency
436+
// Defaults to true for custom chains.
437+
g_con_blockheightinheader = gArgs.GetBoolArg("-con_blockheightinheader", true);
438+
435439
// All non-zero coinbase outputs must go to this scriptPubKey
436440
std::vector<unsigned char> man_bytes = ParseHex(gArgs.GetArg("-con_mandatorycoinbase", ""));
437441
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("-seednode=<ip>", "Use specified node as seed node. This option can be specified multiple times to connect to multiple nodes. (custom only)", true, OptionsCategory::CHAINPARAMS);
2727
gArgs.AddArg("-con_blocksubsidy", "Defines the amount of block subsidy to start with, at genesis block.", false, OptionsCategory::CHAINPARAMS);
2828
gArgs.AddArg("-con_connect_coinbase", "Connect outputs in genesis block to utxo database.", false, OptionsCategory::CHAINPARAMS);
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.", 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

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#include <primitives/transaction.h>
1010
#include <serialize.h>
1111
#include <uint256.h>
12+
#include <util.h>
13+
14+
15+
extern bool g_con_blockheightinheader;
1216

1317
/** Nodes collect new transactions into a block, hash them into a hash tree,
1418
* and scan through nonce values to make the block's hash satisfy proof-of-work
@@ -25,6 +29,9 @@ class CBlockHeader
2529
uint256 hashPrevBlock;
2630
uint256 hashMerkleRoot;
2731
uint32_t nTime;
32+
// Height in header as well as in coinbase for easier hsm validation
33+
// Is set for serialization with `-con_blockheightinheader=1`
34+
uint32_t block_height;
2835
uint32_t nBits;
2936
uint32_t nNonce;
3037

@@ -41,6 +48,9 @@ class CBlockHeader
4148
READWRITE(hashPrevBlock);
4249
READWRITE(hashMerkleRoot);
4350
READWRITE(nTime);
51+
if (g_con_blockheightinheader) {
52+
READWRITE(block_height);
53+
}
4454
READWRITE(nBits);
4555
READWRITE(nNonce);
4656
}
@@ -51,6 +61,7 @@ class CBlockHeader
5161
hashPrevBlock.SetNull();
5262
hashMerkleRoot.SetNull();
5363
nTime = 0;
64+
block_height = 0;
5465
nBits = 0;
5566
nNonce = 0;
5667
}
@@ -111,6 +122,7 @@ class CBlock : public CBlockHeader
111122
block.hashPrevBlock = hashPrevBlock;
112123
block.hashMerkleRoot = hashMerkleRoot;
113124
block.nTime = nTime;
125+
block.block_height = block_height;
114126
block.nBits = nBits;
115127
block.nNonce = nNonce;
116128
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
@@ -307,6 +307,7 @@ def initialize_datadir(dirname, n, chain):
307307
f.write("printtoconsole=0\n")
308308
f.write("con_blocksubsidy=5000000000\n")
309309
f.write("con_connect_coinbase=0\n")
310+
f.write("con_blockheightinheader=0\n")
310311
os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
311312
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
312313
return datadir

0 commit comments

Comments
 (0)