xrpld
Loading...
Searching...
No Matches
RCLValidations.cpp
1#include <xrpld/app/consensus/RCLValidations.h>
2
3#include <xrpld/app/ledger/InboundLedger.h>
4#include <xrpld/app/ledger/InboundLedgers.h>
5#include <xrpld/app/ledger/LedgerMaster.h>
6#include <xrpld/app/main/Application.h>
7#include <xrpld/app/misc/ValidatorList.h>
8#include <xrpld/consensus/Validations.h>
9#include <xrpld/core/TimeKeeper.h>
10
11#include <xrpl/basics/Log.h>
12#include <xrpl/basics/chrono.h>
13#include <xrpl/beast/utility/instrumentation.h>
14#include <xrpl/core/Job.h>
15#include <xrpl/core/JobQueue.h>
16#include <xrpl/core/PerfLog.h>
17#include <xrpl/protocol/Indexes.h>
18#include <xrpl/protocol/PublicKey.h>
19#include <xrpl/protocol/RippleLedgerHash.h>
20#include <xrpl/protocol/SField.h>
21#include <xrpl/protocol/tokens.h>
22
23#include <algorithm>
24#include <memory>
25#include <optional>
26
27namespace xrpl {
28
30 : ledgerID_{0}, ledgerSeq_{0}, j_{beast::Journal::getNullSink()}
31{
32}
33
37 : ledgerID_{ledger->header().hash}, ledgerSeq_{ledger->seq()}, j_{j}
38{
39 auto const hashIndex = ledger->read(keylet::skip());
40 if (hashIndex)
41 {
42 XRPL_ASSERT(
43 hashIndex->getFieldU32(sfLastLedgerSequence) == (seq() - 1),
44 "xrpl::RCLValidatedLedger::RCLValidatedLedger(Ledger) : valid "
45 "last ledger sequence");
46 ancestors_ = hashIndex->getFieldV256(sfHashes).value();
47 }
48 else
49 {
50 JLOG(j_.warn()) << "Ledger " << ledgerSeq_ << ":" << ledgerID_
51 << " missing recent ancestor hashes";
52 }
53}
54
55auto
57{
58 return seq() - std::min(seq(), static_cast<Seq>(ancestors_.size()));
59}
60
61auto
63{
64 return ledgerSeq_;
65}
66auto
68{
69 return ledgerID_;
70}
71
72auto
74{
75 if (s >= minSeq() && s <= seq())
76 {
77 if (s == seq())
78 return ledgerID_;
79 Seq const diff = seq() - s;
80 return ancestors_[ancestors_.size() - diff];
81 }
82
83 JLOG(j_.warn()) << "Unable to determine hash of ancestor seq=" << s
84 << " from ledger hash=" << ledgerID_ << " seq=" << ledgerSeq_
85 << " (available: " << minSeq() << "-" << seq() << ")";
86 // Default ID that is less than all others
87 return ID{0};
88}
89
90// Return the sequence number of the earliest possible mismatching ancestor
93{
95
96 // Find overlapping interval for known sequence for the ledgers
97 Seq const lower = std::max(a.minSeq(), b.minSeq());
98 Seq const upper = std::min(a.seq(), b.seq());
99
100 Seq curr = upper;
101 while (curr != Seq{0} && a[curr] != b[curr] && curr >= lower)
102 --curr;
103
104 // If the searchable interval mismatches entirely, then we have to
105 // assume the ledgers mismatch starting post genesis ledger
106 return (curr < lower) ? Seq{1} : (curr + Seq{1});
107}
108
112
115{
116 return app_.getTimeKeeper().closeTime();
117}
118
121{
122 using namespace std::chrono_literals;
123 auto ledger = perf::measureDurationAndLog(
124 [&]() { return app_.getLedgerMaster().getLedgerByHash(hash); },
125 "getLedgerByHash",
126 10ms,
127 j_);
128
129 if (!ledger)
130 {
131 JLOG(j_.warn()) << "Need validated ledger for preferred ledger analysis " << hash;
132
133 Application* pApp = &app_;
134
135 app_.getJobQueue().addJob(JtAdvance, "GetConsL2", [pApp, hash, this]() {
136 JLOG(j_.debug()) << "JOB advanceLedger getConsensusLedger2 started";
138 });
139 return std::nullopt;
140 }
141
142 XRPL_ASSERT(
143 !ledger->open() && ledger->isImmutable(),
144 "xrpl::RCLValidationsAdaptor::acquire : valid ledger state");
145 XRPL_ASSERT(
146 ledger->header().hash == hash, "xrpl::RCLValidationsAdaptor::acquire : ledger hash match");
147
148 return RCLValidatedLedger(ledger, j_);
149}
150
151void
153 Application& app,
155 std::string const& source,
156 BypassAccept const bypassAccept,
158{
159 auto const& signingKey = val->getSignerPublic();
160 auto const& hash = val->getLedgerHash();
161 auto const seq = val->getFieldU32(sfLedgerSequence);
162
163 // Ensure validation is marked as trusted if signer currently trusted
164 auto masterKey = app.getValidators().getTrustedKey(signingKey);
165
166 if (!val->isTrusted() && masterKey)
167 val->setTrusted();
168
169 // If not currently trusted, see if signer is currently listed
170 if (!masterKey)
171 masterKey = app.getValidators().getListedKey(signingKey);
172
173 auto& validations = app.getValidations();
174
175 // masterKey is seated only if validator is trusted or listed
176 auto const outcome = validations.add(calcNodeID(masterKey.value_or(signingKey)), val);
177
178 if (outcome == ValStatus::Current)
179 {
180 if (val->isTrusted())
181 {
182 if (bypassAccept == BypassAccept::Yes)
183 {
184 XRPL_ASSERT(j, "xrpl::handleNewValidation : journal is available");
185 if (j.has_value())
186 {
187 JLOG(j->trace())
188 << "Bypassing checkAccept for validation " << val->getLedgerHash();
189 }
190 }
191 else
192 {
193 app.getLedgerMaster().checkAccept(hash, seq);
194 }
195 }
196 return;
197 }
198
199 // Ensure that problematic validations from validators we trust are
200 // logged at the highest possible level.
201 //
202 // One might think that we should more than just log: we ought to also
203 // not relay validations that fail these checks. Alas, and somewhat
204 // counterintuitively, we *especially* want to forward such validations,
205 // so that our peers will also observe them and take independent notice of
206 // such validators, informing their operators.
207 if (auto const ls = val->isTrusted() ? validations.adaptor().journal().error()
208 : validations.adaptor().journal().info();
209 ls.active())
210 {
211 auto const id = [&masterKey, &signingKey]() {
212 auto ret = toBase58(TokenType::NodePublic, signingKey);
213
214 if (masterKey && masterKey != signingKey)
215 ret += ":" + toBase58(TokenType::NodePublic, *masterKey);
216
217 return ret;
218 }();
219
220 if (outcome == ValStatus::Conflicting)
221 {
222 ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ")
223 << id << ": Conflicting validation for " << seq << "!\n["
224 << val->getSerializer().slice() << "]";
225 }
226
227 if (outcome == ValStatus::Multiple)
228 {
229 ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ")
230 << id << ": Multiple validations for " << seq << "/" << hash << "!\n["
231 << val->getSerializer().slice() << "]";
232 }
233 }
234}
235
236} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
virtual void acquireAsync(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason reason)=0
void checkAccept(std::shared_ptr< Ledger const > const &ledger)
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
Wraps a ledger instance for use in generic Validations LedgerTrie.
Seq seq() const
The sequence (index) of the ledger.
ID id() const
The ID (hash) of the ledger.
ID operator[](Seq const &s) const
Lookup the ID of the ancestor ledger.
std::vector< uint256 > ancestors_
std::optional< RCLValidatedLedger > acquire(LedgerHash const &id)
Attempt to acquire the ledger with given id from the network.
NetClock::time_point now() const
Current time used to determine if validations are stale.
RCLValidationsAdaptor(Application &app, beast::Journal j)
virtual ValidatorList & getValidators()=0
virtual RCLValidations & getValidations()=0
virtual InboundLedgers & getInboundLedgers()=0
virtual LedgerMaster & getLedgerMaster()=0
ValStatus add(NodeID const &nodeID, Validation const &val)
Add a new validation.
std::optional< PublicKey > getListedKey(PublicKey const &identity) const
Returns listed master public if public key is included on any lists.
std::optional< PublicKey > getTrustedKey(PublicKey const &identity) const
Returns master public key if public key is trusted.
T max(T... args)
T min(T... args)
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:198
auto measureDurationAndLog(Func &&func, std::string const &actionDescription, std::chrono::duration< Rep, Period > maxDelay, beast::Journal const &journal)
Definition PerfLog.h:162
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void handleNewValidation(Application &app, std::shared_ptr< STValidation > const &val, std::string const &source, BypassAccept const bypassAccept, std::optional< beast::Journal > j)
Handle a new validation.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:93
@ Current
This was a new validation and was added.
@ Conflicting
Multiple validations by a validator for different ledgers.
@ Multiple
Multiple validations by a validator for the same ledger.
@ JtAdvance
Definition Job.h:48
uint256 LedgerHash
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
RCLValidatedLedger::Seq mismatch(RCLValidatedLedger const &a, RCLValidatedLedger const &b)
T has_value(T... args)