Skip to content

Commit 4669e6a

Browse files
committed
Merge remote-tracking branch 'master' into elements-22-fix-ci
2 parents 2e99de1 + ffeedde commit 4669e6a

16 files changed

+348
-63
lines changed

src/block_proof.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
bool CheckChallenge(const CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params& params)
1414
{
1515
if (g_signed_blocks) {
16-
return block.proof.challenge == indexLast.proof.challenge;
16+
return block.proof.challenge == indexLast.get_proof().challenge;
1717
} else {
1818
return block.nBits == GetNextWorkRequired(&indexLast, &block, params);
1919
}

src/chain.h

+64-15
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,51 @@ class CBlockIndex
192192
uint32_t nTime{0};
193193
uint32_t nBits{0};
194194
uint32_t nNonce{0};
195-
CProof proof{};
195+
196+
protected:
197+
std::optional<CProof> proof{};
196198
// Dynamic federation fields
197-
DynaFedParams dynafed_params{};
198-
CScriptWitness m_signblock_witness{};
199+
std::optional<DynaFedParams> m_dynafed_params{};
200+
std::optional<CScriptWitness> m_signblock_witness{};
201+
202+
bool m_trimmed{false};
203+
204+
friend class CBlockTreeDB;
205+
206+
public:
207+
208+
// Irrevocably remove blocksigning and dynafed-related stuff from this
209+
// in-memory copy of the block header.
210+
void trim() {
211+
assert_untrimmed();
212+
m_trimmed = true;
213+
proof = std::nullopt;
214+
m_dynafed_params = std::nullopt;
215+
m_signblock_witness = std::nullopt;
216+
}
217+
218+
bool trimmed() const {
219+
return m_trimmed;
220+
}
221+
222+
void assert_untrimmed() const {
223+
assert(!m_trimmed);
224+
}
225+
226+
const CProof& get_proof() const {
227+
assert_untrimmed();
228+
return proof.value();
229+
}
230+
231+
const DynaFedParams& dynafed_params() const {
232+
assert_untrimmed();
233+
return m_dynafed_params.value();
234+
}
235+
236+
const CScriptWitness& signblock_witness() const {
237+
assert_untrimmed();
238+
return m_signblock_witness.value();
239+
}
199240

200241
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
201242
int32_t nSequenceId{0};
@@ -214,7 +255,7 @@ class CBlockIndex
214255
nBits{block.nBits},
215256
nNonce{block.nNonce},
216257
proof{block.proof},
217-
dynafed_params{block.m_dynafed_params},
258+
m_dynafed_params{block.m_dynafed_params},
218259
m_signblock_witness{block.m_signblock_witness}
219260
{
220261
}
@@ -239,6 +280,7 @@ class CBlockIndex
239280

240281
CBlockHeader GetBlockHeader() const
241282
{
283+
assert_untrimmed();
242284
CBlockHeader block;
243285
block.nVersion = nVersion;
244286
if (pprev)
@@ -250,9 +292,9 @@ class CBlockIndex
250292
}
251293
block.nBits = nBits;
252294
block.nNonce = nNonce;
253-
block.proof = proof;
254-
block.m_dynafed_params = dynafed_params;
255-
block.m_signblock_witness = m_signblock_witness;
295+
block.proof = proof.value();
296+
block.m_dynafed_params = m_dynafed_params.value();
297+
block.m_signblock_witness = m_signblock_witness.value();
256298
return block;
257299
}
258300

@@ -366,12 +408,12 @@ class CDiskBlockIndex : public CBlockIndex
366408
nVersion = ~CBlockHeader::DYNAFED_HF_MASK & nVersion;
367409
return is_dyna;
368410
} else {
369-
return !dynafed_params.IsNull();
411+
return !dynafed_params().IsNull();
370412
}
371413
}
372414
bool RemoveDynaFedMaskOnSerialize(bool for_read) const {
373415
assert(!for_read);
374-
return !dynafed_params.IsNull();
416+
return !dynafed_params().IsNull();
375417
}
376418

