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