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