377419
SERIALIZE_METHODS(CDiskBlockIndex, obj)
@@ -394,7 +436,7 @@ class CDiskBlockIndex : public CBlockIndex
394436
READWRITE(obj.nVersion);
395437
} else {
396438
int32_t nVersion = obj.nVersion;
397-
if (!obj.dynafed_params.IsNull()) {
439+
if (!obj.dynafed_params().IsNull()) {
398440
nVersion |= CBlockHeader::DYNAFED_HF_MASK;
399441
}
400442
READWRITE(nVersion);
@@ -404,13 +446,19 @@ class CDiskBlockIndex : public CBlockIndex
404446
READWRITE(obj.hashPrev);
405447
READWRITE(obj.hashMerkleRoot);
406448
READWRITE(obj.nTime);
449+
450+
// Allocate objects in the optional<> fields when reading, since READWRITE will not do this
451+
SER_READ(obj, obj.m_dynafed_params = DynaFedParams());
452+
SER_READ(obj, obj.m_signblock_witness = CScriptWitness());
453+
SER_READ(obj, obj.proof = CProof());
454+
407455
// For compatibility with elements 0.14 based chains
408456
if (g_signed_blocks) {
409457
if (is_dyna) {
410-
READWRITE(obj.dynafed_params);
411-
READWRITE(obj.m_signblock_witness.stack);
458+
READWRITE(obj.m_dynafed_params.value());
459+
READWRITE(obj.m_signblock_witness.value().stack);
412460
} else {
413-
READWRITE(obj.proof);
461+
READWRITE(obj.proof.value());
414462
}
415463
} else {
416464
READWRITE(obj.nBits);
@@ -420,6 +468,7 @@ class CDiskBlockIndex : public CBlockIndex
420468

421469
uint256 GetBlockHash() const
422470
{
471+
assert_untrimmed();
423472
CBlockHeader block;
424473
block.nVersion = nVersion;
425474
block.hashPrevBlock = hashPrev;
@@ -430,8 +479,8 @@ class CDiskBlockIndex : public CBlockIndex
430479
}
431480
block.nBits = nBits;
432481
block.nNonce = nNonce;
433-
block.proof = proof;
434-
block.m_dynafed_params = dynafed_params;
482+
block.proof = proof.value();
483+
block.m_dynafed_params = m_dynafed_params.value();
435484
return block.GetHash();
436485
}
437486

src/dynafed.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ bool NextBlockIsParameterTransition(const CBlockIndex* pindexPrev, const Consens
1515
for (int32_t height = next_height - 1; height >= (int32_t)(next_height - consensus.dynamic_epoch_length); --height) {
1616
const CBlockIndex* p_epoch_walk = pindexPrev->GetAncestor(height);
1717
assert(p_epoch_walk);
18-
const DynaFedParamEntry& proposal = p_epoch_walk->dynafed_params.m_proposed;
18+
const DynaFedParamEntry& proposal = p_epoch_walk->dynafed_params().m_proposed;
1919
const uint256 proposal_root = proposal.CalculateRoot();
2020
vote_tally[proposal_root]++;
2121
// Short-circuit once 4/5 threshold is reached
@@ -56,13 +56,13 @@ DynaFedParamEntry ComputeNextBlockFullCurrentParameters(const CBlockIndex* pinde
5656
// may be pre-dynafed params
5757
const CBlockIndex* p_epoch_start = pindexPrev->GetAncestor(epoch_start_height);
5858
assert(p_epoch_start);
59-
if (p_epoch_start->dynafed_params.IsNull()) {
59+
if (p_epoch_start->dynafed_params().IsNull()) {
6060
// We need to construct the "full" current parameters of pre-dynafed
6161
// consensus
6262

6363
// Convert signblockscript to P2WSH
6464
uint256 signblock_witness_program;
65-
CSHA256().Write(p_epoch_start->proof.challenge.data(), p_epoch_start->proof.challenge.size()).Finalize(signblock_witness_program.begin());
65+
CSHA256().Write(p_epoch_start->get_proof().challenge.data(), p_epoch_start->get_proof().challenge.size()).Finalize(signblock_witness_program.begin());
6666
CScript p2wsh_signblock_script = CScript() << OP_0 << ToByteVector(signblock_witness_program);
6767

6868
// Make P2SH-P2WSH-ness of non-dynafed fedpegscript explicit
@@ -75,7 +75,7 @@ DynaFedParamEntry ComputeNextBlockFullCurrentParameters(const CBlockIndex* pinde
7575
// Put them in winning proposal
7676
winning_proposal = DynaFedParamEntry(p2wsh_signblock_script, consensus.max_block_signature_size, sh_wsh_fedpeg_program, consensus.fedpegScript, consensus.first_extension_space);
7777
} else {
78-
winning_proposal = p_epoch_start->dynafed_params.m_current;
78+
winning_proposal = p_epoch_start->dynafed_params().m_current;
7979
}
8080
return winning_proposal;
8181
}
@@ -93,7 +93,7 @@ DynaFedParamEntry ComputeNextBlockCurrentParameters(const CBlockIndex* pindexPre
9393

9494
// Return appropriate format based on epoch age or if we *just* activated
9595
// dynafed via BIP9
96-
if (epoch_age == 0 || pindexPrev->dynafed_params.IsNull()) {
96+
if (epoch_age == 0 || pindexPrev->dynafed_params().IsNull()) {
9797
return entry;
9898
} else {
9999
return DynaFedParamEntry(entry.m_signblockscript, entry.m_signblock_witness_limit, entry.CalculateExtraRoot());

src/init.cpp

+27-1
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ void SetupServerArgs(ArgsManager& argsman)
425425
hidden_args.emplace_back("-sysperms");
426426
#endif
427427
argsman.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
428+
argsman.AddArg("-trim_headers", strprintf("Trim old headers in memory (by default older than 2 epochs), removing blocksigning and dynafed-related fields. Saves memory, but blocks us from serving blocks or headers to peers, and removes trimmed fields from some JSON RPC outputs. (default: false)"), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
428429
argsman.AddArg("-blockfilterindex=<type>",
429430
strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
430431
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
@@ -979,6 +980,26 @@ bool AppInitParameterInteraction(const ArgsManager& args)
979980
fPruneMode = true;
980981
}
981982

983+
uint32_t epoch_length = chainparams.GetConsensus().dynamic_epoch_length;
984+
if (epoch_length == std::numeric_limits<uint32_t>::max()) {
985+
// That's the default value, for non-dynafed chains and some tests. Pick a more sensible default here.
986+
epoch_length = 20160;
987+
}
988+
989+
if (args.IsArgSet("-trim_headers")) {
990+
LogPrintf("Configured for header-trimming mode. This will reduce memory usage substantially, but we will be unable to serve as a full P2P peer, and certain header fields may be missing from JSON RPC output.\n");
991+
fTrimHeaders = true;
992+
// This calculation is driven by GetValidFedpegScripts in pegins.cpp, which walks the chain
993+
// back to current epoch start, and then an additional total_valid_epochs on top of that.
994+
// We add one epoch here for the current partial epoch, and then another one for good luck.
995+
996+
nMustKeepFullHeaders = (chainparams.GetConsensus().total_valid_epochs + 2) * epoch_length;
997+
// This is the number of headers we can have in flight downloading at a time, beyond the
998+
// set of blocks we've already validated. Capping this is necessary to keep memory usage
999+
// bounded during IBD.
1000+
}
1001+
nHeaderDownloadBuffer = epoch_length * 2;
1002+
9821003
nConnectTimeout = args.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
9831004
if (nConnectTimeout <= 0) {
9841005
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
@@ -1690,7 +1711,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16901711

16911712
// if pruning, unset the service bit and perform the initial blockstore prune
16921713
// after any wallet rescanning has taken place.
1693-
if (fPruneMode) {
1714+
if (fPruneMode || fTrimHeaders) {
16941715
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
16951716
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
16961717
if (!fReindex) {
@@ -1702,6 +1723,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17021723
}
17031724
}
17041725

1726+
if (fTrimHeaders) {
1727+
LogPrintf("Unsetting NODE_NETWORK_LIMITED on header trim mode\n");
1728+
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK_LIMITED);
1729+
}
1730+
17051731
if (DeploymentEnabled(chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
17061732
// Advertise witness capabilities.
17071733
// The option to not set NODE_WITNESS is only used in the tests and should be removed.

src/miner.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
void ResetChallenge(CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params& params)
3131
{
32-
block.proof.challenge = indexLast.proof.challenge;
32+
block.proof.challenge = indexLast.get_proof().challenge;
3333
}
3434

3535
void ResetProof(CBlockHeader& block)

src/net_processing.cpp

+69-10
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static constexpr auto UNCONDITIONAL_RELAY_DELAY = 2min;
4848
/** Headers download timeout.
4949
* Timeout = base + per_header * (expected number of headers) */
5050
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
51-
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms;
51+
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 2ms;
5252
/** Protect at least this many outbound peers from disconnection due to slow/
5353
* behind headers chain.
5454
*/
@@ -910,6 +910,10 @@ bool PeerManagerImpl::TipMayBeStale()
910910

911911
bool PeerManagerImpl::CanDirectFetch()
912912
{
913+
if(!m_chainman.ActiveChain().Tip()) {
914+
LogPrint(BCLog::NET, "Tried to call CanDirectFetch with no currently-active chain.\n");
915+
return false;
916+
}
913917
return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - m_chainparams.GetConsensus().nPowTargetSpacing * 20;
914918
}
915919

@@ -2007,6 +2011,36 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
20072011
return;
20082012
}
20092013

2014+
// If we are already too far ahead of where we want to be on headers, discard
2015+
// the received headers. We can still get ahead by up to a single maximum-sized
2016+
// headers message here, but never further, so that's fine.
2017+
if (pindexBestHeader) {
2018+
uint64_t headers_ahead = pindexBestHeader->nHeight - m_chainman.ActiveHeight();
2019+
bool too_far_ahead = fTrimHeaders && (headers_ahead >= nHeaderDownloadBuffer);
2020+
if (too_far_ahead) {
2021+
LOCK(cs_main);
2022+
CNodeState *nodestate = State(pfrom.GetId());
2023+
if ((nodestate->pindexBestKnownBlock == nullptr) ||
2024+
(nodestate->pindexBestKnownBlock->nHeight < m_chainman.ActiveHeight())) {
2025+
// Our notion of what blocks a peer has available is based on its pindexBestKnownBlock,
2026+
// which is based on headers recieved from it. If we don't have one, or it's too old,
2027+
// then we can never get blocks from this peer until we accept headers from it first.
2028+
LogPrint(BCLog::NET, "NOT discarding headers from peer=%d, to update its block availability. (current best header %d, active chain height %d)\n", pfrom.GetId(), pindexBestHeader->nHeight, m_chainman.ActiveHeight());
2029+
} else {
2030+
LogPrint(BCLog::NET, "Discarding received headers and pausing header sync from peer=%d, because we are too far ahead of block sync. (%d > %d)\n", pfrom.GetId(), pindexBestHeader->nHeight, m_chainman.ActiveHeight());
2031+
if (nodestate->fSyncStarted) {
2032+
// Cancel sync from this node, so we don't penalize it later.
2033+
// This will cause us to automatically start syncing from a different node (or restart syncing from the same node) later,
2034+
// if we still need to sync headers.
2035+
nSyncStarted--;
2036+
nodestate->fSyncStarted = false;
2037+
nodestate->m_headers_sync_timeout = 0us;
2038+
}
2039+
return;
2040+
}
2041+
}
2042+
}
2043+
20102044
bool received_new_header = false;
20112045
const CBlockIndex *pindexLast = nullptr;
20122046
{
@@ -2084,14 +2118,29 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
20842118
nodestate->m_last_block_announcement = GetTime();
20852119
}
20862120

2087-
if (nCount == MAX_HEADERS_RESULTS && !all_duplicate) {
2088-
// Headers message had its maximum size; the peer may have more headers.
2089-
// TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue
2090-
// from there instead.
2091-
// HOWEVER, if all headers we got this time were duplicates that we already had, don't ask for any more.
2092-
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
2093-
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
2094-
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256()));
2121+
// If a peer gives us as many headers as possible, this is implicitly a signal that the
2122+
// peer has more headers to send us. In Bitcoin Core, the node always asks for more
2123+
// headers at this point. Our logic is slightly more complex, to work around an apparent
2124+
// bug in the Bitcoin Core state machine, where we can end up downloading headers from
2125+
/// lots of peers at the same time by accident.
2126+
if (nCount == MAX_HEADERS_RESULTS) {
2127+
if (all_duplicate && !nodestate->fSyncStarted) {
2128+
// In this case two things are true:
2129+
// 1) This node's most recent batch of headers only included ones we already had.
2130+
// 2) We don't have this node marked as a peer to header-sync from.
2131+
// This happens when some exogenous event, like an INV of a new block, causes us
2132+
// to ask a peer for an unbounded number of headers, when we're already in the
2133+
// process of downloading the headers from a different peer.
2134+
// In this case the right thing to do is simply stop syncing headers from this
2135+
// peer; it's redundant. Here we do nothing; since we don't ask the peer for
2136+
// more headers, it will stop sending them.
2137+
} else {
2138+
// TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue
2139+
// from there instead.
2140+
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
2141+
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
2142+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256()));
2143+
}
20952144
}
20962145

20972146
// If this set of headers is valid and ends in a block with at least as
@@ -3133,10 +3182,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
31333182
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());
31343183
for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
31353184
{
3185+
if (pindex->trimmed()) {
3186+
// For simplicity, if any of the headers they're asking for are trimmed,
3187+
// just drop the request.
3188+
LogPrint(BCLog::NET, "%s: ignoring getheaders from peer=%i which would return at least one trimmed header\n", __func__, pfrom.GetId());
3189+
return;
3190+
}
31363191
vHeaders.push_back(pindex->GetBlockHeader());
31373192
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
31383193
break;
31393194
}
3195+
31403196
// pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR
31413197
// if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty
31423198
// headers message). In both cases it's safe to update
@@ -4476,7 +4532,10 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
44764532
if (pindexBestHeader == nullptr)
44774533
pindexBestHeader = m_chainman.ActiveChain().Tip();
44784534
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
4479-
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
4535+
uint64_t headers_ahead = pindexBestHeader->nHeight - m_chainman.ActiveHeight();
4536+
// ELEMENTS: Only download if our headers aren't "too far ahead" of our blocks.
4537+
bool got_enough_headers = fTrimHeaders && (headers_ahead >= nHeaderDownloadBuffer);
4538+
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex && !got_enough_headers) {
44804539
// Only actively request headers from a single peer, unless we're close to today.
44814540
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
44824541
state.fSyncStarted = true;

0 commit comments

Comments
 (0)