rippled
Loading...
Searching...
No Matches
ApplyStateTable.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/ledger/detail/ApplyStateTable.h>
21#include <xrpl/basics/Log.h>
22#include <xrpl/beast/utility/instrumentation.h>
23#include <xrpl/json/to_string.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/st.h>
26
27namespace ripple {
28namespace detail {
29
30void
32{
34 for (auto const& item : items_)
35 {
36 auto const& sle = item.second.second;
37 switch (item.second.first)
38 {
39 case Action::cache:
40 break;
41 case Action::erase:
42 to.rawErase(sle);
43 break;
44 case Action::insert:
45 to.rawInsert(sle);
46 break;
47 case Action::modify:
48 to.rawReplace(sle);
49 break;
50 };
51 }
52}
53
56{
57 std::size_t ret = 0;
58 for (auto& item : items_)
59 {
60 switch (item.second.first)
61 {
62 case Action::erase:
63 case Action::insert:
64 case Action::modify:
65 ++ret;
66 default:
67 break;
68 }
69 }
70 return ret;
71}
72
73void
75 ReadView const& to,
76 std::function<void(
77 uint256 const& key,
78 bool isDelete,
79 std::shared_ptr<SLE const> const& before,
80 std::shared_ptr<SLE const> const& after)> const& func) const
81{
82 for (auto& item : items_)
83 {
84 switch (item.second.first)
85 {
86 case Action::erase:
87 func(
88 item.first,
89 true,
90 to.read(keylet::unchecked(item.first)),
91 item.second.second);
92 break;
93
94 case Action::insert:
95 func(item.first, false, nullptr, item.second.second);
96 break;
97
98 case Action::modify:
99 func(
100 item.first,
101 false,
102 to.read(keylet::unchecked(item.first)),
103 item.second.second);
104 break;
105
106 default:
107 break;
108 }
109 }
110}
111
114 OpenView& to,
115 STTx const& tx,
116 TER ter,
117 std::optional<STAmount> const& deliver,
118 bool isDryRun,
120{
121 // Build metadata and insert
122 auto const sTx = std::make_shared<Serializer>();
123 tx.add(*sTx);
125 std::optional<TxMeta> metadata;
126 if (!to.open() || isDryRun)
127 {
128 TxMeta meta(tx.getTransactionID(), to.seq());
129 if (deliver)
130 meta.setDeliveredAmount(*deliver);
131 Mods newMod;
132 for (auto& item : items_)
133 {
134 SField const* type;
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 nodeType = curNode
155 ? curNode->getFieldU16(sfLedgerEntryType)
156 : origNode->getFieldU16(sfLedgerEntryType);
157 meta.setAffectedNode(item.first, *type, nodeType);
158 if (type == &sfDeletedNode)
159 {
160 XRPL_ASSERT(
161 origNode && curNode,
162 "ripple::detail::ApplyStateTable::apply : valid nodes for "
163 "deletion");
164 threadOwners(to, meta, origNode, newMod, j);
165
166 STObject prevs(sfPreviousFields);
167 for (auto const& obj : *origNode)
168 {
169 // go through the original node for
170 // modified fields saved on modification
171 if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) &&
172 !curNode->hasMatchingEntry(obj))
173 prevs.emplace_back(obj);
174 }
175
176 if (!prevs.empty())
177 meta.getAffectedNode(item.first)
178 .emplace_back(std::move(prevs));
179
180 STObject finals(sfFinalFields);
181 for (auto const& obj : *curNode)
182 {
183 // go through the final node for final fields
184 if (obj.getFName().shouldMeta(
186 finals.emplace_back(obj);
187 }
188
189 if (!finals.empty())
190 meta.getAffectedNode(item.first)
191 .emplace_back(std::move(finals));
192 }
193 else if (type == &sfModifiedNode)
194 {
195 XRPL_ASSERT(
196 curNode && origNode,
197 "ripple::detail::ApplyStateTable::apply : valid nodes for "
198 "modification");
199
200 if (curNode->isThreadedType(
201 to.rules())) // thread transaction to node
202 // item modified
203 threadItem(meta, curNode);
204
205 STObject prevs(sfPreviousFields);
206 for (auto const& obj : *origNode)
207 {
208 // search the original node for values saved on modify
209 if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) &&
210 !curNode->hasMatchingEntry(obj))
211 prevs.emplace_back(obj);
212 }
213
214 if (!prevs.empty())
215 meta.getAffectedNode(item.first)
216 .emplace_back(std::move(prevs));
217
218 STObject finals(sfFinalFields);
219 for (auto const& obj : *curNode)
220 {
221 // search the final node for values saved always
222 if (obj.getFName().shouldMeta(
224 finals.emplace_back(obj);
225 }
226
227 if (!finals.empty())
228 meta.getAffectedNode(item.first)
229 .emplace_back(std::move(finals));
230 }
231 else if (type == &sfCreatedNode) // if created, thread to owner(s)
232 {
233 XRPL_ASSERT(
234 curNode && !origNode,
235 "ripple::detail::ApplyStateTable::apply : valid nodes for "
236 "creation");
237 threadOwners(to, meta, curNode, newMod, j);
238
239 if (curNode->isThreadedType(
240 to.rules())) // always thread to self
241 threadItem(meta, curNode);
242
243 STObject news(sfNewFields);
244 for (auto const& obj : *curNode)
245 {
246 // save non-default values
247 if (!obj.isDefault() &&
248 obj.getFName().shouldMeta(
250 news.emplace_back(obj);
251 }
252
253 if (!news.empty())
254 meta.getAffectedNode(item.first)
255 .emplace_back(std::move(news));
256 }
257 else
258 {
259 UNREACHABLE(
260 "ripple::detail::ApplyStateTable::apply : unsupported "
261 "operation type");
262 }
263 }
264
265 if (!isDryRun)
266 {
267 // add any new modified nodes to the modification set
268 for (auto const& mod : newMod)
269 to.rawReplace(mod.second);
270 }
271
272 sMeta = std::make_shared<Serializer>();
273 meta.addRaw(*sMeta, ter, to.txCount());
274
275 // VFALCO For diagnostics do we want to show
276 // metadata even when the base view is open?
277 JLOG(j.trace()) << "metadata " << meta.getJson(JsonOptions::none);
278
279 metadata = meta;
280 }
281
282 if (!isDryRun)
283 {
284 to.rawTxInsert(tx.getTransactionID(), sTx, sMeta);
285 apply(to);
286 }
287 return metadata;
288}
289
290//---
291
292bool
293ApplyStateTable::exists(ReadView const& base, Keylet const& k) const
294{
295 auto const iter = items_.find(k.key);
296 if (iter == items_.end())
297 return base.exists(k);
298 auto const& item = iter->second;
299 auto const& sle = item.second;
300 switch (item.first)
301 {
302 case Action::erase:
303 return false;
304 case Action::cache:
305 case Action::insert:
306 case Action::modify:
307 break;
308 }
309 if (!k.check(*sle))
310 return false;
311 return true;
312}
313
314auto
316 ReadView const& base,
317 key_type const& key,
319{
320 std::optional<key_type> next = key;
321 items_t::const_iterator iter;
322 // Find base successor that is
323 // not also deleted in our list
324 do
325 {
326 next = base.succ(*next, last);
327 if (!next)
328 break;
329 iter = items_.find(*next);
330 } while (iter != items_.end() && iter->second.first == Action::erase);
331 // Find non-deleted successor in our list
332 for (iter = items_.upper_bound(key); iter != items_.end(); ++iter)
333 {
334 if (iter->second.first != Action::erase)
335 {
336 // Found both, return the lower key
337 if (!next || next > iter->first)
338 next = iter->first;
339 break;
340 }
341 }
342 // Nothing in our list, return
343 // what we got from the parent.
344 if (last && next >= last)
345 return std::nullopt;
346 return next;
347}
348
350ApplyStateTable::read(ReadView const& base, Keylet const& k) const
351{
352 auto const iter = items_.find(k.key);
353 if (iter == items_.end())
354 return base.read(k);
355 auto const& item = iter->second;
356 auto const& sle = item.second;
357 switch (item.first)
358 {
359 case Action::erase:
360 return nullptr;
361 case Action::cache:
362 case Action::insert:
363 case Action::modify:
364 break;
365 };
366 if (!k.check(*sle))
367 return nullptr;
368 return sle;
369}
370
373{
374 auto iter = items_.lower_bound(k.key);
375 if (iter == items_.end() || iter->first != k.key)
376 {
377 auto const sle = base.read(k);
378 if (!sle)
379 return nullptr;
380 // Make our own copy
381 using namespace std;
382 iter = items_.emplace_hint(
383 iter,
384 piecewise_construct,
385 forward_as_tuple(sle->key()),
386 forward_as_tuple(Action::cache, make_shared<SLE>(*sle)));
387 return iter->second.second;
388 }
389 auto const& item = iter->second;
390 auto const& sle = item.second;
391 switch (item.first)
392 {
393 case Action::erase:
394 return nullptr;
395 case Action::cache:
396 case Action::insert:
397 case Action::modify:
398 break;
399 };
400 if (!k.check(*sle))
401 return nullptr;
402 return sle;
403}
404
405void
407{
408 auto const iter = items_.find(sle->key());
409 if (iter == items_.end())
410 LogicError("ApplyStateTable::erase: missing key");
411 auto& item = iter->second;
412 if (item.second != sle)
413 LogicError("ApplyStateTable::erase: unknown SLE");
414 switch (item.first)
415 {
416 case Action::erase:
417 LogicError("ApplyStateTable::erase: double erase");
418 break;
419 case Action::insert:
420 items_.erase(iter);
421 break;
422 case Action::cache:
423 case Action::modify:
424 item.first = Action::erase;
425 break;
426 }
427}
428
429void
431{
432 using namespace std;
433 auto const result = items_.emplace(
434 piecewise_construct,
435 forward_as_tuple(sle->key()),
436 forward_as_tuple(Action::erase, sle));
437 if (result.second)
438 return;
439 auto& item = result.first->second;
440 switch (item.first)
441 {
442 case Action::erase:
443 LogicError("ApplyStateTable::rawErase: double erase");
444 break;
445 case Action::insert:
446 items_.erase(result.first);
447 break;
448 case Action::cache:
449 case Action::modify:
450 item.first = Action::erase;
451 item.second = sle;
452 break;
453 }
454}
455
456void
458{
459 auto const iter = items_.lower_bound(sle->key());
460 if (iter == items_.end() || iter->first != sle->key())
461 {
462 using namespace std;
464 iter,
465 piecewise_construct,
466 forward_as_tuple(sle->key()),
467 forward_as_tuple(Action::insert, sle));
468 return;
469 }
470 auto& item = iter->second;
471 switch (item.first)
472 {
473 case Action::cache:
474 LogicError("ApplyStateTable::insert: already cached");
475 case Action::insert:
476 LogicError("ApplyStateTable::insert: already inserted");
477 case Action::modify:
478 LogicError("ApplyStateTable::insert: already modified");
479 case Action::erase:
480 break;
481 }
482 item.first = Action::modify;
483 item.second = sle;
484}
485
486void
488{
489 auto const iter = items_.lower_bound(sle->key());
490 if (iter == items_.end() || iter->first != sle->key())
491 {
492 using namespace std;
494 iter,
495 piecewise_construct,
496 forward_as_tuple(sle->key()),
497 forward_as_tuple(Action::modify, sle));
498 return;
499 }
500 auto& item = iter->second;
501 switch (item.first)
502 {
503 case Action::erase:
504 LogicError("ApplyStateTable::replace: already erased");
505 case Action::cache:
506 item.first = Action::modify;
507 break;
508 case Action::insert:
509 case Action::modify:
510 break;
511 }
512 item.second = sle;
513}
514
515void
517{
518 auto const iter = items_.find(sle->key());
519 if (iter == items_.end())
520 LogicError("ApplyStateTable::update: missing key");
521 auto& item = iter->second;
522 if (item.second != sle)
523 LogicError("ApplyStateTable::update: unknown SLE");
524 switch (item.first)
525 {
526 case Action::erase:
527 LogicError("ApplyStateTable::update: erased");
528 break;
529 case Action::cache:
530 item.first = Action::modify;
531 break;
532 case Action::insert:
533 case Action::modify:
534 break;
535 };
536}
537
538void
540{
541 dropsDestroyed_ += fee;
542}
543
544//------------------------------------------------------------------------------
545
546// Insert this transaction to the SLE's threading list
547void
549{
550 key_type prevTxID;
551 LedgerIndex prevLgrID;
552
553 if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID))
554 return;
555
556 if (!prevTxID.isZero())
557 {
558 auto& node = meta.getAffectedNode(sle, sfModifiedNode);
559
560 if (node.getFieldIndex(sfPreviousTxnID) == -1)
561 {
562 XRPL_ASSERT(
563 node.getFieldIndex(sfPreviousTxnLgrSeq) == -1,
564 "ripple::ApplyStateTable::threadItem : previous ledger is not "
565 "set");
566 node.setFieldH256(sfPreviousTxnID, prevTxID);
567 node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID);
568 }
569
570 XRPL_ASSERT(
571 node.getFieldH256(sfPreviousTxnID) == prevTxID,
572 "ripple::ApplyStateTable::threadItem : previous transaction is a "
573 "match");
574 XRPL_ASSERT(
575 node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID,
576 "ripple::ApplyStateTable::threadItem : previous ledger is a match");
577 }
578}
579
582 ReadView const& base,
583 key_type const& key,
584 Mods& mods,
586{
587 {
588 auto miter = mods.find(key);
589 if (miter != mods.end())
590 {
591 XRPL_ASSERT(
592 miter->second,
593 "ripple::ApplyStateTable::getForMod : non-null result");
594 return miter->second;
595 }
596 }
597 {
598 auto iter = items_.find(key);
599 if (iter != items_.end())
600 {
601 auto const& item = iter->second;
602 if (item.first == Action::erase)
603 {
604 // The Destination of an Escrow or a PayChannel may have been
605 // deleted. In that case the account we're threading to will
606 // not be found and it is appropriate to return a nullptr.
607 JLOG(j.warn()) << "Trying to thread to deleted node";
608 return nullptr;
609 }
610 if (item.first != Action::cache)
611 return item.second;
612
613 // If it's only cached, then the node is being modified only by
614 // metadata; fall through and track it in the mods table.
615 }
616 }
617 auto c = base.read(keylet::unchecked(key));
618 if (!c)
619 {
620 // The Destination of an Escrow or a PayChannel may have been
621 // deleted. In that case the account we're threading to will
622 // not be found and it is appropriate to return a nullptr.
623 JLOG(j.warn()) << "ApplyStateTable::getForMod: key not found";
624 return nullptr;
625 }
626 auto sle = std::make_shared<SLE>(*c);
627 mods.emplace(key, sle);
628 return sle;
629}
630
631void
633 ReadView const& base,
634 TxMeta& meta,
635 AccountID const& to,
636 Mods& mods,
638{
639 auto const sle = getForMod(base, keylet::account(to).key, mods, j);
640 if (!sle)
641 {
642 // The Destination of an Escrow or PayChannel may have been deleted.
643 // In that case the account we are threading to will not be found.
644 // So this logging is just a warning.
645 JLOG(j.warn()) << "Threading to non-existent account: " << toBase58(to);
646 return;
647 }
648 // threadItem only applied to AccountRoot
649 XRPL_ASSERT(
650 sle->isThreadedType(base.rules()),
651 "ripple::ApplyStateTable::threadTx : SLE is threaded");
652 threadItem(meta, sle);
653}
654
655void
657 ReadView const& base,
658 TxMeta& meta,
660 Mods& mods,
662{
663 LedgerEntryType const ledgerType{sle->getType()};
664 switch (ledgerType)
665 {
666 case ltACCOUNT_ROOT: {
667 // Nothing to do
668 break;
669 }
670 case ltRIPPLE_STATE: {
671 threadTx(base, meta, (*sle)[sfLowLimit].getIssuer(), mods, j);
672 threadTx(base, meta, (*sle)[sfHighLimit].getIssuer(), mods, j);
673 break;
674 }
675 default: {
676 // If sfAccount is present, thread to that account
677 if (auto const optSleAcct{(*sle)[~sfAccount]})
678 threadTx(base, meta, *optSleAcct, mods, j);
679
680 // Don't thread a check's sfDestination unless the amendment is
681 // enabled
682 if (ledgerType == ltCHECK &&
683 !base.rules().enabled(fixCheckThreading))
684 break;
685
686 // If sfDestination is present, thread to that account
687 if (auto const optSleDest{(*sle)[~sfDestination]})
688 threadTx(base, meta, *optSleDest, mods, j);
689 }
690 }
691}
692
693} // namespace detail
694} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:59
Stream trace() const
Severity stream access functions.
Definition: Journal.h:321
Stream warn() const
Definition: Journal.h:339
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:56
std::size_t txCount() const
Return the number of tx inserted since creation.
Definition: OpenView.cpp:124
Rules const & rules() const override
Returns the tx processing rules.
Definition: OpenView.cpp:152
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition: OpenView.cpp:171
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:261
void rawReplace(std::shared_ptr< SLE > const &sle) override
Unconditionally replace a state item.
Definition: OpenView.cpp:245
bool open() const override
Returns true if this reflects an open ledger.
Definition: OpenView.h:175
Interface for ledger entry changes.
Definition: RawView.h:37
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:55
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:122
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:122
Identifies fields.
Definition: SField.h:144
@ sMD_DeleteFinal
Definition: SField.h:150
@ sMD_ChangeOrig
Definition: SField.h:148
void add(Serializer &s) const override
Definition: STObject.cpp:111
bool empty() const
Definition: STObject.h:923
std::size_t emplace_back(Args &&... args)
Definition: STObject.h:959
uint256 getTransactionID() const
Definition: STTx.h:193
void setAffectedNode(uint256 const &, SField const &type, std::uint16_t nodeType)
Definition: TxMeta.cpp:92
std::uint32_t getLgrSeq() const
Definition: TxMeta.h:58
void setDeliveredAmount(STAmount const &delivered)
Definition: TxMeta.h:111
Json::Value getJson(JsonOptions p) const
Definition: TxMeta.h:90
uint256 const & getTxID() const
Definition: TxMeta.h:53
void addRaw(Serializer &, TER, std::uint32_t index)
Definition: TxMeta.cpp:225
STObject & getAffectedNode(SLE::ref node, SField const &type)
Definition: TxMeta.cpp:178
bool isZero() const
Definition: base_uint.h:539
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 lower_bound(T... args)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:160
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition: Indexes.cpp:344
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:106
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:54
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: Escrow.cpp:89
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
STL namespace.
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
bool check(STLedgerEntry const &) const
Returns true if the SLE matches the type.
Definition: Keylet.cpp:26
uint256 key
Definition: Keylet.h:40