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