rippled
Loading...
Searching...
No Matches
ApplyStateTable.cpp
1#include <xrpl/basics/Log.h>
2#include <xrpl/beast/utility/instrumentation.h>
3#include <xrpl/json/to_string.h>
4#include <xrpl/ledger/detail/ApplyStateTable.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/st.h>
7
8namespace xrpl {
9namespace detail {
10
11void
13{
15 for (auto const& item : items_)
16 {
17 auto const& sle = item.second.second;
18 switch (item.second.first)
19 {
20 case Action::cache:
21 break;
22 case Action::erase:
23 to.rawErase(sle);
24 break;
25 case Action::insert:
26 to.rawInsert(sle);
27 break;
28 case Action::modify:
29 to.rawReplace(sle);
30 break;
31 };
32 }
33}
34
37{
38 std::size_t ret = 0;
39 for (auto& item : items_)
40 {
41 switch (item.second.first)
42 {
43 case Action::erase:
44 case Action::insert:
45 case Action::modify:
46 ++ret;
47 default:
48 break;
49 }
50 }
51 return ret;
52}
53
54void
56 ReadView const& to,
57 std::function<void(
58 uint256 const& key,
59 bool isDelete,
60 std::shared_ptr<SLE const> const& before,
61 std::shared_ptr<SLE const> const& after)> const& func) const
62{
63 for (auto& item : items_)
64 {
65 switch (item.second.first)
66 {
67 case Action::erase:
68 func(item.first, true, to.read(keylet::unchecked(item.first)), item.second.second);
69 break;
70
71 case Action::insert:
72 func(item.first, false, nullptr, item.second.second);
73 break;
74
75 case Action::modify:
76 func(item.first, false, to.read(keylet::unchecked(item.first)), item.second.second);
77 break;
78
79 default:
80 break;
81 }
82 }
83}
84
87 OpenView& to,
88 STTx const& tx,
89 TER ter,
90 std::optional<STAmount> const& deliver,
91 std::optional<uint256 const> const& parentBatchId,
92 bool isDryRun,
94{
95 // Build metadata and insert
96 auto const sTx = std::make_shared<Serializer>();
97 tx.add(*sTx);
99 std::optional<TxMeta> metadata;
100 if (!to.open() || isDryRun)
101 {
102 TxMeta meta(tx.getTransactionID(), to.seq());
103
104 meta.setDeliveredAmount(deliver);
105 meta.setParentBatchID(parentBatchId);
106
107 Mods newMod;
108 for (auto& item : items_)
109 {
110 SField const* type;
111 switch (item.second.first)
112 {
113 default:
114 case Action::cache:
115 continue;
116 case Action::erase:
117 type = &sfDeletedNode;
118 break;
119 case Action::insert:
120 type = &sfCreatedNode;
121 break;
122 case Action::modify:
123 type = &sfModifiedNode;
124 break;
125 }
126 auto const origNode = to.read(keylet::unchecked(item.first));
127 auto curNode = item.second.second;
128 if ((type == &sfModifiedNode) && (*curNode == *origNode))
129 continue;
130 std::uint16_t nodeType =
131 curNode ? curNode->getFieldU16(sfLedgerEntryType) : origNode->getFieldU16(sfLedgerEntryType);
132 meta.setAffectedNode(item.first, *type, nodeType);
133 if (type == &sfDeletedNode)
134 {
135 XRPL_ASSERT(
136 origNode && curNode,
137 "xrpl::detail::ApplyStateTable::apply : valid nodes for "
138 "deletion");
139 threadOwners(to, meta, origNode, newMod, j);
140
141 STObject prevs(sfPreviousFields);
142 for (auto const& obj : *origNode)
143 {
144 // go through the original node for
145 // modified fields saved on modification
146 if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry(obj))
147 prevs.emplace_back(obj);
148 }
149
150 if (!prevs.empty())
151 meta.getAffectedNode(item.first).emplace_back(std::move(prevs));
152
153 STObject finals(sfFinalFields);
154 for (auto const& obj : *curNode)
155 {
156 // go through the final node for final fields
157 if (obj.getFName().shouldMeta(SField::sMD_Always | SField::sMD_DeleteFinal))
158 finals.emplace_back(obj);
159 }
160
161 if (!finals.empty())
162 meta.getAffectedNode(item.first).emplace_back(std::move(finals));
163 }
164 else if (type == &sfModifiedNode)
165 {
166 XRPL_ASSERT(
167 curNode && origNode,
168 "xrpl::detail::ApplyStateTable::apply : valid nodes for "
169 "modification");
170
171 if (curNode->isThreadedType(to.rules())) // thread transaction to node
172 // item modified
173 threadItem(meta, curNode);
174
175 STObject prevs(sfPreviousFields);
176 for (auto const& obj : *origNode)
177 {
178 // search the original node for values saved on modify
179 if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry(obj))
180 prevs.emplace_back(obj);
181 }
182
183 if (!prevs.empty())
184 meta.getAffectedNode(item.first).emplace_back(std::move(prevs));
185
186 STObject finals(sfFinalFields);
187 for (auto const& obj : *curNode)
188 {
189 // search the final node for values saved always
190 if (obj.getFName().shouldMeta(SField::sMD_Always | SField::sMD_ChangeNew))
191 finals.emplace_back(obj);
192 }
193
194 if (!finals.empty())
195 meta.getAffectedNode(item.first).emplace_back(std::move(finals));
196 }
197 else if (type == &sfCreatedNode) // if created, thread to owner(s)
198 {
199 XRPL_ASSERT(
200 curNode && !origNode,
201 "xrpl::detail::ApplyStateTable::apply : valid nodes for "
202 "creation");
203 threadOwners(to, meta, curNode, newMod, j);
204
205 if (curNode->isThreadedType(to.rules())) // always thread to self
206 threadItem(meta, curNode);
207
208 STObject news(sfNewFields);
209 for (auto const& obj : *curNode)
210 {
211 // save non-default values
212 if (!obj.isDefault() && obj.getFName().shouldMeta(SField::sMD_Create | SField::sMD_Always))
213 news.emplace_back(obj);
214 }
215
216 if (!news.empty())
217 meta.getAffectedNode(item.first).emplace_back(std::move(news));
218 }
219 else
220 {
221 // LCOV_EXCL_START
222 UNREACHABLE(
223 "xrpl::detail::ApplyStateTable::apply : unsupported "
224 "operation type");
225 // LCOV_EXCL_STOP
226 }
227 }
228
229 if (!isDryRun)
230 {
231 // add any new modified nodes to the modification set
232 for (auto const& mod : newMod)
233 to.rawReplace(mod.second);
234 }
235
237 meta.addRaw(*sMeta, ter, to.txCount());
238
239 // VFALCO For diagnostics do we want to show
240 // metadata even when the base view is open?
241 JLOG(j.trace()) << "metadata " << meta.getJson(JsonOptions::none);
242
243 metadata = meta;
244 }
245
246 if (!isDryRun)
247 {
248 to.rawTxInsert(tx.getTransactionID(), sTx, sMeta);
249 apply(to);
250 }
251 return metadata;
252}
253
254//---
255
256bool
257ApplyStateTable::exists(ReadView const& base, Keylet const& k) const
258{
259 auto const iter = items_.find(k.key);
260 if (iter == items_.end())
261 return base.exists(k);
262 auto const& item = iter->second;
263 auto const& sle = item.second;
264 switch (item.first)
265 {
266 case Action::erase:
267 return false;
268 case Action::cache:
269 case Action::insert:
270 case Action::modify:
271 break;
272 }
273 if (!k.check(*sle))
274 return false;
275 return true;
276}
277
278auto
279ApplyStateTable::succ(ReadView const& base, key_type const& key, std::optional<key_type> const& last) const
281{
282 std::optional<key_type> next = key;
283 items_t::const_iterator iter;
284 // Find base successor that is
285 // not also deleted in our list
286 do
287 {
288 next = base.succ(*next, last);
289 if (!next)
290 break;
291 iter = items_.find(*next);
292 } while (iter != items_.end() && iter->second.first == Action::erase);
293 // Find non-deleted successor in our list
294 for (iter = items_.upper_bound(key); iter != items_.end(); ++iter)
295 {
296 if (iter->second.first != Action::erase)
297 {
298 // Found both, return the lower key
299 if (!next || next > iter->first)
300 next = iter->first;
301 break;
302 }
303 }
304 // Nothing in our list, return
305 // what we got from the parent.
306 if (last && next >= last)
307 return std::nullopt;
308 return next;
309}
310
312ApplyStateTable::read(ReadView const& base, Keylet const& k) const
313{
314 auto const iter = items_.find(k.key);
315 if (iter == items_.end())
316 return base.read(k);
317 auto const& item = iter->second;
318 auto const& sle = item.second;
319 switch (item.first)
320 {
321 case Action::erase:
322 return nullptr;
323 case Action::cache:
324 case Action::insert:
325 case Action::modify:
326 break;
327 };
328 if (!k.check(*sle))
329 return nullptr;
330 return sle;
331}
332
335{
336 auto iter = items_.lower_bound(k.key);
337 if (iter == items_.end() || iter->first != k.key)
338 {
339 auto const sle = base.read(k);
340 if (!sle)
341 return nullptr;
342 // Make our own copy
343 using namespace std;
344 iter = items_.emplace_hint(
345 iter,
346 piecewise_construct,
347 forward_as_tuple(sle->key()),
348 forward_as_tuple(Action::cache, make_shared<SLE>(*sle)));
349 return iter->second.second;
350 }
351 auto const& item = iter->second;
352 auto const& sle = item.second;
353 switch (item.first)
354 {
355 case Action::erase:
356 return nullptr;
357 case Action::cache:
358 case Action::insert:
359 case Action::modify:
360 break;
361 };
362 if (!k.check(*sle))
363 return nullptr;
364 return sle;
365}
366
367void
369{
370 auto const iter = items_.find(sle->key());
371 if (iter == items_.end())
372 LogicError("ApplyStateTable::erase: missing key");
373 auto& item = iter->second;
374 if (item.second != sle)
375 LogicError("ApplyStateTable::erase: unknown SLE");
376 switch (item.first)
377 {
378 case Action::erase:
379 LogicError("ApplyStateTable::erase: double erase");
380 break;
381 case Action::insert:
382 items_.erase(iter);
383 break;
384 case Action::cache:
385 case Action::modify:
386 item.first = Action::erase;
387 break;
388 }
389}
390
391void
393{
394 using namespace std;
395 auto const result =
396 items_.emplace(piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::erase, sle));
397 if (result.second)
398 return;
399 auto& item = result.first->second;
400 switch (item.first)
401 {
402 case Action::erase:
403 LogicError("ApplyStateTable::rawErase: double erase");
404 break;
405 case Action::insert:
406 items_.erase(result.first);
407 break;
408 case Action::cache:
409 case Action::modify:
410 item.first = Action::erase;
411 item.second = sle;
412 break;
413 }
414}
415
416void
418{
419 auto const iter = items_.lower_bound(sle->key());
420 if (iter == items_.end() || iter->first != sle->key())
421 {
422 using namespace std;
424 iter, piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::insert, sle));
425 return;
426 }
427 auto& item = iter->second;
428 switch (item.first)
429 {
430 case Action::cache:
431 LogicError("ApplyStateTable::insert: already cached");
432 case Action::insert:
433 LogicError("ApplyStateTable::insert: already inserted");
434 case Action::modify:
435 LogicError("ApplyStateTable::insert: already modified");
436 case Action::erase:
437 break;
438 }
439 item.first = Action::modify;
440 item.second = sle;
441}
442
443void
445{
446 auto const iter = items_.lower_bound(sle->key());
447 if (iter == items_.end() || iter->first != sle->key())
448 {
449 using namespace std;
451 iter, piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::modify, sle));
452 return;
453 }
454 auto& item = iter->second;
455 switch (item.first)
456 {
457 case Action::erase:
458 LogicError("ApplyStateTable::replace: already erased");
459 case Action::cache:
460 item.first = Action::modify;
461 break;
462 case Action::insert:
463 case Action::modify:
464 break;
465 }
466 item.second = sle;
467}
468
469void
471{
472 auto const iter = items_.find(sle->key());
473 if (iter == items_.end())
474 LogicError("ApplyStateTable::update: missing key");
475 auto& item = iter->second;
476 if (item.second != sle)
477 LogicError("ApplyStateTable::update: unknown SLE");
478 switch (item.first)
479 {
480 case Action::erase:
481 LogicError("ApplyStateTable::update: erased");
482 break;
483 case Action::cache:
484 item.first = Action::modify;
485 break;
486 case Action::insert:
487 case Action::modify:
488 break;
489 };
490}
491
492void
494{
495 dropsDestroyed_ += fee;
496}
497
498//------------------------------------------------------------------------------
499
500// Insert this transaction to the SLE's threading list
501void
503{
504 key_type prevTxID;
505 LedgerIndex prevLgrID;
506
507 if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID))
508 return;
509
510 if (!prevTxID.isZero())
511 {
512 auto& node = meta.getAffectedNode(sle, sfModifiedNode);
513
514 if (node.getFieldIndex(sfPreviousTxnID) == -1)
515 {
516 XRPL_ASSERT(
517 node.getFieldIndex(sfPreviousTxnLgrSeq) == -1,
518 "xrpl::ApplyStateTable::threadItem : previous ledger is not "
519 "set");
520 node.setFieldH256(sfPreviousTxnID, prevTxID);
521 node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID);
522 }
523
524 XRPL_ASSERT(
525 node.getFieldH256(sfPreviousTxnID) == prevTxID,
526 "xrpl::ApplyStateTable::threadItem : previous transaction is a "
527 "match");
528 XRPL_ASSERT(
529 node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID,
530 "xrpl::ApplyStateTable::threadItem : previous ledger is a match");
531 }
532}
533
536{
537 {
538 auto miter = mods.find(key);
539 if (miter != mods.end())
540 {
541 XRPL_ASSERT(miter->second, "xrpl::ApplyStateTable::getForMod : non-null result");
542 return miter->second;
543 }
544 }
545 {
546 auto iter = items_.find(key);
547 if (iter != items_.end())
548 {
549 auto const& item = iter->second;
550 if (item.first == Action::erase)
551 {
552 // The Destination of an Escrow or a PayChannel may have been
553 // deleted. In that case the account we're threading to will
554 // not be found and it is appropriate to return a nullptr.
555 JLOG(j.warn()) << "Trying to thread to deleted node";
556 return nullptr;
557 }
558 if (item.first != Action::cache)
559 return item.second;
560
561 // If it's only cached, then the node is being modified only by
562 // metadata; fall through and track it in the mods table.
563 }
564 }
565 auto c = base.read(keylet::unchecked(key));
566 if (!c)
567 {
568 // The Destination of an Escrow or a PayChannel may have been
569 // deleted. In that case the account we're threading to will
570 // not be found and it is appropriate to return a nullptr.
571 JLOG(j.warn()) << "ApplyStateTable::getForMod: key not found";
572 return nullptr;
573 }
574 auto sle = std::make_shared<SLE>(*c);
575 mods.emplace(key, sle);
576 return sle;
577}
578
579void
581{
582 auto const sle = getForMod(base, keylet::account(to).key, mods, j);
583 if (!sle)
584 {
585 // The Destination of an Escrow or PayChannel may have been deleted.
586 // In that case the account we are threading to will not be found.
587 // So this logging is just a warning.
588 JLOG(j.warn()) << "Threading to non-existent account: " << toBase58(to);
589 return;
590 }
591 // threadItem only applied to AccountRoot
592 XRPL_ASSERT(sle->isThreadedType(base.rules()), "xrpl::ApplyStateTable::threadTx : SLE is threaded");
593 threadItem(meta, sle);
594}
595
596void
598 ReadView const& base,
599 TxMeta& meta,
601 Mods& mods,
603{
604 LedgerEntryType const ledgerType{sle->getType()};
605 switch (ledgerType)
606 {
607 case ltACCOUNT_ROOT: {
608 // Nothing to do
609 break;
610 }
611 case ltRIPPLE_STATE: {
612 threadTx(base, meta, (*sle)[sfLowLimit].getIssuer(), mods, j);
613 threadTx(base, meta, (*sle)[sfHighLimit].getIssuer(), mods, j);
614 break;
615 }
616 default: {
617 // If sfAccount is present, thread to that account
618 if (auto const optSleAcct{(*sle)[~sfAccount]})
619 threadTx(base, meta, *optSleAcct, mods, j);
620
621 // If sfDestination is present, thread to that account
622 if (auto const optSleDest{(*sle)[~sfDestination]})
623 threadTx(base, meta, *optSleDest, mods, j);
624 }
625 }
626}
627
628} // namespace detail
629} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
Stream warn() const
Definition Journal.h:312
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
std::size_t txCount() const
Return the number of tx inserted since creation.
Definition OpenView.cpp:95
void rawReplace(std::shared_ptr< SLE > const &sle) override
Unconditionally replace a state item.
Definition OpenView.cpp:213
bool open() const override
Returns true if this reflects an open ledger.
Definition OpenView.h:160
void rawTxInsert(key_type const &key, std::shared_ptr< Serializer const > const &txn, std::shared_ptr< Serializer const > const &metaData) override
Add a transaction to the tx map.
Definition OpenView.cpp:229
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition OpenView.cpp:141
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:123
Interface for ledger entry changes.
Definition RawView.h:14
virtual void rawReplace(std::shared_ptr< SLE > const &sle)=0
Unconditionally replace a state item.
virtual void rawErase(std::shared_ptr< SLE > const &sle)=0
Delete an existing state item.
virtual void rawInsert(std::shared_ptr< SLE > const &sle)=0
Unconditionally insert a state item.
virtual void rawDestroyXRP(XRPAmount const &fee)=0
Destroy XRP.
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:97
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Identifies fields.
Definition SField.h:126
@ sMD_DeleteFinal
Definition SField.h:132
@ sMD_ChangeOrig
Definition SField.h:130
@ sMD_ChangeNew
Definition SField.h:131
bool empty() const
Definition STObject.h:939
std::size_t emplace_back(Args &&... args)
Definition STObject.h:975
void add(Serializer &s) const override
Definition STObject.cpp:117
uint256 getTransactionID() const
Definition STTx.h:192
void setDeliveredAmount(std::optional< STAmount > const &amount)
Definition TxMeta.h:96
void setAffectedNode(uint256 const &, SField const &type, std::uint16_t nodeType)
Definition TxMeta.cpp:61
uint256 const & getTxID() const
Definition TxMeta.h:22
std::uint32_t getLgrSeq() const
Definition TxMeta.h:27
STObject & getAffectedNode(SLE::ref node, SField const &type)
Definition TxMeta.cpp:146
void addRaw(Serializer &, TER, std::uint32_t index)
Definition TxMeta.cpp:197
void setParentBatchID(std::optional< uint256 > const &id)
Definition TxMeta.h:102
Json::Value getJson(JsonOptions p) const
Definition TxMeta.h:59
bool isZero() const
Definition base_uint.h:508
void destroyXRP(XRPAmount const &fee)
bool exists(ReadView const &base, Keylet const &k) const
void visit(ReadView const &base, std::function< void(uint256 const &key, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)> const &func) const
std::shared_ptr< SLE > peek(ReadView const &base, Keylet const &k)
void replace(ReadView const &base, std::shared_ptr< SLE > const &sle)
static void threadItem(TxMeta &meta, std::shared_ptr< SLE > const &to)
std::optional< key_type > succ(ReadView const &base, key_type const &key, std::optional< key_type > const &last) const
void rawErase(ReadView const &base, std::shared_ptr< SLE > const &sle)
void threadTx(ReadView const &base, TxMeta &meta, AccountID const &to, Mods &mods, beast::Journal j)
void threadOwners(ReadView const &base, TxMeta &meta, std::shared_ptr< SLE const > const &sle, Mods &mods, beast::Journal j)
std::shared_ptr< SLE > getForMod(ReadView const &base, key_type const &key, Mods &mods, beast::Journal j)
std::shared_ptr< SLE const > read(ReadView const &base, Keylet const &k) const
void apply(RawView &to) const
void update(ReadView const &base, std::shared_ptr< SLE > const &sle)
T emplace_hint(T... args)
T emplace(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
T is_same_v
T lower_bound(T... args)
STL namespace.
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:319
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3436
LedgerEntryType
Identifiers for on-ledger objects.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
uint256 key
Definition Keylet.h:20
bool check(STLedgerEntry const &) const
Returns true if the SLE matches the type.
Definition Keylet.cpp:9