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 = curNode ? curNode->getFieldU16(sfLedgerEntryType)
131 : 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) &&
147 !curNode->hasMatchingEntry(obj))
148 prevs.emplace_back(obj);
149 }
150
151 if (!prevs.empty())
152 meta.getAffectedNode(item.first).emplace_back(std::move(prevs));
153
154 STObject finals(sfFinalFields);
155 for (auto const& obj : *curNode)
156 {
157 // go through the final node for final fields
158 if (obj.getFName().shouldMeta(SField::sMD_Always | SField::sMD_DeleteFinal))
159 finals.emplace_back(obj);
160 }
161
162 if (!finals.empty())
163 meta.getAffectedNode(item.first).emplace_back(std::move(finals));
164 }
165 else if (type == &sfModifiedNode)
166 {
167 XRPL_ASSERT(
168 curNode && origNode,
169 "xrpl::detail::ApplyStateTable::apply : valid nodes for "
170 "modification");
171
172 if (curNode->isThreadedType(to.rules())) // thread transaction to node
173 // item modified
174 threadItem(meta, curNode);
175
176 STObject prevs(sfPreviousFields);
177 for (auto const& obj : *origNode)
178 {
179 // search the original node for values saved on modify
180 if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) &&
181 !curNode->hasMatchingEntry(obj))
182 prevs.emplace_back(obj);
183 }
184
185 if (!prevs.empty())
186 meta.getAffectedNode(item.first).emplace_back(std::move(prevs));
187
188 STObject finals(sfFinalFields);
189 for (auto const& obj : *curNode)
190 {
191 // search the final node for values saved always
192 if (obj.getFName().shouldMeta(SField::sMD_Always | SField::sMD_ChangeNew))
193 finals.emplace_back(obj);
194 }
195
196 if (!finals.empty())
197 meta.getAffectedNode(item.first).emplace_back(std::move(finals));
198 }
199 else if (type == &sfCreatedNode) // if created, thread to owner(s)
200 {
201 XRPL_ASSERT(
202 curNode && !origNode,
203 "xrpl::detail::ApplyStateTable::apply : valid nodes for "
204 "creation");
205 threadOwners(to, meta, curNode, newMod, j);
206
207 if (curNode->isThreadedType(to.rules())) // always thread to self
208 threadItem(meta, curNode);
209
210 STObject news(sfNewFields);
211 for (auto const& obj : *curNode)
212 {
213 // save non-default values
214 if (!obj.isDefault() &&
215 obj.getFName().shouldMeta(SField::sMD_Create | SField::sMD_Always))
216 news.emplace_back(obj);
217 }
218
219 if (!news.empty())
220 meta.getAffectedNode(item.first).emplace_back(std::move(news));
221 }
222 else
223 {
224 // LCOV_EXCL_START
225 UNREACHABLE(
226 "xrpl::detail::ApplyStateTable::apply : unsupported "
227 "operation type");
228 // LCOV_EXCL_STOP
229 }
230 }
231
232 if (!isDryRun)
233 {
234 // add any new modified nodes to the modification set
235 for (auto const& mod : newMod)
236 to.rawReplace(mod.second);
237 }
238
240 meta.addRaw(*sMeta, ter, to.txCount());
241
242 // VFALCO For diagnostics do we want to show
243 // metadata even when the base view is open?
244 JLOG(j.trace()) << "metadata " << meta.getJson(JsonOptions::none);
245
246 metadata = meta;
247 }
248
249 if (!isDryRun)
250 {
251 to.rawTxInsert(tx.getTransactionID(), sTx, sMeta);
252 apply(to);
253 }
254 return metadata;
255}
256
257//---
258
259bool
260ApplyStateTable::exists(ReadView const& base, Keylet const& k) const
261{
262 auto const iter = items_.find(k.key);
263 if (iter == items_.end())
264 return base.exists(k);
265 auto const& item = iter->second;
266 auto const& sle = item.second;
267 switch (item.first)
268 {
269 case Action::erase:
270 return false;
271 case Action::cache:
272 case Action::insert:
273 case Action::modify:
274 break;
275 }
276 if (!k.check(*sle))
277 return false;
278 return true;
279}
280
281auto
283 ReadView const& base,
284 key_type const& key,
286{
287 std::optional<key_type> next = key;
288 items_t::const_iterator iter;
289 // Find base successor that is
290 // not also deleted in our list
291 do
292 {
293 next = base.succ(*next, last);
294 if (!next)
295 break;
296 iter = items_.find(*next);
297 } while (iter != items_.end() && iter->second.first == Action::erase);
298 // Find non-deleted successor in our list
299 for (iter = items_.upper_bound(key); iter != items_.end(); ++iter)
300 {
301 if (iter->second.first != Action::erase)
302 {
303 // Found both, return the lower key
304 if (!next || next > iter->first)
305 next = iter->first;
306 break;
307 }
308 }
309 // Nothing in our list, return
310 // what we got from the parent.
311 if (last && next >= last)
312 return std::nullopt;
313 return next;
314}
315
317ApplyStateTable::read(ReadView const& base, Keylet const& k) const
318{
319 auto const iter = items_.find(k.key);
320 if (iter == items_.end())
321 return base.read(k);
322 auto const& item = iter->second;
323 auto const& sle = item.second;
324 switch (item.first)
325 {
326 case Action::erase:
327 return nullptr;
328 case Action::cache:
329 case Action::insert:
330 case Action::modify:
331 break;
332 };
333 if (!k.check(*sle))
334 return nullptr;
335 return sle;
336}
337
340{
341 auto iter = items_.lower_bound(k.key);
342 if (iter == items_.end() || iter->first != k.key)
343 {
344 auto const sle = base.read(k);
345 if (!sle)
346 return nullptr;
347 // Make our own copy
348 using namespace std;
349 iter = items_.emplace_hint(
350 iter,
351 piecewise_construct,
352 forward_as_tuple(sle->key()),
353 forward_as_tuple(Action::cache, make_shared<SLE>(*sle)));
354 return iter->second.second;
355 }
356 auto const& item = iter->second;
357 auto const& sle = item.second;
358 switch (item.first)
359 {
360 case Action::erase:
361 return nullptr;
362 case Action::cache:
363 case Action::insert:
364 case Action::modify:
365 break;
366 };
367 if (!k.check(*sle))
368 return nullptr;
369 return sle;
370}
371
372void
374{
375 auto const iter = items_.find(sle->key());
376 if (iter == items_.end())
377 LogicError("ApplyStateTable::erase: missing key");
378 auto& item = iter->second;
379 if (item.second != sle)
380 LogicError("ApplyStateTable::erase: unknown SLE");
381 switch (item.first)
382 {
383 case Action::erase:
384 LogicError("ApplyStateTable::erase: double erase");
385 break;
386 case Action::insert:
387 items_.erase(iter);
388 break;
389 case Action::cache:
390 case Action::modify:
391 item.first = Action::erase;
392 break;
393 }
394}
395
396void
398{
399 using namespace std;
400 auto const result = items_.emplace(
401 piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::erase, sle));
402 if (result.second)
403 return;
404 auto& item = result.first->second;
405 switch (item.first)
406 {
407 case Action::erase:
408 LogicError("ApplyStateTable::rawErase: double erase");
409 break;
410 case Action::insert:
411 items_.erase(result.first);
412 break;
413 case Action::cache:
414 case Action::modify:
415 item.first = Action::erase;
416 item.second = sle;
417 break;
418 }
419}
420
421void
423{
424 auto const iter = items_.lower_bound(sle->key());
425 if (iter == items_.end() || iter->first != sle->key())
426 {
427 using namespace std;
429 iter,
430 piecewise_construct,
431 forward_as_tuple(sle->key()),
432 forward_as_tuple(Action::insert, sle));
433 return;
434 }
435 auto& item = iter->second;
436 switch (item.first)
437 {
438 case Action::cache:
439 LogicError("ApplyStateTable::insert: already cached");
440 case Action::insert:
441 LogicError("ApplyStateTable::insert: already inserted");
442 case Action::modify:
443 LogicError("ApplyStateTable::insert: already modified");
444 case Action::erase:
445 break;
446 }
447 item.first = Action::modify;
448 item.second = sle;
449}
450
451void
453{
454 auto const iter = items_.lower_bound(sle->key());
455 if (iter == items_.end() || iter->first != sle->key())
456 {
457 using namespace std;
459 iter,
460 piecewise_construct,
461 forward_as_tuple(sle->key()),
462 forward_as_tuple(Action::modify, sle));
463 return;
464 }
465 auto& item = iter->second;
466 switch (item.first)
467 {
468 case Action::erase:
469 LogicError("ApplyStateTable::replace: already erased");
470 case Action::cache:
471 item.first = Action::modify;
472 break;
473 case Action::insert:
474 case Action::modify:
475 break;
476 }
477 item.second = sle;
478}
479
480void
482{
483 auto const iter = items_.find(sle->key());
484 if (iter == items_.end())
485 LogicError("ApplyStateTable::update: missing key");
486 auto& item = iter->second;
487 if (item.second != sle)
488 LogicError("ApplyStateTable::update: unknown SLE");
489 switch (item.first)
490 {
491 case Action::erase:
492 LogicError("ApplyStateTable::update: erased");
493 break;
494 case Action::cache:
495 item.first = Action::modify;
496 break;
497 case Action::insert:
498 case Action::modify:
499 break;
500 };
501}
502
503void
505{
506 dropsDestroyed_ += fee;
507}
508
509//------------------------------------------------------------------------------
510
511// Insert this transaction to the SLE's threading list
512void
514{
515 key_type prevTxID;
516 LedgerIndex prevLgrID;
517
518 if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID))
519 return;
520
521 if (!prevTxID.isZero())
522 {
523 auto& node = meta.getAffectedNode(sle, sfModifiedNode);
524
525 if (node.getFieldIndex(sfPreviousTxnID) == -1)
526 {
527 XRPL_ASSERT(
528 node.getFieldIndex(sfPreviousTxnLgrSeq) == -1,
529 "xrpl::ApplyStateTable::threadItem : previous ledger is not "
530 "set");
531 node.setFieldH256(sfPreviousTxnID, prevTxID);
532 node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID);
533 }
534
535 XRPL_ASSERT(
536 node.getFieldH256(sfPreviousTxnID) == prevTxID,
537 "xrpl::ApplyStateTable::threadItem : previous transaction is a "
538 "match");
539 XRPL_ASSERT(
540 node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID,
541 "xrpl::ApplyStateTable::threadItem : previous ledger is a match");
542 }
543}
544
547{
548 {
549 auto miter = mods.find(key);
550 if (miter != mods.end())
551 {
552 XRPL_ASSERT(miter->second, "xrpl::ApplyStateTable::getForMod : non-null result");
553 return miter->second;
554 }
555 }
556 {
557 auto iter = items_.find(key);
558 if (iter != items_.end())
559 {
560 auto const& item = iter->second;
561 if (item.first == Action::erase)
562 {
563 // The Destination of an Escrow or a PayChannel may have been
564 // deleted. In that case the account we're threading to will
565 // not be found and it is appropriate to return a nullptr.
566 JLOG(j.warn()) << "Trying to thread to deleted node";
567 return nullptr;
568 }
569 if (item.first != Action::cache)
570 return item.second;
571
572 // If it's only cached, then the node is being modified only by
573 // metadata; fall through and track it in the mods table.
574 }
575 }
576 auto c = base.read(keylet::unchecked(key));
577 if (!c)
578 {
579 // The Destination of an Escrow or a PayChannel may have been
580 // deleted. In that case the account we're threading to will
581 // not be found and it is appropriate to return a nullptr.
582 JLOG(j.warn()) << "ApplyStateTable::getForMod: key not found";
583 return nullptr;
584 }
585 auto sle = std::make_shared<SLE>(*c);
586 mods.emplace(key, sle);
587 return sle;
588}
589
590void
592 ReadView const& base,
593 TxMeta& meta,
594 AccountID const& to,
595 Mods& mods,
597{
598 auto const sle = getForMod(base, keylet::account(to).key, mods, j);
599 if (!sle)
600 {
601 // The Destination of an Escrow or PayChannel may have been deleted.
602 // In that case the account we are threading to will not be found.
603 // So this logging is just a warning.
604 JLOG(j.warn()) << "Threading to non-existent account: " << toBase58(to);
605 return;
606 }
607 // threadItem only applied to AccountRoot
608 XRPL_ASSERT(
609 sle->isThreadedType(base.rules()), "xrpl::ApplyStateTable::threadTx : SLE is threaded");
610 threadItem(meta, sle);
611}
612
613void
615 ReadView const& base,
616 TxMeta& meta,
618 Mods& mods,
620{
621 LedgerEntryType const ledgerType{sle->getType()};
622 switch (ledgerType)
623 {
624 case ltACCOUNT_ROOT: {
625 // Nothing to do
626 break;
627 }
628 case ltRIPPLE_STATE: {
629 threadTx(base, meta, (*sle)[sfLowLimit].getIssuer(), mods, j);
630 threadTx(base, meta, (*sle)[sfHighLimit].getIssuer(), mods, j);
631 break;
632 }
633 default: {
634 // If sfAccount is present, thread to that account
635 if (auto const optSleAcct{(*sle)[~sfAccount]})
636 threadTx(base, meta, *optSleAcct, mods, j);
637
638 // If sfDestination is present, thread to that account
639 if (auto const optSleDest{(*sle)[~sfDestination]})
640 threadTx(base, meta, *optSleDest, mods, j);
641 }
642 }
643}
644
645} // namespace detail
646} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
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:103
void rawReplace(std::shared_ptr< SLE > const &sle) override
Unconditionally replace a state item.
Definition OpenView.cpp:222
bool open() const override
Returns true if this reflects an open ledger.
Definition OpenView.h:166
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:238
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition OpenView.cpp:150
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:131
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:950
std::size_t emplace_back(Args &&... args)
Definition STObject.h:986
void add(Serializer &s) const override
Definition STObject.cpp:120
uint256 getTransactionID() const
Definition STTx.h:196
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:147
void addRaw(Serializer &, TER, std::uint32_t index)
Definition TxMeta.cpp:199
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:511
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:330
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
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:3652
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