Compare commits

...

5 Commits

Author SHA1 Message Date
Nicholas Dudfield
4cf2be8e24 fix: add fatal log on amendment-blocked shutdown 2026-03-09 09:21:10 +07:00
Nicholas Dudfield
277e9f26bc fix: rethrow runtime_error if not amendment blocked 2026-03-08 10:33:28 +07:00
Nicholas Dudfield
ffcc58c8aa fix: narrow catch to std::runtime_error in switchLastClosedLedger 2026-03-08 10:32:36 +07:00
Nicholas Dudfield
9246677e9c fix: skip signalStop in standalone mode for test compatibility 2026-03-08 09:51:30 +07:00
Nicholas Dudfield
1f8418a58b fix: fail fast when amendment blocked instead of zombie state
- signalStop() for graceful shutdown when unsupported amendment activates
- early shutdown ~1 minute before expected activation to avoid race
- try/catch in switchLastClosedLedger to survive unknown field crashes
  during shutdown window
- show amendment warning to all RPC users, not just admin

Fixes: #706
2026-03-08 08:41:20 +07:00
2 changed files with 52 additions and 9 deletions

View File

@@ -310,12 +310,28 @@ LedgerMaster::setValidLedger(std::shared_ptr<Ledger const> const& l)
if (auto const first =
app_.getAmendmentTable().firstUnsupportedExpected())
{
JLOG(m_journal.error()) << "One or more unsupported amendments "
"reached majority. Upgrade before "
<< to_string(*first)
<< " to prevent your server from "
"becoming amendment blocked.";
app_.getOPs().setAmendmentWarned();
using namespace std::chrono_literals;
auto const now = app_.timeKeeper().closeTime();
if (*first > now && (*first - now) <= 1min)
{
// Shut down just before the amendment activates to
// avoid processing ledgers with unknown fields.
JLOG(m_journal.error())
<< "Unsupported amendment activating imminently "
"at "
<< to_string(*first) << ". Shutting down.";
app_.getOPs().setAmendmentBlocked();
}
else
{
JLOG(m_journal.error())
<< "One or more unsupported amendments "
"reached majority. Upgrade before "
<< to_string(*first)
<< " to prevent your server from "
"becoming amendment blocked.";
app_.getOPs().setAmendmentWarned();
}
}
else
app_.getOPs().clearAmendmentWarned();

View File

@@ -1634,6 +1634,16 @@ NetworkOPsImp::setAmendmentBlocked()
{
amendmentBlocked_ = true;
setMode(OperatingMode::CONNECTED);
if (!app_.config().standalone())
{
JLOG(m_journal.fatal())
<< "One or more unsupported amendments activated. "
"Shutting down. Upgrade the server to remain "
"compatible with the network.";
app_.signalStop(
"One or more unsupported amendments activated. "
"Server must be upgraded to remain compatible with the network.");
}
}
inline bool
@@ -1789,8 +1799,23 @@ NetworkOPsImp::switchLastClosedLedger(
clearNeedNetworkLedger();
// Update fee computations.
app_.getTxQ().processClosedLedger(app_, *newLCL, true);
// Update fee computations. May throw if the ledger contains
// transactions with fields unknown to this binary (e.g. after an
// unsupported amendment activates). Catch to allow graceful shutdown.
//@@start process-closed-ledger-catch
try
{
app_.getTxQ().processClosedLedger(app_, *newLCL, true);
}
catch (std::runtime_error const& e)
{
if (!amendmentBlocked_)
throw;
JLOG(m_journal.error())
<< "Failed to process closed ledger: " << e.what();
return;
}
//@@end process-closed-ledger-catch
// Caller must own master lock
{
@@ -2449,7 +2474,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters)
"may be incorrectly configured or some [validator_list_sites] "
"may be unreachable.";
}
if (admin && isAmendmentWarned())
if (isAmendmentWarned())
{
Json::Value& w = warnings.append(Json::objectValue);
w[jss::id] = warnRPC_UNSUPPORTED_MAJORITY;
@@ -2893,6 +2918,7 @@ NetworkOPsImp::pubLedger(std::shared_ptr<ReadView const> const& lpAccepted)
// Ledgers are published only when they acquire sufficient validations
// Holes are filled across connection loss or other catastrophe
//@@start pubLedger-accepted-ledger-construction
std::shared_ptr<AcceptedLedger> alpAccepted =
app_.getAcceptedLedgerCache().fetch(lpAccepted->info().hash);
if (!alpAccepted)
@@ -2901,6 +2927,7 @@ NetworkOPsImp::pubLedger(std::shared_ptr<ReadView const> const& lpAccepted)
app_.getAcceptedLedgerCache().canonicalize_replace_client(
lpAccepted->info().hash, alpAccepted);
}
//@@end pubLedger-accepted-ledger-construction
XRPL_ASSERT(
alpAccepted->getLedger().get() == lpAccepted.get(),