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