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