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 UNREACHABLE(
263 "ripple::detail::ApplyStateTable::apply : unsupported "
264 "operation type");
265 }
266 }
267
268 if (!isDryRun)
269 {
270 // add any new modified nodes to the modification set
271 for (auto const& mod : newMod)
272 to.rawReplace(mod.second);
273 }
274
276 meta.addRaw(*sMeta, ter, to.txCount());
277
278 // VFALCO For diagnostics do we want to show
279 // metadata even when the base view is open?
280 JLOG(j.trace()) << "metadata " << meta.getJson(JsonOptions::none);
281
282 metadata = meta;
283 }
284
285 if (!isDryRun)
286 {
287 to.rawTxInsert(tx.getTransactionID(), sTx, sMeta);
288 apply(to);
289 }
290 return metadata;
291}
292
293//---
294
295bool
296ApplyStateTable::exists(ReadView const& base, Keylet const& k) const
297{
298 auto const iter = items_.find(k.key);
299 if (iter == items_.end())
300 return base.exists(k);
301 auto const& item = iter->second;
302 auto const& sle = item.second;
303 switch (item.first)
304 {
305 case Action::erase:
306 return false;
307 case Action::cache:
308 case Action::insert:
309 case Action::modify:
310 break;
311 }
312 if (!k.check(*sle))
313 return false;
314 return true;
315}
316
317auto
319 ReadView const& base,
320 key_type const& key,
322{
323 std::optional<key_type> next = key;
324 items_t::const_iterator iter;
325 // Find base successor that is
326 // not also deleted in our list
327 do
328 {
329 next = base.succ(*next, last);
330 if (!next)
331 break;
332 iter = items_.find(*next);
333 } while (iter != items_.end() && iter->second.first == Action::erase);
334 // Find non-deleted successor in our list
335 for (iter = items_.upper_bound(key); iter != items_.end(); ++iter)
336 {
337 if (iter->second.first != Action::erase)
338 {
339 // Found both, return the lower key
340 if (!next || next > iter->first)
341 next = iter->first;
342 break;
343 }
344 }
345 // Nothing in our list, return
346 // what we got from the parent.
347 if (last && next >= last)
348 return std::nullopt;
349 return next;
350}
351
353ApplyStateTable::read(ReadView const& base, Keylet const& k) const
354{
355 auto const iter = items_.find(k.key);
356 if (iter == items_.end())
357 return base.read(k);
358 auto const& item = iter->second;
359 auto const& sle = item.second;
360 switch (item.first)
361 {
362 case Action::erase:
363 return nullptr;
364 case Action::cache:
365 case Action::insert:
366 case Action::modify:
367 break;
368 };
369 if (!k.check(*sle))
370 return nullptr;
371 return sle;
372}
373
376{
377 auto iter = items_.lower_bound(k.key);
378 if (iter == items_.end() || iter->first != k.key)
379 {
380 auto const sle = base.read(k);
381 if (!sle)
382 return nullptr;
383 // Make our own copy
384 using namespace std;
385 iter = items_.emplace_hint(
386 iter,
387 piecewise_construct,
388 forward_as_tuple(sle->key()),
389 forward_as_tuple(Action::cache, make_shared<SLE>(*sle)));
390 return iter->second.second;
391 }
392 auto const& item = iter->second;
393 auto const& sle = item.second;
394 switch (item.first)
395 {
396 case Action::erase:
397 return nullptr;
398 case Action::cache:
399 case Action::insert:
400 case Action::modify:
401 break;
402 };
403 if (!k.check(*sle))
404 return nullptr;
405 return sle;
406}
407
408void
410{
411 auto const iter = items_.find(sle->key());
412 if (iter == items_.end())
413 LogicError("ApplyStateTable::erase: missing key");
414 auto& item = iter->second;
415 if (item.second != sle)
416 LogicError("ApplyStateTable::erase: unknown SLE");
417 switch (item.first)
418 {
419 case Action::erase:
420 LogicError("ApplyStateTable::erase: double erase");
421 break;
422 case Action::insert:
423 items_.erase(iter);
424 break;
425 case Action::cache:
426 case Action::modify:
427 item.first = Action::erase;
428 break;
429 }
430}
431
432void
434{
435 using namespace std;
436 auto const result = items_.emplace(
437 piecewise_construct,
438 forward_as_tuple(sle->key()),
439 forward_as_tuple(Action::erase, sle));
440 if (result.second)
441 return;
442 auto& item = result.first->second;
443 switch (item.first)
444 {
445 case Action::erase:
446 LogicError("ApplyStateTable::rawErase: double erase");
447 break;
448 case Action::insert:
449 items_.erase(result.first);
450 break;
451 case Action::cache:
452 case Action::modify:
453 item.first = Action::erase;
454 item.second = sle;
455 break;
456 }
457}
458
459void
461{
462 auto const iter = items_.lower_bound(sle->key());
463 if (iter == items_.end() || iter->first != sle->key())
464 {
465 using namespace std;
467 iter,
468 piecewise_construct,
469 forward_as_tuple(sle->key()),
470 forward_as_tuple(Action::insert, sle));
471 return;
472 }
473 auto& item = iter->second;
474 switch (item.first)
475 {
476 case Action::cache:
477 LogicError("ApplyStateTable::insert: already cached");
478 case Action::insert:
479 LogicError("ApplyStateTable::insert: already inserted");
480 case Action::modify:
481 LogicError("ApplyStateTable::insert: already modified");
482 case Action::erase:
483 break;
484 }
485 item.first = Action::modify;
486 item.second = sle;
487}
488
489void
491{
492 auto const iter = items_.lower_bound(sle->key());
493 if (iter == items_.end() || iter->first != sle->key())
494 {
495 using namespace std;
497 iter,
498 piecewise_construct,
499 forward_as_tuple(sle->key()),
500 forward_as_tuple(Action::modify, sle));
501 return;
502 }
503 auto& item = iter->second;
504 switch (item.first)
505 {
506 case Action::erase:
507 LogicError("ApplyStateTable::replace: already erased");
508 case Action::cache:
509 item.first = Action::modify;
510 break;
511 case Action::insert:
512 case Action::modify:
513 break;
514 }
515 item.second = sle;
516}
517
518void
520{
521 auto const iter = items_.find(sle->key());
522 if (iter == items_.end())
523 LogicError("ApplyStateTable::update: missing key");
524 auto& item = iter->second;
525 if (item.second != sle)
526 LogicError("ApplyStateTable::update: unknown SLE");
527 switch (item.first)
528 {
529 case Action::erase:
530 LogicError("ApplyStateTable::update: erased");
531 break;
532 case Action::cache:
533 item.first = Action::modify;
534 break;
535 case Action::insert:
536 case Action::modify:
537 break;
538 };
539}
540
541void
543{
544 dropsDestroyed_ += fee;
545}
546
547//------------------------------------------------------------------------------
548
549// Insert this transaction to the SLE's threading list
550void
552{
553 key_type prevTxID;
554 LedgerIndex prevLgrID;
555
556 if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID))
557 return;
558
559 if (!prevTxID.isZero())
560 {
561 auto& node = meta.getAffectedNode(sle, sfModifiedNode);
562
563 if (node.getFieldIndex(sfPreviousTxnID) == -1)
564 {
565 XRPL_ASSERT(
566 node.getFieldIndex(sfPreviousTxnLgrSeq) == -1,
567 "ripple::ApplyStateTable::threadItem : previous ledger is not "
568 "set");
569 node.setFieldH256(sfPreviousTxnID, prevTxID);
570 node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID);
571 }
572
573 XRPL_ASSERT(
574 node.getFieldH256(sfPreviousTxnID) == prevTxID,
575 "ripple::ApplyStateTable::threadItem : previous transaction is a "
576 "match");
577 XRPL_ASSERT(
578 node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID,
579 "ripple::ApplyStateTable::threadItem : previous ledger is a match");
580 }
581}
582
585 ReadView const& base,
586 key_type const& key,
587 Mods& mods,
589{
590 {
591 auto miter = mods.find(key);
592 if (miter != mods.end())
593 {
594 XRPL_ASSERT(
595 miter->second,
596 "ripple::ApplyStateTable::getForMod : non-null result");
597 return miter->second;
598 }
599 }
600 {
601 auto iter = items_.find(key);
602 if (iter != items_.end())
603 {
604 auto const& item = iter->second;
605 if (item.first == Action::erase)
606 {
607 // The Destination of an Escrow or a PayChannel may have been
608 // deleted. In that case the account we're threading to will
609 // not be found and it is appropriate to return a nullptr.
610 JLOG(j.warn()) << "Trying to thread to deleted node";
611 return nullptr;
612 }
613 if (item.first != Action::cache)
614 return item.second;
615
616 // If it's only cached, then the node is being modified only by
617 // metadata; fall through and track it in the mods table.
618 }
619 }
620 auto c = base.read(keylet::unchecked(key));
621 if (!c)
622 {
623 // The Destination of an Escrow or a PayChannel may have been
624 // deleted. In that case the account we're threading to will
625 // not be found and it is appropriate to return a nullptr.
626 JLOG(j.warn()) << "ApplyStateTable::getForMod: key not found";
627 return nullptr;
628 }
629 auto sle = std::make_shared<SLE>(*c);
630 mods.emplace(key, sle);
631 return sle;
632}
633
634void
636 ReadView const& base,
637 TxMeta& meta,
638 AccountID const& to,
639 Mods& mods,
641{
642 auto const sle = getForMod(base, keylet::account(to).key, mods, j);
643 if (!sle)
644 {
645 // The Destination of an Escrow or PayChannel may have been deleted.
646 // In that case the account we are threading to will not be found.
647 // So this logging is just a warning.
648 JLOG(j.warn()) << "Threading to non-existent account: " << toBase58(to);
649 return;
650 }
651 // threadItem only applied to AccountRoot
652 XRPL_ASSERT(
653 sle->isThreadedType(base.rules()),
654 "ripple::ApplyStateTable::threadTx : SLE is threaded");
655 threadItem(meta, sle);
656}
657
658void
660 ReadView const& base,
661 TxMeta& meta,
663 Mods& mods,
665{
666 LedgerEntryType const ledgerType{sle->getType()};
667 switch (ledgerType)
668 {
669 case ltACCOUNT_ROOT: {
670 // Nothing to do
671 break;
672 }
673 case ltRIPPLE_STATE: {
674 threadTx(base, meta, (*sle)[sfLowLimit].getIssuer(), mods, j);
675 threadTx(base, meta, (*sle)[sfHighLimit].getIssuer(), mods, j);
676 break;
677 }
678 default: {
679 // If sfAccount is present, thread to that account
680 if (auto const optSleAcct{(*sle)[~sfAccount]})
681 threadTx(base, meta, *optSleAcct, mods, j);
682
683 // Don't thread a check's sfDestination unless the amendment is
684 // enabled
685 if (ledgerType == ltCHECK &&
686 !base.rules().enabled(fixCheckThreading))
687 break;
688
689 // If sfDestination is present, thread to that account
690 if (auto const optSleDest{(*sle)[~sfDestination]})
691 threadTx(base, meta, *optSleDest, mods, j);
692 }
693 }
694}
695
696} // namespace detail
697} // 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:144
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:3239
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