feat: trigger early quorum calculation when VL is fetched

Call updateTrusted() immediately when all publisher lists become
available in applyListsAndBroadcast(), rather than waiting for
beginConsensus(). This allows validations to be trusted within
milliseconds of VL fetch instead of waiting 14+ seconds for
consensus to start.

Also adds debugging logs:
- PartialSync journal: untrusted validations during startup
- PartialSync journal: checkAccept quorum details
- ValidatorSite journal: VL fetch timing
This commit is contained in:
Nicholas Dudfield
2025-11-30 11:56:22 +07:00
parent 5d85d2df4b
commit dc5ec93207
4 changed files with 49 additions and 3 deletions

View File

@@ -216,6 +216,23 @@ handleNewValidation(
app.getLedgerMaster().checkAccept(hash, seq);
}
}
else
{
// Partial sync debug: only log untrusted validations during startup
// (before we have any validated ledger)
auto [lastHash, lastSeq] =
app.getLedgerMaster().getLastValidatedLedger();
if (lastSeq == 0)
{
auto jPartialSync = app.journal("PartialSync");
auto const quorum = app.validators().quorum();
auto const unlSize = app.validators().count();
JLOG(jPartialSync.debug())
<< "validation NOT trusted: seq=" << seq << " hash=" << hash
<< " unlSize=" << unlSize << " quorum=" << quorum
<< " (masterKey=" << (masterKey ? "found" : "none") << ")";
}
}
return;
}

View File

@@ -1012,7 +1012,7 @@ LedgerMaster::checkAccept(uint256 const& hash, std::uint32_t seq)
valCount = validations.size();
auto const quorum = app_.validators().quorum();
JLOG(m_journal.warn())
JLOG(jPartialSync_.warn())
<< "checkAccept: hash=" << hash << " seq=" << seq
<< " valCount=" << valCount << " quorum=" << quorum
<< " mLastValidLedger.seq=" << mLastValidLedger.second;
@@ -1022,11 +1022,18 @@ LedgerMaster::checkAccept(uint256 const& hash, std::uint32_t seq)
std::lock_guard ml(m_mutex);
if (seq > mLastValidLedger.second)
{
JLOG(m_journal.warn())
<< "checkAccept: setting mLastValidLedger to seq=" << seq;
JLOG(jPartialSync_.warn())
<< "checkAccept: QUORUM REACHED - setting mLastValidLedger"
<< " seq=" << seq << " hash=" << hash;
mLastValidLedger = std::make_pair(hash, seq);
}
}
else
{
JLOG(jPartialSync_.debug())
<< "checkAccept: quorum not reached, need " << quorum
<< " have " << valCount;
}
if (seq == mValidLedgerSeq)
return;

View File

@@ -895,6 +895,16 @@ ValidatorList::applyListsAndBroadcast(
if (good)
{
networkOPs.clearUNLBlocked();
// For partial sync: trigger early quorum calculation so
// validations can be trusted before consensus starts
JLOG(j_.warn()) << "All publisher lists available, triggering "
"early updateTrusted for partial sync";
updateTrusted(
{}, // empty seenValidators - we just need quorum calculated
timeKeeper_.now(),
networkOPs,
overlay,
hashRouter);
}
}
bool broadcast = disposition <= ListDisposition::known_sequence;

View File

@@ -166,6 +166,7 @@ ValidatorSite::load(
void
ValidatorSite::start()
{
JLOG(j_.warn()) << "ValidatorSite::start() called";
std::lock_guard l0{sites_mutex_};
std::lock_guard l1{state_mutex_};
if (timer_.expires_at() == clock_type::time_point{})
@@ -218,6 +219,11 @@ ValidatorSite::setTimer(
if (next != sites_.end())
{
pending_ = next->nextRefresh <= clock_type::now();
auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(
next->nextRefresh - clock_type::now());
JLOG(j_.warn()) << "ValidatorSite::setTimer() pending=" << pending_
<< " delay=" << delay.count() << "ms"
<< " uri=" << next->startingResource->uri;
cv_.notify_all();
timer_.expires_at(next->nextRefresh);
auto idx = std::distance(sites_.begin(), next);
@@ -225,6 +231,10 @@ ValidatorSite::setTimer(
this->onTimer(idx, ec);
});
}
else
{
JLOG(j_.warn()) << "ValidatorSite::setTimer() no sites configured";
}
}
void
@@ -339,6 +349,8 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec)
void
ValidatorSite::onTimer(std::size_t siteIdx, error_code const& ec)
{
JLOG(j_.warn()) << "ValidatorSite::onTimer() fired for site " << siteIdx
<< " ec=" << ec.message();
if (ec)
{
// Restart the timer if any errors are encountered, unless the error