mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-28 09:30:10 +00:00
feat(consensus): default sidecar reconciliation off
Compile out sidecar acquisition and merge plumbing unless xahaud_sidecar_reconciliation is enabled, and move fetch-positive .testnet scenarios into an opt-in suite. When reconciliation is enabled, gate eager fetches on current trusted root support so first-seen transient roots do not trigger network acquisition. This keeps the recovery path available for measurement without making it the default path. Verified default OFF with x-format-changed, git diff --check, x-quick-check, cmake configure/build, and ./build/rippled --unittest=ConsensusExtensions. ON path syntax-checked; Claude is assigned the parallel ON build/test review in a separate worktree.
This commit is contained in:
@@ -39,16 +39,6 @@ tests:
|
||||
- n3:no_export_sig=true
|
||||
- n4:no_export_sig=true
|
||||
|
||||
# Resilience counterpart to export_degradation: all nodes DO sign, but the
|
||||
# scenario drops one validator's proposals to n0 after setup. n0 remains above
|
||||
# quorum from direct proposals, but must reconstruct the missing sidecar leaf
|
||||
# through the fetch/acquisition path.
|
||||
- name: export_sidecar_fetch_under_proposal_drop_ce
|
||||
script: .testnet/scenarios/export/export_fetch_under_proposal_drop.py
|
||||
network:
|
||||
rc:
|
||||
- rng_poll_ms=333
|
||||
|
||||
- name: export_without_unl_report
|
||||
script: .testnet/scenarios/export/export_without_unl_report.py
|
||||
network:
|
||||
|
||||
48
.testnet/scenarios/sidecar-reconciliation-suite.yml
Normal file
48
.testnet/scenarios/sidecar-reconciliation-suite.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
# Opt-in suite for the sidecar SHAMap reconciliation path.
|
||||
#
|
||||
# These scenarios assert fetch/acquire/merge behavior and require a binary built
|
||||
# with:
|
||||
#
|
||||
# -Dxahaud_sidecar_reconciliation=ON
|
||||
#
|
||||
# The normal feature suites default to proposal-carried CE/export material and
|
||||
# do not run these tests while reconciliation is disabled by default.
|
||||
|
||||
defaults:
|
||||
network:
|
||||
node_count: 5
|
||||
launcher: tmux
|
||||
find_ports: true
|
||||
slave_delay: 0.2
|
||||
features:
|
||||
- ConsensusEntropy
|
||||
- Export
|
||||
track_features:
|
||||
- ConsensusEntropy
|
||||
- Export
|
||||
unl_report: true
|
||||
log_levels:
|
||||
TxQ: info
|
||||
Protocol: debug
|
||||
Peer: debug
|
||||
LedgerConsensus: debug
|
||||
ConsensusExtensions: debug
|
||||
NetworkOPs: info
|
||||
env:
|
||||
XAHAU_RESOURCE_PER_PORT: "1"
|
||||
rc:
|
||||
- rng_poll_ms=333
|
||||
|
||||
tests:
|
||||
- name: entropy_fetch_recovers_dropped_claims
|
||||
script: .testnet/scenarios/entropy/fetch_recovers_dropped_claims.py
|
||||
network:
|
||||
rc:
|
||||
- rng_poll_ms=333
|
||||
- n0:rngrevealdrop=100
|
||||
|
||||
- name: export_sidecar_fetch_under_proposal_drop_ce
|
||||
script: .testnet/scenarios/export/export_fetch_under_proposal_drop.py
|
||||
network:
|
||||
rc:
|
||||
- rng_poll_ms=333
|
||||
@@ -25,13 +25,6 @@ tests:
|
||||
- name: steady_state_entropy
|
||||
script: .testnet/scenarios/entropy/steady_state_entropy.py
|
||||
|
||||
- name: entropy_fetch_recovers_dropped_claims
|
||||
script: .testnet/scenarios/entropy/fetch_recovers_dropped_claims.py
|
||||
network:
|
||||
rc:
|
||||
- rng_poll_ms=333
|
||||
- n0:rngrevealdrop=100
|
||||
|
||||
- name: fallback_without_unl_report
|
||||
script: .testnet/scenarios/entropy/fallback_without_unl_report.py
|
||||
network:
|
||||
|
||||
@@ -181,6 +181,9 @@ if(xrpld)
|
||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/xrpld/*.cpp"
|
||||
)
|
||||
if(NOT xahaud_sidecar_reconciliation)
|
||||
list(FILTER sources EXCLUDE REGEX "/src/xrpld/app/ledger/SidecarSetSF\\.cpp$")
|
||||
endif()
|
||||
target_sources(rippled PRIVATE ${sources})
|
||||
|
||||
if(tests)
|
||||
|
||||
@@ -21,7 +21,7 @@ option(xahaud_runtime_test_config
|
||||
|
||||
option(xahaud_sidecar_reconciliation
|
||||
"Enable consensus-extension sidecar SHAMap fetch/acquire/reconcile path"
|
||||
ON)
|
||||
OFF)
|
||||
|
||||
option(unity "Creates a build using UNITY support in cmake. This is the default" ON)
|
||||
if(unity)
|
||||
|
||||
@@ -1143,7 +1143,9 @@ struct Export_test : public beast::unit_test::suite
|
||||
ce.exportSigCollector().addVerifiedSignature(
|
||||
txHash, valPK, originalSig, applySeq);
|
||||
auto const agreedHash = ce.buildExportSigSet(applySeq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(agreedHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(agreedHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
ce.acceptExportSigSet(agreedHash);
|
||||
ce.setExportSigConvergenceFailed();
|
||||
|
||||
@@ -1541,7 +1543,9 @@ struct Export_test : public beast::unit_test::suite
|
||||
ce.exportSigCollector().addVerifiedSignature(
|
||||
txHash, valPK, sig, applySeq);
|
||||
auto const agreedHash = ce.buildExportSigSet(applySeq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(agreedHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(agreedHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
ce.acceptExportSigSet(agreedHash);
|
||||
|
||||
auto const parent = env.app().getLedgerMaster().getClosedLedger();
|
||||
@@ -1613,7 +1617,9 @@ struct Export_test : public beast::unit_test::suite
|
||||
ce.exportSigCollector().addVerifiedSignature(
|
||||
txHash, valPK, sig, applySeq);
|
||||
auto const agreedHash = ce.buildExportSigSet(applySeq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(agreedHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(agreedHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
ce.acceptExportSigSet(agreedHash);
|
||||
|
||||
ExportResultBuilder::SignatureSnapshot signatures;
|
||||
|
||||
@@ -1471,7 +1471,9 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
|
||||
auto const entropySetHash = ce.buildEntropySet(seq);
|
||||
ce.acceptEntropySet(entropySetHash);
|
||||
BEAST_EXPECT(ce.isSidecarSet(entropySetHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(entropySetHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
// Once the sidecar gate has accepted a hash, injection is derived from
|
||||
// that sidecar snapshot. A local failure flag is diagnostic state, not
|
||||
// an additional selector input.
|
||||
@@ -2362,7 +2364,9 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
ce.exportSigCollector().addVerifiedSignature(
|
||||
txHash, valPK, originalSig, seq);
|
||||
auto const exportSigSetHash = ce.buildExportSigSet(seq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(exportSigSetHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(exportSigSetHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
auto const view = ce.activeValidatorView();
|
||||
|
||||
// A locally-built export signature map is not closed-ledger material
|
||||
@@ -4154,7 +4158,9 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
|
||||
auto const seq = ledger->info().seq + 1;
|
||||
auto const commitHash = ce.buildCommitSet(seq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(commitHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(commitHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
|
||||
ExtendedPosition revealPos{commitPos.txSetHash};
|
||||
revealPos.myReveal = ce.getEntropySecret();
|
||||
@@ -4178,7 +4184,9 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ce.pendingRevealCount() == 1);
|
||||
BEAST_EXPECT(ce.hasMinimumReveals());
|
||||
auto const entropyHash = ce.buildEntropySet(seq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(entropyHash));
|
||||
BEAST_EXPECT(
|
||||
ce.isSidecarSet(entropyHash) ==
|
||||
ConsensusExtensions::sidecarReconciliationEnabled());
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -4203,16 +4211,22 @@ public:
|
||||
testOnPreBuildTier2WithNegativeUNL();
|
||||
testProposalProofRoundTrip();
|
||||
testHarvestRngDataReplacementAndRejection();
|
||||
testExportSidecarBuildFetchAndMerge();
|
||||
testExportSidecarIgnoresCancelOnlyExports();
|
||||
testExportSidecarBuildCapsConsensusCandidates();
|
||||
testExportSidecarRejectsInvalidFetchedEntries();
|
||||
testExportSidecarRejectsOversizedFetchedSet();
|
||||
if constexpr (ConsensusExtensions::sidecarReconciliationEnabled())
|
||||
{
|
||||
testExportSidecarBuildFetchAndMerge();
|
||||
testExportSidecarIgnoresCancelOnlyExports();
|
||||
testExportSidecarBuildCapsConsensusCandidates();
|
||||
testExportSidecarRejectsInvalidFetchedEntries();
|
||||
testExportSidecarRejectsOversizedFetchedSet();
|
||||
}
|
||||
testExportAgreedSignaturesIgnoreLiveCollectorMutation();
|
||||
testOnPreBuildPreservesExportDecision();
|
||||
testRngSidecarBuildFetchAndMerge();
|
||||
testRngSidecarRejectsOversizedFetchedSet();
|
||||
testRngSidecarRejectsInvalidFetchedEntries();
|
||||
if constexpr (ConsensusExtensions::sidecarReconciliationEnabled())
|
||||
{
|
||||
testRngSidecarBuildFetchAndMerge();
|
||||
testRngSidecarRejectsOversizedFetchedSet();
|
||||
testRngSidecarRejectsInvalidFetchedEntries();
|
||||
}
|
||||
testOnPreBuildInjectsStandaloneEntropy();
|
||||
testOnPreBuildEntropyMismatchKeepsAgreed();
|
||||
testDiagnosticsJsonAndPositionLogging();
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
namespace ripple {
|
||||
@@ -181,6 +182,7 @@ makeSidecarItem(STObject const& sidecar)
|
||||
return make_shamapitem(itemKey, s.slice());
|
||||
}
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
bool
|
||||
sidecarLeafCountWithin(SHAMap const& map, std::size_t maxLeaves)
|
||||
{
|
||||
@@ -196,6 +198,7 @@ sidecarLeafCountWithin(SHAMap const& map, std::size_t maxLeaves)
|
||||
});
|
||||
return within;
|
||||
}
|
||||
#endif
|
||||
|
||||
//@@start active-validator-view-build
|
||||
ActiveValidatorViewSource
|
||||
@@ -406,6 +409,117 @@ ConsensusExtensions::entropyGateThreshold() const
|
||||
return entropyGateThresholdForView(view->size(), view->originalViewSize);
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusExtensions::recordSidecarRootSupport(
|
||||
NodeID const& nodeId,
|
||||
ExtendedPosition const& position)
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
if (!isUNLReportMember(nodeId))
|
||||
return;
|
||||
|
||||
auto record = [&](auto& roots, std::optional<uint256> const& hash) {
|
||||
for (auto it = roots.begin(); it != roots.end();)
|
||||
{
|
||||
auto current = it++;
|
||||
current->second.erase(nodeId);
|
||||
if (current->second.empty())
|
||||
roots.erase(current);
|
||||
}
|
||||
|
||||
if (hash && *hash != uint256{})
|
||||
roots[*hash].insert(nodeId);
|
||||
};
|
||||
|
||||
record(sidecarRootSupport_.commitSet, position.commitSetHash);
|
||||
record(sidecarRootSupport_.entropySet, position.entropySetHash);
|
||||
record(sidecarRootSupport_.exportSigSet, position.exportSigSetHash);
|
||||
#else
|
||||
(void)nodeId;
|
||||
(void)position;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ConsensusExtensions::sidecarRootSupport(SidecarKind kind, uint256 const& hash)
|
||||
const
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
auto observedSupport = [&](auto const& roots) -> std::size_t {
|
||||
auto const it = roots.find(hash);
|
||||
return it == roots.end() ? 0 : it->second.size();
|
||||
};
|
||||
|
||||
std::size_t support = 0;
|
||||
auto const localContribution =
|
||||
localIsActiveValidator() ? std::size_t{1} : std::size_t{0};
|
||||
switch (kind)
|
||||
{
|
||||
case SidecarKind::commitSet:
|
||||
support = observedSupport(sidecarRootSupport_.commitSet);
|
||||
if (commitSetMap_ && commitSetMap_->getHash().as_uint256() == hash)
|
||||
support += localContribution;
|
||||
break;
|
||||
case SidecarKind::entropySet:
|
||||
support = observedSupport(sidecarRootSupport_.entropySet);
|
||||
if (entropySetMap_ &&
|
||||
entropySetMap_->getHash().as_uint256() == hash)
|
||||
support += localContribution;
|
||||
break;
|
||||
case SidecarKind::exportSigSet:
|
||||
support = observedSupport(sidecarRootSupport_.exportSigSet);
|
||||
if (exportSigSetMap_ &&
|
||||
exportSigSetMap_->getHash().as_uint256() == hash)
|
||||
support += localContribution;
|
||||
break;
|
||||
}
|
||||
return support;
|
||||
#else
|
||||
(void)kind;
|
||||
(void)hash;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ConsensusExtensions::sidecarFetchSupportThreshold(SidecarKind kind) const
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
auto const threshold = kind == SidecarKind::exportSigSet
|
||||
? exportSigQuorumThreshold()
|
||||
: entropyGateThreshold();
|
||||
return threshold == 0 ? std::numeric_limits<std::size_t>::max() : threshold;
|
||||
#else
|
||||
(void)kind;
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ConsensusExtensions::shouldEagerFetchSidecarSet(
|
||||
SidecarKind kind,
|
||||
uint256 const& hash) const
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
auto const support = sidecarRootSupport(kind, hash);
|
||||
auto const threshold = sidecarFetchSupportThreshold(kind);
|
||||
if (support >= threshold)
|
||||
return true;
|
||||
|
||||
JLOG(j_.trace()) << "SIDECARFETCH: skip"
|
||||
<< " kind=" << sidecarKindName(kind)
|
||||
<< " origin=eagerProposal"
|
||||
<< " hash=" << hash
|
||||
<< " reason=root-support-below-threshold"
|
||||
<< " support=" << support << " threshold=" << threshold;
|
||||
return false;
|
||||
#else
|
||||
(void)kind;
|
||||
(void)hash;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ConsensusExtensions::entropyGateThresholdForView(
|
||||
std::size_t effectiveViewSize,
|
||||
@@ -1425,6 +1539,7 @@ ConsensusExtensions::clearRngStatePreservingExport()
|
||||
consensusExportTxns_.clear();
|
||||
consensusTxSetHash_.reset();
|
||||
pendingSidecarFetches_.clear();
|
||||
sidecarRootSupport_ = {};
|
||||
observedParticipantsHash_.reset();
|
||||
observedParticipantsCount_ = 0;
|
||||
observedParticipantsBitmapBin_.clear();
|
||||
@@ -1555,9 +1670,7 @@ ConsensusExtensions::isActiveValidator(
|
||||
bool
|
||||
ConsensusExtensions::isSidecarSet(uint256 const& hash) const
|
||||
{
|
||||
if constexpr (!sidecarReconciliationEnabled())
|
||||
return false;
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
if (commitSetMap_ && commitSetMap_->getHash().as_uint256() == hash)
|
||||
return true;
|
||||
if (entropySetMap_ && entropySetMap_->getHash().as_uint256() == hash)
|
||||
@@ -1565,6 +1678,10 @@ ConsensusExtensions::isSidecarSet(uint256 const& hash) const
|
||||
if (exportSigSetMap_ && exportSigSetMap_->getHash().as_uint256() == hash)
|
||||
return true;
|
||||
return pendingSidecarFetches_.find(hash) != pendingSidecarFetches_.end();
|
||||
#else
|
||||
(void)hash;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
//@@end is-sidecar-set
|
||||
|
||||
@@ -1573,14 +1690,7 @@ ConsensusExtensions::isSidecarSet(uint256 const& hash) const
|
||||
void
|
||||
ConsensusExtensions::onAcquiredSidecarSet(std::shared_ptr<SHAMap> const& map)
|
||||
{
|
||||
if constexpr (!sidecarReconciliationEnabled())
|
||||
{
|
||||
JLOG(j_.debug()) << "SIDECARFETCH: acquired set ignored"
|
||||
<< " reason=reconciliation-disabled"
|
||||
<< " hash=" << map->getHash().as_uint256();
|
||||
return;
|
||||
}
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
auto const hash = map->getHash().as_uint256();
|
||||
|
||||
// Look up the expected kind before erasing.
|
||||
@@ -1974,6 +2084,11 @@ ConsensusExtensions::onAcquiredSidecarSet(std::shared_ptr<SHAMap> const& map)
|
||||
<< " pendingBefore=" << pendingBefore
|
||||
<< " pendingAfter=" << pendingAfter
|
||||
<< " pendingDelta=" << pendingDelta;
|
||||
#else
|
||||
JLOG(j_.debug()) << "SIDECARFETCH: acquired set ignored"
|
||||
<< " reason=reconciliation-disabled"
|
||||
<< " hash=" << map->getHash().as_uint256();
|
||||
#endif
|
||||
}
|
||||
//@@end handle-acquired-sidecar
|
||||
|
||||
@@ -1983,18 +2098,7 @@ ConsensusExtensions::fetchSidecarSetIfNeeded(
|
||||
SidecarKind kind,
|
||||
char const* origin)
|
||||
{
|
||||
if constexpr (!sidecarReconciliationEnabled())
|
||||
{
|
||||
if (hash && *hash != uint256{})
|
||||
{
|
||||
JLOG(j_.trace())
|
||||
<< "SIDECARFETCH: skip"
|
||||
<< " kind=" << sidecarKindName(kind) << " origin=" << origin
|
||||
<< " hash=" << *hash << " reason=reconciliation-disabled";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
if (!hash)
|
||||
{
|
||||
JLOG(j_.trace()) << "SIDECARFETCH: skip"
|
||||
@@ -2090,6 +2194,15 @@ ConsensusExtensions::fetchSidecarSetIfNeeded(
|
||||
<< " origin=" << origin << " hash=" << *hash;
|
||||
onAcquiredSidecarSet(immediate);
|
||||
}
|
||||
#else
|
||||
if (hash && *hash != uint256{})
|
||||
{
|
||||
JLOG(j_.trace()) << "SIDECARFETCH: skip"
|
||||
<< " kind=" << sidecarKindName(kind)
|
||||
<< " origin=" << origin << " hash=" << *hash
|
||||
<< " reason=reconciliation-disabled";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2097,19 +2210,22 @@ ConsensusExtensions::fetchSidecarsIfNeeded(
|
||||
ExtendedPosition const& peerPos,
|
||||
char const* origin)
|
||||
{
|
||||
if constexpr (!sidecarReconciliationEnabled())
|
||||
{
|
||||
(void)peerPos;
|
||||
(void)origin;
|
||||
return;
|
||||
}
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
auto const isEager = std::strcmp(origin, "eagerProposal") == 0;
|
||||
auto fetch = [&](std::optional<uint256> const& hash, SidecarKind kind) {
|
||||
if (isEager && hash && *hash != uint256{} &&
|
||||
!shouldEagerFetchSidecarSet(kind, *hash))
|
||||
return;
|
||||
fetchSidecarSetIfNeeded(hash, kind, origin);
|
||||
};
|
||||
|
||||
fetchSidecarSetIfNeeded(
|
||||
peerPos.commitSetHash, SidecarKind::commitSet, origin);
|
||||
fetchSidecarSetIfNeeded(
|
||||
peerPos.entropySetHash, SidecarKind::entropySet, origin);
|
||||
fetchSidecarSetIfNeeded(
|
||||
peerPos.exportSigSetHash, SidecarKind::exportSigSet, origin);
|
||||
fetch(peerPos.commitSetHash, SidecarKind::commitSet);
|
||||
fetch(peerPos.entropySetHash, SidecarKind::entropySet);
|
||||
fetch(peerPos.exportSigSetHash, SidecarKind::exportSigSet);
|
||||
#else
|
||||
(void)peerPos;
|
||||
(void)origin;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2850,6 +2966,8 @@ ConsensusExtensions::onTrustedPeerProposal(
|
||||
return;
|
||||
}
|
||||
|
||||
recordSidecarRootSupport(nodeId, position);
|
||||
|
||||
harvestRngData(
|
||||
nodeId,
|
||||
publicKey,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace ripple {
|
||||
|
||||
#ifndef XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
#define XAHAUD_ENABLE_SIDECAR_RECONCILIATION 1
|
||||
#define XAHAUD_ENABLE_SIDECAR_RECONCILIATION 0
|
||||
#endif
|
||||
|
||||
class Application;
|
||||
@@ -127,6 +127,17 @@ private:
|
||||
// content-sniffing and logs can attribute eager vs gate-triggered fetches.
|
||||
hash_map<uint256, PendingSidecarFetch> pendingSidecarFetches_;
|
||||
|
||||
struct SidecarRootSupport
|
||||
{
|
||||
hash_map<uint256, hash_set<NodeID>> commitSet;
|
||||
hash_map<uint256, hash_set<NodeID>> entropySet;
|
||||
hash_map<uint256, hash_set<NodeID>> exportSigSet;
|
||||
};
|
||||
|
||||
// Per-round support for sidecar roots observed in trusted proposals. Eager
|
||||
// acquisition uses this to avoid fetching first-seen transient roots.
|
||||
SidecarRootSupport sidecarRootSupport_;
|
||||
|
||||
// Parent-ledger validator view used by RNG and Export quorum logic.
|
||||
ActiveValidatorViewPtr activeValidatorView_ =
|
||||
std::make_shared<ActiveValidatorView const>();
|
||||
@@ -175,6 +186,20 @@ private:
|
||||
char const* sourceTag,
|
||||
RngProofCachePolicy proofCachePolicy);
|
||||
|
||||
void
|
||||
recordSidecarRootSupport(
|
||||
NodeID const& nodeId,
|
||||
ExtendedPosition const& position);
|
||||
|
||||
std::size_t
|
||||
sidecarRootSupport(SidecarKind kind, uint256 const& hash) const;
|
||||
|
||||
std::size_t
|
||||
sidecarFetchSupportThreshold(SidecarKind kind) const;
|
||||
|
||||
bool
|
||||
shouldEagerFetchSidecarSet(SidecarKind kind, uint256 const& hash) const;
|
||||
|
||||
// Commit proofs keyed by NodeID. Only seq=0 proofs are cached because the
|
||||
// commit sidecar hash must be deterministic across all nodes.
|
||||
hash_map<NodeID, ProposalProof> commitProofs_;
|
||||
|
||||
@@ -1032,29 +1032,21 @@ RCLConsensus::phase() const
|
||||
bool
|
||||
RCLConsensus::isExtensionSet(uint256 const& hash) const
|
||||
{
|
||||
if constexpr (!ConsensusExtensions::sidecarReconciliationEnabled())
|
||||
{
|
||||
(void)hash;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
std::lock_guard _{mutex_};
|
||||
if (consensus_->phase() == ConsensusPhase::accepted)
|
||||
return false;
|
||||
return adaptor_.ce().isSidecarSet(hash);
|
||||
#else
|
||||
(void)hash;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::gotExtensionSet(std::shared_ptr<SHAMap> const& map)
|
||||
{
|
||||
if constexpr (!ConsensusExtensions::sidecarReconciliationEnabled())
|
||||
{
|
||||
JLOG(j_.debug()) << "Ignoring acquired sidecar set "
|
||||
<< map->getHash().as_uint256()
|
||||
<< " reason=reconciliation-disabled";
|
||||
return;
|
||||
}
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
std::lock_guard _{mutex_};
|
||||
// Accept builds run without the consensus mutex and clear extension
|
||||
// working state. Late sidecar fetches belong to the previous establish
|
||||
@@ -1066,6 +1058,11 @@ RCLConsensus::gotExtensionSet(std::shared_ptr<SHAMap> const& map)
|
||||
return;
|
||||
}
|
||||
adaptor_.ce().onAcquiredSidecarSet(map);
|
||||
#else
|
||||
JLOG(j_.debug()) << "Ignoring acquired sidecar set "
|
||||
<< map->getHash().as_uint256()
|
||||
<< " reason=reconciliation-disabled";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -20,16 +20,24 @@
|
||||
#include <xrpld/app/ledger/ConsensusTransSetSF.h>
|
||||
#include <xrpld/app/ledger/InboundLedgers.h>
|
||||
#include <xrpld/app/ledger/InboundTransactions.h>
|
||||
#include <xrpld/app/ledger/SidecarSetSF.h>
|
||||
#include <xrpld/app/ledger/detail/TransactionAcquire.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/app/misc/NetworkOPs.h>
|
||||
#include <xrpld/overlay/Overlay.h>
|
||||
#include <xrpld/overlay/detail/ProtocolMessage.h>
|
||||
#include <xrpld/shamap/SHAMapSyncFilter.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifndef XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
#define XAHAUD_ENABLE_SIDECAR_RECONCILIATION 0
|
||||
#endif
|
||||
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
#include <xrpld/app/ledger/SidecarSetSF.h>
|
||||
#endif
|
||||
|
||||
namespace ripple {
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
@@ -47,14 +55,47 @@ namespace {
|
||||
std::unique_ptr<SHAMapSyncFilter>
|
||||
makeSyncFilter(InboundSetKind kind, Application& app)
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
// Sidecars deliberately reuse candidate tx-set acquisition; the filter only
|
||||
// changes leaf handling so sidecar STObjects are cached, not submitted.
|
||||
if (kind == InboundSetKind::sidecar)
|
||||
return std::make_unique<SidecarSetSF>(app.getTempNodeCache());
|
||||
#else
|
||||
if (kind == InboundSetKind::sidecar)
|
||||
LogicError("Sidecar acquisition requested while disabled");
|
||||
#endif
|
||||
|
||||
return std::make_unique<ConsensusTransSetSF>(app, app.getTempNodeCache());
|
||||
}
|
||||
|
||||
SHAMapType
|
||||
acquireMapType(InboundSetKind kind)
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
if (kind == InboundSetKind::sidecar)
|
||||
return SHAMapType::SIDECAR;
|
||||
#else
|
||||
if (kind == InboundSetKind::sidecar)
|
||||
LogicError("Sidecar SHAMap acquisition requested while disabled");
|
||||
#endif
|
||||
|
||||
return SHAMapType::TRANSACTION;
|
||||
}
|
||||
|
||||
char const*
|
||||
acquireSetKindName(InboundSetKind kind)
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
if (kind == InboundSetKind::sidecar)
|
||||
return "sidecar";
|
||||
#else
|
||||
if (kind == InboundSetKind::sidecar)
|
||||
return "sidecar-disabled";
|
||||
#endif
|
||||
|
||||
return "TX";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransactionAcquire::TransactionAcquire(
|
||||
@@ -72,13 +113,10 @@ TransactionAcquire::TransactionAcquire(
|
||||
, mPeerSet(std::move(peerSet))
|
||||
, mSetKind(kind)
|
||||
{
|
||||
// Keep sidecar fetch on the same content-addressed SHAMap path as tx sets:
|
||||
// normal reply limits, peer scoring, charging, and timeout behavior apply.
|
||||
// Candidate set acquisition is content-addressed; normal reply limits, peer
|
||||
// scoring, charging, and timeout behavior apply.
|
||||
mMap = std::make_shared<SHAMap>(
|
||||
kind == InboundSetKind::sidecar ? SHAMapType::SIDECAR
|
||||
: SHAMapType::TRANSACTION,
|
||||
hash,
|
||||
app_.getNodeFamily());
|
||||
acquireMapType(kind), hash, app_.getNodeFamily());
|
||||
mMap->setUnbacked();
|
||||
}
|
||||
|
||||
@@ -94,9 +132,7 @@ TransactionAcquire::done()
|
||||
else
|
||||
{
|
||||
JLOG(journal_.debug())
|
||||
<< "Acquired "
|
||||
<< (mSetKind == InboundSetKind::sidecar ? "sidecar" : "TX")
|
||||
<< " set " << hash_;
|
||||
<< "Acquired " << acquireSetKindName(mSetKind) << " set " << hash_;
|
||||
mMap->setImmutable();
|
||||
|
||||
uint256 const& hash(hash_);
|
||||
|
||||
@@ -1990,6 +1990,7 @@ NetworkOPsImp::mapComplete(std::shared_ptr<SHAMap> const& map, bool fromAcquire)
|
||||
auto const hash = map->getHash().as_uint256();
|
||||
if (map->mapType() == SHAMapType::SIDECAR)
|
||||
{
|
||||
#if XAHAUD_ENABLE_SIDECAR_RECONCILIATION
|
||||
if (mConsensus.isExtensionSet(hash))
|
||||
{
|
||||
// Extension sidecar set (commitSet, entropySet, or
|
||||
@@ -2005,6 +2006,10 @@ NetworkOPsImp::mapComplete(std::shared_ptr<SHAMap> const& map, bool fromAcquire)
|
||||
JLOG(m_journal.debug())
|
||||
<< "Ignoring stale acquired sidecar set " << hash;
|
||||
}
|
||||
#else
|
||||
JLOG(m_journal.debug()) << "Ignoring acquired sidecar set " << hash
|
||||
<< " reason=reconciliation-disabled";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
mConsensus.gotTxSet(app_.timeKeeper().closeTime(), RCLTxSet{map});
|
||||
|
||||
Reference in New Issue
Block a user