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