Skip to content

Commit 4656929

Browse files
author
jagdeep sidhu
committed
headonly mode
1 parent 4d7d5f6 commit 4656929

File tree

5 files changed

+131
-36
lines changed

5 files changed

+131
-36
lines changed

src/init.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
493493
#endif
494494
argsman.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
495495
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Disables automatic broadcast and rebroadcast of transactions, unless the source peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
496+
argsman.AddArg("-headersonly", strprintf("Sync and maintain headers only (disables block body download and implies -blocksonly=1). (default: %u)", DEFAULT_HEADERSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
496497
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
497498
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
498499
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
@@ -793,6 +794,12 @@ void InitParameterInteraction(ArgsManager& args)
793794
LogInfo("parameter interaction: -externalip set -> setting -discover=0\n");
794795
}
795796

797+
if (args.GetBoolArg("-headersonly", DEFAULT_HEADERSONLY)) {
798+
if (args.SoftSetBoolArg("-blocksonly", true)) {
799+
LogInfo("parameter interaction: -headersonly=1 -> setting -blocksonly=1\n");
800+
}
801+
}
802+
796803
if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
797804
// disable whitelistrelay in blocksonly mode
798805
if (args.SoftSetBoolArg("-whitelistrelay", false))
@@ -1302,7 +1309,8 @@ static ChainstateLoadResult InitAndLoadChainstate(
13021309
// dependency between validation and index/base, since the latter is not in
13031310
// libbitcoinkernel.
13041311
chainman.snapshot_download_completed = [&node]() {
1305-
if (!node.chainman->m_blockman.IsPruneMode()) {
1312+
const bool headers_only_mode = node.args != nullptr && node.args->GetBoolArg("-headersonly", DEFAULT_HEADERSONLY);
1313+
if (!headers_only_mode && !node.chainman->m_blockman.IsPruneMode()) {
13061314
LogInfo("[snapshot] re-enabling NODE_NETWORK services");
13071315
node.connman->AddLocalServices(NODE_NETWORK);
13081316
}
@@ -1508,6 +1516,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15081516

15091517
PeerManager::Options peerman_opts{};
15101518
ApplyArgsManOptions(args, peerman_opts);
1519+
const bool headers_only_mode{peerman_opts.headers_only};
1520+
if (headers_only_mode) {
1521+
g_local_services = ServiceFlags(g_local_services & ~(NODE_NETWORK | NODE_NETWORK_LIMITED | NODE_COMPACT_FILTERS));
1522+
LogInfo("Running with -headersonly=1: disabling block-serving service flags");
1523+
}
15111524

15121525
{
15131526

@@ -1837,6 +1850,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18371850
chainstate->PruneAndFlush();
18381851
}
18391852
}
1853+
} else if (headers_only_mode) {
1854+
LogInfo("Running node in headers-only mode; keeping NODE_NETWORK services disabled");
18401855
} else {
18411856
// Prior to setting NODE_NETWORK, check if we can provide historical blocks.
18421857
if (!WITH_LOCK(chainman.GetMutex(), return chainman.BackgroundSyncInProgress())) {

src/net_processing.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1842,6 +1842,7 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
18421842
std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBlockIndex& block_index)
18431843
{
18441844
if (m_chainman.m_blockman.LoadingBlocks()) return "Loading blocks ...";
1845+
if (m_opts.headers_only) return "Node is running in -headersonly mode";
18451846

18461847
// Ensure this peer exists and hasn't been disconnected
18471848
PeerRef peer = GetPeerRef(peer_id);
@@ -2706,6 +2707,8 @@ bool PeerManagerImpl::MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& loc
27062707
*/
27072708
void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex& last_header)
27082709
{
2710+
if (m_opts.headers_only) return;
2711+
27092712
LOCK(cs_main);
27102713
CNodeState *nodestate = State(pfrom.GetId());
27112714

@@ -4323,6 +4326,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43234326
return;
43244327
}
43254328

4329+
if (m_opts.headers_only) {
4330+
CBlockHeaderAndShortTxIDs cmpctblock;
4331+
vRecv >> cmpctblock;
4332+
return ProcessHeadersMessage(pfrom, *peer, std::vector<CBlockHeader>{cmpctblock.header}, /*via_compact_block=*/true);
4333+
}
4334+
43264335
CBlockHeaderAndShortTxIDs cmpctblock;
43274336
vRecv >> cmpctblock;
43284337

@@ -4571,6 +4580,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
45714580
return;
45724581
}
45734582

4583+
if (m_opts.headers_only) {
4584+
return;
4585+
}
4586+
45744587
BlockTransactions resp;
45754588
vRecv >> resp;
45764589

@@ -4626,6 +4639,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
46264639
return;
46274640
}
46284641

4642+
if (m_opts.headers_only) {
4643+
CBlockHeader header;
4644+
vRecv >> header;
4645+
return ProcessHeadersMessage(pfrom, *peer, std::vector<CBlockHeader>{header}, /*via_compact_block=*/false);
4646+
}
4647+
46294648
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
46304649
vRecv >> TX_WITH_WITNESS(*pblock);
46314650

@@ -5891,7 +5910,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
58915910
// Message: getdata (blocks)
58925911
//
58935912
std::vector<CInv> vGetData;
5894-
if (CanServeBlocks(*peer) && ((sync_blocks_and_headers_from_peer && !IsLimitedPeer(*peer)) || !m_chainman.IsInitialBlockDownload()) && state.vBlocksInFlight.size() < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
5913+
if (!m_opts.headers_only && CanServeBlocks(*peer) && ((sync_blocks_and_headers_from_peer && !IsLimitedPeer(*peer)) || !m_chainman.IsInitialBlockDownload()) && state.vBlocksInFlight.size() < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
58955914
std::vector<const CBlockIndex*> vToDownload;
58965915
NodeId staller = -1;
58975916
auto get_inflight_budget = [&state]() {

src/net_processing.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class Warnings;
3636

3737
/** Whether transaction reconciliation protocol should be enabled by default. */
3838
static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false};
39+
/** Whether to run in headers-only mode (no block body download). */
40+
static constexpr bool DEFAULT_HEADERSONLY{false};
3941
/** Default number of non-mempool transactions to keep around for block reconstruction. Includes
4042
orphan, replaced, and rejected transactions. */
4143
static const uint32_t DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN{100};
@@ -87,6 +89,8 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
8789
//! Number of headers sent in one getheaders message result (this is
8890
//! a test-only option).
8991
uint32_t max_headers_result{MAX_HEADERS_RESULTS};
92+
//! Whether this node should avoid downloading block bodies.
93+
bool headers_only{DEFAULT_HEADERSONLY};
9094
};
9195

9296
static std::unique_ptr<PeerManager> make(CConnman& connman, AddrMan& addrman,

src/node/peerman_args.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void ApplyArgsManOptions(const ArgsManager& argsman, PeerManager::Options& optio
2323
if (auto value{argsman.GetBoolArg("-capturemessages")}) options.capture_messages = *value;
2424

2525
if (auto value{argsman.GetBoolArg("-blocksonly")}) options.ignore_incoming_txs = *value;
26+
if (auto value{argsman.GetBoolArg("-headersonly")}) options.headers_only = *value;
2627
}
2728

2829
} // namespace node

src/rpc/blockchain.cpp

Lines changed: 90 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <util/check.h>
4848
#include <util/fs.h>
4949
#include <util/strencodings.h>
50+
#include <util/time.h>
5051
#include <util/syserror.h>
5152
#include <util/translation.h>
5253
#include <validation.h>
@@ -120,6 +121,28 @@ static int ComputeNextBlockAndDepth(const CBlockIndex& tip, const CBlockIndex& b
120121
return &blockindex == &tip ? 1 : -1;
121122
}
122123

124+
static bool IsHeadersOnlyMode(const JSONRPCRequest& request)
125+
{
126+
const NodeContext& node = EnsureAnyNodeContext(request.context);
127+
return node.args != nullptr && node.args->GetBoolArg("-headersonly", DEFAULT_HEADERSONLY);
128+
}
129+
130+
static const CBlockIndex* GetRPCBestTip(const ChainstateManager& chainman, bool headers_only) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
131+
{
132+
if (headers_only && chainman.m_best_header != nullptr) {
133+
return chainman.m_best_header;
134+
}
135+
return chainman.ActiveChain().Tip();
136+
}
137+
138+
static bool HeaderTipIsInitialSync(const ChainstateManager& chainman, const CBlockIndex& header_tip)
139+
{
140+
const bool has_minimum_work{header_tip.nChainWork >= chainman.MinimumChainWork()};
141+
const auto header_time{NodeSeconds{std::chrono::seconds{header_tip.GetBlockTime()}}};
142+
const bool stale{header_time <= (NodeClock::now() - 24h)};
143+
return !has_minimum_work || stale;
144+
}
145+
123146
static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
124147
{
125148
LOCK(::cs_main);
@@ -620,10 +643,11 @@ static RPCHelpMan getblockheader()
620643
const CBlockIndex* pblockindex;
621644
const CBlockIndex* tip;
622645
ChainstateManager& chainman = EnsureAnyChainman(request.context);
646+
const bool headers_only{IsHeadersOnlyMode(request)};
623647
{
624648
LOCK(cs_main);
625649
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
626-
tip = chainman.ActiveChain().Tip();
650+
tip = GetRPCBestTip(chainman, headers_only);
627651
}
628652

629653
if (!pblockindex) {
@@ -1375,24 +1399,26 @@ RPCHelpMan getblockchaininfo()
13751399
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
13761400
{
13771401
ChainstateManager& chainman = EnsureAnyChainman(request.context);
1402+
const bool headers_only{IsHeadersOnlyMode(request)};
13781403
LOCK(cs_main);
13791404
Chainstate& active_chainstate = chainman.ActiveChainstate();
13801405

1381-
const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
1382-
const int height{tip.nHeight};
1406+
const CBlockIndex& active_tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
1407+
const CBlockIndex& rpc_tip{*CHECK_NONFATAL(GetRPCBestTip(chainman, headers_only))};
1408+
const int height{headers_only ? rpc_tip.nHeight : active_tip.nHeight};
13831409
UniValue obj(UniValue::VOBJ);
13841410
obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
13851411
obj.pushKV("blocks", height);
13861412
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
1387-
obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
1388-
obj.pushKV("bits", strprintf("%08x", tip.nBits));
1389-
obj.pushKV("target", GetTarget(tip, chainman.GetConsensus().powLimit).GetHex());
1390-
obj.pushKV("difficulty", GetDifficulty(tip));
1391-
obj.pushKV("time", tip.GetBlockTime());
1392-
obj.pushKV("mediantime", tip.GetMedianTimePast());
1393-
obj.pushKV("verificationprogress", chainman.GuessVerificationProgress(&tip));
1394-
obj.pushKV("initialblockdownload", chainman.IsInitialBlockDownload());
1395-
obj.pushKV("chainwork", tip.nChainWork.GetHex());
1413+
obj.pushKV("bestblockhash", rpc_tip.GetBlockHash().GetHex());
1414+
obj.pushKV("bits", strprintf("%08x", rpc_tip.nBits));
1415+
obj.pushKV("target", GetTarget(rpc_tip, chainman.GetConsensus().powLimit).GetHex());
1416+
obj.pushKV("difficulty", GetDifficulty(rpc_tip));
1417+
obj.pushKV("time", rpc_tip.GetBlockTime());
1418+
obj.pushKV("mediantime", rpc_tip.GetMedianTimePast());
1419+
obj.pushKV("verificationprogress", chainman.GuessVerificationProgress(&rpc_tip));
1420+
obj.pushKV("initialblockdownload", headers_only ? HeaderTipIsInitialSync(chainman, rpc_tip) : chainman.IsInitialBlockDownload());
1421+
obj.pushKV("chainwork", rpc_tip.nChainWork.GetHex());
13961422
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
13971423
obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
13981424
if (chainman.m_blockman.IsPruneMode()) {
@@ -1544,35 +1570,56 @@ static RPCHelpMan getchaintips()
15441570
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
15451571
{
15461572
ChainstateManager& chainman = EnsureAnyChainman(request.context);
1573+
const bool headers_only{IsHeadersOnlyMode(request)};
15471574
LOCK(cs_main);
15481575
CChain& active_chain = chainman.ActiveChain();
1576+
const CBlockIndex* reference_tip{headers_only ? CHECK_NONFATAL(GetRPCBestTip(chainman, /*headers_only=*/true)) : active_chain.Tip()};
15491577

1550-
/*
1551-
* Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them.
1552-
* Algorithm:
1553-
* - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
1554-
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
1555-
* - Add the active chain tip
1556-
*/
15571578
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
1558-
std::set<const CBlockIndex*> setOrphans;
1559-
std::set<const CBlockIndex*> setPrevs;
1579+
if (headers_only) {
1580+
/*
1581+
* In headers-only mode, use the set of all leaves in BlockIndex()
1582+
* relative to the selected header-chain reference tip.
1583+
*/
1584+
std::set<const CBlockIndex*> setPrevs;
1585+
1586+
for (const auto& [_, block_index] : chainman.BlockIndex()) {
1587+
if (block_index.pprev != nullptr) {
1588+
setPrevs.insert(block_index.pprev);
1589+
}
1590+
}
15601591

1561-
for (const auto& [_, block_index] : chainman.BlockIndex()) {
1562-
if (!active_chain.Contains(&block_index)) {
1563-
setOrphans.insert(&block_index);
1564-
setPrevs.insert(block_index.pprev);
1592+
for (const auto& [_, block_index] : chainman.BlockIndex()) {
1593+
if (!setPrevs.contains(&block_index)) {
1594+
setTips.insert(&block_index);
1595+
}
15651596
}
1566-
}
15671597

1568-
for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) {
1569-
if (setPrevs.erase(*it) == 0) {
1570-
setTips.insert(*it);
1598+
// Always report the selected reference tip.
1599+
setTips.insert(reference_tip);
1600+
} else {
1601+
/*
1602+
* Preserve existing non-headersonly behavior:
1603+
* active tip + orphan leaves.
1604+
*/
1605+
std::set<const CBlockIndex*> setOrphans;
1606+
std::set<const CBlockIndex*> setPrevs;
1607+
1608+
for (const auto& [_, block_index] : chainman.BlockIndex()) {
1609+
if (!active_chain.Contains(&block_index)) {
1610+
setOrphans.insert(&block_index);
1611+
setPrevs.insert(block_index.pprev);
1612+
}
15711613
}
1572-
}
15731614

1574-
// Always report the currently active tip.
1575-
setTips.insert(active_chain.Tip());
1615+
for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) {
1616+
if (setPrevs.erase(*it) == 0) {
1617+
setTips.insert(*it);
1618+
}
1619+
}
1620+
1621+
setTips.insert(active_chain.Tip());
1622+
}
15761623

15771624
/* Construct the output array. */
15781625
UniValue res(UniValue::VARR);
@@ -1581,13 +1628,22 @@ static RPCHelpMan getchaintips()
15811628
obj.pushKV("height", block->nHeight);
15821629
obj.pushKV("hash", block->phashBlock->GetHex());
15831630

1584-
const int branchLen = block->nHeight - active_chain.FindFork(block)->nHeight;
1631+
int branchLen{0};
1632+
if (headers_only) {
1633+
const CBlockIndex* fork{LastCommonAncestor(block, reference_tip)};
1634+
branchLen = block->nHeight - (fork ? fork->nHeight : 0);
1635+
} else {
1636+
branchLen = block->nHeight - active_chain.FindFork(block)->nHeight;
1637+
}
15851638
obj.pushKV("branchlen", branchLen);
15861639

15871640
std::string status;
1588-
if (active_chain.Contains(block)) {
1641+
if (!headers_only && active_chain.Contains(block)) {
15891642
// This block is part of the currently active chain.
15901643
status = "active";
1644+
} else if (headers_only && block == reference_tip) {
1645+
// This block is part of the selected header-chain reference tip.
1646+
status = "active";
15911647
} else if (block->nStatus & BLOCK_FAILED_MASK) {
15921648
// This block or one of its ancestors is invalid.
15931649
status = "invalid";

0 commit comments

Comments
 (0)