Files
rippled/src/xrpld/app/consensus/RCLValidations.cpp

233 lines
7.2 KiB
C++

#include <xrpld/app/consensus/RCLValidations.h>
#include <xrpld/app/ledger/InboundLedger.h>
#include <xrpld/app/ledger/InboundLedgers.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/app/misc/ValidatorList.h>
#include <xrpld/consensus/Validations.h>
#include <xrpld/core/TimeKeeper.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/Job.h>
#include <xrpl/core/JobQueue.h>
#include <xrpl/core/PerfLog.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/RippleLedgerHash.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <memory>
#include <optional>
namespace xrpl {
RCLValidatedLedger::RCLValidatedLedger(MakeGenesis)
: ledgerID_{0}, ledgerSeq_{0}, j_{beast::Journal::getNullSink()}
{
}
RCLValidatedLedger::RCLValidatedLedger(
std::shared_ptr<Ledger const> const& ledger,
beast::Journal j)
: ledgerID_{ledger->header().hash}, ledgerSeq_{ledger->seq()}, j_{j}
{
auto const hashIndex = ledger->read(keylet::skip());
if (hashIndex)
{
XRPL_ASSERT(
hashIndex->getFieldU32(sfLastLedgerSequence) == (seq() - 1),
"xrpl::RCLValidatedLedger::RCLValidatedLedger(Ledger) : valid "
"last ledger sequence");
ancestors_ = hashIndex->getFieldV256(sfHashes).value();
}
else
{
JLOG(j_.warn()) << "Ledger " << ledgerSeq_ << ":" << ledgerID_
<< " missing recent ancestor hashes";
}
}
auto
RCLValidatedLedger::minSeq() const -> Seq
{
return seq() - std::min(seq(), static_cast<Seq>(ancestors_.size()));
}
auto
RCLValidatedLedger::seq() const -> Seq
{
return ledgerSeq_;
}
auto
RCLValidatedLedger::id() const -> ID
{
return ledgerID_;
}
auto
RCLValidatedLedger::operator[](Seq const& s) const -> ID
{
if (s >= minSeq() && s <= seq())
{
if (s == seq())
return ledgerID_;
Seq const diff = seq() - s;
return ancestors_[ancestors_.size() - diff];
}
JLOG(j_.warn()) << "Unable to determine hash of ancestor seq=" << s
<< " from ledger hash=" << ledgerID_ << " seq=" << ledgerSeq_
<< " (available: " << minSeq() << "-" << seq() << ")";
// Default ID that is less than all others
return ID{0};
}
// Return the sequence number of the earliest possible mismatching ancestor
RCLValidatedLedger::Seq
mismatch(RCLValidatedLedger const& a, RCLValidatedLedger const& b)
{
using Seq = RCLValidatedLedger::Seq;
// Find overlapping interval for known sequence for the ledgers
Seq const lower = std::max(a.minSeq(), b.minSeq());
Seq const upper = std::min(a.seq(), b.seq());
Seq curr = upper;
while (curr != Seq{0} && a[curr] != b[curr] && curr >= lower)
--curr;
// If the searchable interval mismatches entirely, then we have to
// assume the ledgers mismatch starting post genesis ledger
return (curr < lower) ? Seq{1} : (curr + Seq{1});
}
RCLValidationsAdaptor::RCLValidationsAdaptor(Application& app, beast::Journal j) : app_(app), j_(j)
{
}
NetClock::time_point
RCLValidationsAdaptor::now() const
{
return app_.getTimeKeeper().closeTime();
}
std::optional<RCLValidatedLedger>
RCLValidationsAdaptor::acquire(LedgerHash const& hash)
{
using namespace std::chrono_literals;
auto ledger = perf::measureDurationAndLog(
[&]() { return app_.getLedgerMaster().getLedgerByHash(hash); },
"getLedgerByHash",
10ms,
j_);
if (!ledger)
{
JLOG(j_.warn()) << "Need validated ledger for preferred ledger analysis " << hash;
app_.getInboundLedgers().acquireAsync(
JtAdvance, "GetConsL2", hash, 0, InboundLedger::Reason::CONSENSUS);
return std::nullopt;
}
XRPL_ASSERT(
!ledger->open() && ledger->isImmutable(),
"xrpl::RCLValidationsAdaptor::acquire : valid ledger state");
XRPL_ASSERT(
ledger->header().hash == hash, "xrpl::RCLValidationsAdaptor::acquire : ledger hash match");
return RCLValidatedLedger(ledger, j_);
}
void
handleNewValidation(
Application& app,
std::shared_ptr<STValidation> const& val,
std::string const& source,
BypassAccept const bypassAccept,
std::optional<beast::Journal> j)
{
auto const& signingKey = val->getSignerPublic();
auto const& hash = val->getLedgerHash();
auto const seq = val->getFieldU32(sfLedgerSequence);
// Ensure validation is marked as trusted if signer currently trusted
auto masterKey = app.getValidators().getTrustedKey(signingKey);
if (!val->isTrusted() && masterKey)
val->setTrusted();
// If not currently trusted, see if signer is currently listed
if (!masterKey)
masterKey = app.getValidators().getListedKey(signingKey);
auto& validations = app.getValidations();
// masterKey is seated only if validator is trusted or listed
auto const outcome = validations.add(calcNodeID(masterKey.value_or(signingKey)), val);
if (outcome == ValStatus::Current)
{
if (val->isTrusted())
{
if (bypassAccept == BypassAccept::Yes)
{
XRPL_ASSERT(j, "xrpl::handleNewValidation : journal is available");
if (j.has_value())
{
JLOG(j->trace())
<< "Bypassing checkAccept for validation " << val->getLedgerHash();
}
}
else
{
app.getLedgerMaster().checkAccept(hash, seq);
}
}
return;
}
// Ensure that problematic validations from validators we trust are
// logged at the highest possible level.
//
// One might think that we should more than just log: we ought to also
// not relay validations that fail these checks. Alas, and somewhat
// counterintuitively, we *especially* want to forward such validations,
// so that our peers will also observe them and take independent notice of
// such validators, informing their operators.
if (auto const ls = val->isTrusted() ? validations.adaptor().journal().error()
: validations.adaptor().journal().info();
ls.active())
{
auto const id = [&masterKey, &signingKey]() {
auto ret = toBase58(TokenType::NodePublic, signingKey);
if (masterKey && masterKey != signingKey)
ret += ":" + toBase58(TokenType::NodePublic, *masterKey);
return ret;
}();
if (outcome == ValStatus::Conflicting)
{
ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ")
<< id << ": Conflicting validation for " << seq << "!\n["
<< val->getSerializer().slice() << "]";
}
if (outcome == ValStatus::Multiple)
{
ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ")
<< id << ": Multiple validations for " << seq << "/" << hash << "!\n["
<< val->getSerializer().slice() << "]";
}
}
}
} // namespace xrpl