rippled
InvariantCheck.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2016 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 <ripple/app/tx/impl/InvariantCheck.h>
21 #include <ripple/basics/FeeUnits.h>
22 #include <ripple/basics/Log.h>
23 #include <ripple/ledger/ReadView.h>
24 #include <ripple/protocol/Feature.h>
25 #include <ripple/protocol/SystemParameters.h>
26 
27 namespace ripple {
28 
29 void
31  bool,
34 {
35  // nothing to do
36 }
37 
38 bool
40  STTx const& tx,
41  TER const,
42  XRPAmount const fee,
43  ReadView const&,
44  beast::Journal const& j)
45 {
46  // We should never charge a negative fee
47  if (fee.drops() < 0)
48  {
49  JLOG(j.fatal()) << "Invariant failed: fee paid was negative: "
50  << fee.drops();
51  return false;
52  }
53 
54  // We should never charge a fee that's greater than or equal to the
55  // entire XRP supply.
56  if (fee >= INITIAL_XRP)
57  {
58  JLOG(j.fatal()) << "Invariant failed: fee paid exceeds system limit: "
59  << fee.drops();
60  return false;
61  }
62 
63  // We should never charge more for a transaction than the transaction
64  // authorizes. It's possible to charge less in some circumstances.
65  if (fee > tx.getFieldAmount(sfFee).xrp())
66  {
67  JLOG(j.fatal()) << "Invariant failed: fee paid is " << fee.drops()
68  << " exceeds fee specified in transaction.";
69  return false;
70  }
71 
72  return true;
73 }
74 
75 //------------------------------------------------------------------------------
76 
77 void
79  bool isDelete,
80  std::shared_ptr<SLE const> const& before,
82 {
83  /* We go through all modified ledger entries, looking only at account roots,
84  * escrow payments, and payment channels. We remove from the total any
85  * previous XRP values and add to the total any new XRP values. The net
86  * balance of a payment channel is computed from two fields (amount and
87  * balance) and deletions are ignored for paychan and escrow because the
88  * amount fields have not been adjusted for those in the case of deletion.
89  */
90  if (before)
91  {
92  switch (before->getType())
93  {
94  case ltACCOUNT_ROOT:
95  drops_ -= (*before)[sfBalance].xrp().drops();
96  break;
97  case ltPAYCHAN:
98  drops_ -=
99  ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops();
100  break;
101  case ltESCROW:
102  drops_ -= (*before)[sfAmount].xrp().drops();
103  break;
104  default:
105  break;
106  }
107  }
108 
109  if (after)
110  {
111  switch (after->getType())
112  {
113  case ltACCOUNT_ROOT:
114  drops_ += (*after)[sfBalance].xrp().drops();
115  break;
116  case ltPAYCHAN:
117  if (!isDelete)
118  drops_ += ((*after)[sfAmount] - (*after)[sfBalance])
119  .xrp()
120  .drops();
121  break;
122  case ltESCROW:
123  if (!isDelete)
124  drops_ += (*after)[sfAmount].xrp().drops();
125  break;
126  default:
127  break;
128  }
129  }
130 }
131 
132 bool
134  STTx const&,
135  TER const,
136  XRPAmount const fee,
137  ReadView const&,
138  beast::Journal const& j)
139 {
140  // The net change should never be positive, as this would mean that the
141  // transaction created XRP out of thin air. That's not possible.
142  if (drops_ > 0)
143  {
144  JLOG(j.fatal()) << "Invariant failed: XRP net change was positive: "
145  << drops_;
146  return false;
147  }
148 
149  // The negative of the net change should be equal to actual fee charged.
150  if (-drops_ != fee.drops())
151  {
152  JLOG(j.fatal()) << "Invariant failed: XRP net change of " << drops_
153  << " doesn't match fee " << fee.drops();
154  return false;
155  }
156 
157  return true;
158 }
159 
160 //------------------------------------------------------------------------------
161 
162 void
164  bool,
165  std::shared_ptr<SLE const> const& before,
167 {
168  auto isBad = [](STAmount const& balance) {
169  if (!balance.native())
170  return true;
171 
172  auto const drops = balance.xrp();
173 
174  // Can't have more than the number of drops instantiated
175  // in the genesis ledger.
176  if (drops > INITIAL_XRP)
177  return true;
178 
179  // Can't have a negative balance (0 is OK)
180  if (drops < XRPAmount{0})
181  return true;
182 
183  return false;
184  };
185 
186  if (before && before->getType() == ltACCOUNT_ROOT)
187  bad_ |= isBad((*before)[sfBalance]);
188 
189  if (after && after->getType() == ltACCOUNT_ROOT)
190  bad_ |= isBad((*after)[sfBalance]);
191 }
192 
193 bool
195  STTx const&,
196  TER const,
197  XRPAmount const,
198  ReadView const&,
199  beast::Journal const& j)
200 {
201  if (bad_)
202  {
203  JLOG(j.fatal()) << "Invariant failed: incorrect account XRP balance";
204  return false;
205  }
206 
207  return true;
208 }
209 
210 //------------------------------------------------------------------------------
211 
212 void
214  bool isDelete,
215  std::shared_ptr<SLE const> const& before,
217 {
218  auto isBad = [](STAmount const& pays, STAmount const& gets) {
219  // An offer should never be negative
220  if (pays < beast::zero)
221  return true;
222 
223  if (gets < beast::zero)
224  return true;
225 
226  // Can't have an XRP to XRP offer:
227  return pays.native() && gets.native();
228  };
229 
230  if (before && before->getType() == ltOFFER)
231  bad_ |= isBad((*before)[sfTakerPays], (*before)[sfTakerGets]);
232 
233  if (after && after->getType() == ltOFFER)
234  bad_ |= isBad((*after)[sfTakerPays], (*after)[sfTakerGets]);
235 }
236 
237 bool
239  STTx const&,
240  TER const,
241  XRPAmount const,
242  ReadView const&,
243  beast::Journal const& j)
244 {
245  if (bad_)
246  {
247  JLOG(j.fatal()) << "Invariant failed: offer with a bad amount";
248  return false;
249  }
250 
251  return true;
252 }
253 
254 //------------------------------------------------------------------------------
255 
256 void
258  bool isDelete,
259  std::shared_ptr<SLE const> const& before,
261 {
262  auto isBad = [](STAmount const& amount) {
263  if (!amount.native())
264  return true;
265 
266  if (amount.xrp() <= XRPAmount{0})
267  return true;
268 
269  if (amount.xrp() >= INITIAL_XRP)
270  return true;
271 
272  return false;
273  };
274 
275  if (before && before->getType() == ltESCROW)
276  bad_ |= isBad((*before)[sfAmount]);
277 
278  if (after && after->getType() == ltESCROW)
279  bad_ |= isBad((*after)[sfAmount]);
280 }
281 
282 bool
284  STTx const&,
285  TER const,
286  XRPAmount const,
287  ReadView const&,
288  beast::Journal const& j)
289 {
290  if (bad_)
291  {
292  JLOG(j.fatal()) << "Invariant failed: escrow specifies invalid amount";
293  return false;
294  }
295 
296  return true;
297 }
298 
299 //------------------------------------------------------------------------------
300 
301 void
303  bool isDelete,
304  std::shared_ptr<SLE const> const& before,
306 {
307  if (isDelete && before && before->getType() == ltACCOUNT_ROOT)
309 }
310 
311 bool
313  STTx const& tx,
314  TER const result,
315  XRPAmount const,
316  ReadView const&,
317  beast::Journal const& j)
318 {
319  if (tx.getTxnType() == ttACCOUNT_DELETE && result == tesSUCCESS)
320  {
321  if (accountsDeleted_ == 1)
322  return true;
323 
324  if (accountsDeleted_ == 0)
325  JLOG(j.fatal()) << "Invariant failed: account deletion "
326  "succeeded without deleting an account";
327  else
328  JLOG(j.fatal()) << "Invariant failed: account deletion "
329  "succeeded but deleted multiple accounts!";
330  return false;
331  }
332 
333  if (accountsDeleted_ == 0)
334  return true;
335 
336  JLOG(j.fatal()) << "Invariant failed: an account root was deleted";
337  return false;
338 }
339 
340 //------------------------------------------------------------------------------
341 
342 void
344  bool,
345  std::shared_ptr<SLE const> const& before,
347 {
348  if (before && after && before->getType() != after->getType())
349  typeMismatch_ = true;
350 
351  if (after)
352  {
353  switch (after->getType())
354  {
355  case ltACCOUNT_ROOT:
356  case ltDIR_NODE:
357  case ltRIPPLE_STATE:
358  case ltTICKET:
359  case ltSIGNER_LIST:
360  case ltOFFER:
361  case ltLEDGER_HASHES:
362  case ltAMENDMENTS:
363  case ltFEE_SETTINGS:
364  case ltESCROW:
365  case ltPAYCHAN:
366  case ltCHECK:
367  case ltDEPOSIT_PREAUTH:
368  case ltNEGATIVE_UNL:
369  break;
370  default:
371  invalidTypeAdded_ = true;
372  break;
373  }
374  }
375 }
376 
377 bool
379  STTx const&,
380  TER const,
381  XRPAmount const,
382  ReadView const&,
383  beast::Journal const& j)
384 {
385  if ((!typeMismatch_) && (!invalidTypeAdded_))
386  return true;
387 
388  if (typeMismatch_)
389  {
390  JLOG(j.fatal()) << "Invariant failed: ledger entry type mismatch";
391  }
392 
393  if (invalidTypeAdded_)
394  {
395  JLOG(j.fatal()) << "Invariant failed: invalid ledger entry type added";
396  }
397 
398  return false;
399 }
400 
401 //------------------------------------------------------------------------------
402 
403 void
405  bool,
408 {
409  if (after && after->getType() == ltRIPPLE_STATE)
410  {
411  // checking the issue directly here instead of
412  // relying on .native() just in case native somehow
413  // were systematically incorrect
414  xrpTrustLine_ =
415  after->getFieldAmount(sfLowLimit).issue() == xrpIssue() ||
416  after->getFieldAmount(sfHighLimit).issue() == xrpIssue();
417  }
418 }
419 
420 bool
422  STTx const&,
423  TER const,
424  XRPAmount const,
425  ReadView const&,
426  beast::Journal const& j)
427 {
428  if (!xrpTrustLine_)
429  return true;
430 
431  JLOG(j.fatal()) << "Invariant failed: an XRP trust line was created";
432  return false;
433 }
434 
435 //------------------------------------------------------------------------------
436 
437 void
439  bool,
440  std::shared_ptr<SLE const> const& before,
442 {
443  if (!before && after->getType() == ltACCOUNT_ROOT)
444  {
446  accountSeq_ = (*after)[sfSequence];
447  }
448 }
449 
450 bool
452  STTx const& tx,
453  TER const result,
454  XRPAmount const,
455  ReadView const& view,
456  beast::Journal const& j)
457 {
458  if (accountsCreated_ == 0)
459  return true;
460 
461  if (accountsCreated_ > 1)
462  {
463  JLOG(j.fatal()) << "Invariant failed: multiple accounts "
464  "created in a single transaction";
465  return false;
466  }
467 
468  // From this point on we know exactly one account was created.
469  if (tx.getTxnType() == ttPAYMENT && result == tesSUCCESS)
470  {
471  std::uint32_t const startingSeq{
472  view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1};
473 
474  if (accountSeq_ != startingSeq)
475  {
476  JLOG(j.fatal()) << "Invariant failed: account created with "
477  "wrong starting sequence number";
478  return false;
479  }
480  return true;
481  }
482 
483  JLOG(j.fatal()) << "Invariant failed: account root created "
484  "by a non-Payment or by an unsuccessful transaction";
485  return false;
486 }
487 
488 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::STTx::getTxnType
TxType getTxnType() const
Definition: STTx.h:105
ripple::LedgerEntryTypesMatch::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:343
ripple::NoZeroEscrow::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:257
ripple::ttACCOUNT_DELETE
@ ttACCOUNT_DELETE
Definition: TxFormats.h:57
std::shared_ptr
STL class.
ripple::TransactionFeeCheck::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:39
ripple::XRPBalanceChecks::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:194
ripple::XRPAmount::drops
constexpr value_type drops() const
Returns the number of drops.
Definition: XRPAmount.h:172
ripple::NoXRPTrustLines::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:404
ripple::ltESCROW
@ ltESCROW
Definition: LedgerFormats.h:80
ripple::sfSequence
const SF_U32 sfSequence(access, STI_UINT32, 4, "Sequence")
Definition: SField.h:356
ripple::NoBadOffers::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:213
ripple::NoZeroEscrow::bad_
bool bad_
Definition: InvariantCheck.h:277
ripple::sfTakerPays
const SF_Amount sfTakerPays(access, STI_AMOUNT, 4, "TakerPays")
Definition: SField.h:444
ripple::NoBadOffers::bad_
bool bad_
Definition: InvariantCheck.h:253
ripple::ltLEDGER_HASHES
@ ltLEDGER_HASHES
Definition: LedgerFormats.h:74
ripple::ltAMENDMENTS
@ ltAMENDMENTS
Definition: LedgerFormats.h:76
ripple::ltSIGNER_LIST
@ ltSIGNER_LIST
Definition: LedgerFormats.h:70
ripple::sfAmount
const SF_Amount sfAmount(access, STI_AMOUNT, 1, "Amount")
Definition: SField.h:441
ripple::sfHighLimit
const SF_Amount sfHighLimit(access, STI_AMOUNT, 7, "HighLimit")
Definition: SField.h:447
ripple::STAmount::xrp
XRPAmount xrp() const
Definition: STAmount.cpp:299
ripple::ltTICKET
@ ltTICKET
Definition: LedgerFormats.h:68
ripple::sfLowLimit
const SF_Amount sfLowLimit(access, STI_AMOUNT, 6, "LowLimit")
Definition: SField.h:446
ripple::ttPAYMENT
@ ttPAYMENT
Definition: TxFormats.h:36
ripple::ltCHECK
@ ltCHECK
Definition: LedgerFormats.h:85
ripple::INITIAL_XRP
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
Definition: SystemParameters.h:43
ripple::ValidNewAccountRoot::accountSeq_
std::uint32_t accountSeq_
Definition: InvariantCheck.h:303
ripple::ValidNewAccountRoot::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:451
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
Definition: Feature.cpp:182
ripple::NoXRPTrustLines::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:421
ripple::LedgerEntryTypesMatch::typeMismatch_
bool typeMismatch_
Definition: InvariantCheck.h:199
ripple::NoBadOffers::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:238
ripple::LedgerEntryTypesMatch::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:378
ripple::ltFEE_SETTINGS
@ ltFEE_SETTINGS
Definition: LedgerFormats.h:78
ripple::TERSubset< CanCvtToTER >
ripple::ValidNewAccountRoot::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:438
ripple::STAmount
Definition: STAmount.h:42
ripple::XRPNotCreated::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:78
ripple::XRPNotCreated::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:133
ripple::STTx
Definition: STTx.h:42
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:103
ripple::ltDEPOSIT_PREAUTH
@ ltDEPOSIT_PREAUTH
Definition: LedgerFormats.h:87
ripple::sfFee
const SF_Amount sfFee(access, STI_AMOUNT, 8, "Fee")
Definition: SField.h:448
ripple::NoZeroEscrow::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:283
ripple::AccountRootsNotDeleted::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:312
ripple::ltNEGATIVE_UNL
@ ltNEGATIVE_UNL
Definition: LedgerFormats.h:89
ripple::STAmount::native
bool native() const noexcept
Definition: STAmount.h:182
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:192
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::ltRIPPLE_STATE
@ ltRIPPLE_STATE
Definition: LedgerFormats.h:66
ripple::sfBalance
const SF_Amount sfBalance(access, STI_AMOUNT, 2, "Balance")
Definition: SField.h:442
ripple::NoXRPTrustLines::xrpTrustLine_
bool xrpTrustLine_
Definition: InvariantCheck.h:226
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:260
ripple::ValidNewAccountRoot::accountsCreated_
std::uint32_t accountsCreated_
Definition: InvariantCheck.h:302
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::TransactionFeeCheck::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:30
ripple::AccountRootsNotDeleted::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:302
ripple::sfTakerGets
const SF_Amount sfTakerGets(access, STI_AMOUNT, 5, "TakerGets")
Definition: SField.h:445
ripple::xrpIssue
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:97
ripple::ltPAYCHAN
@ ltPAYCHAN
Definition: LedgerFormats.h:83
ripple::after
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: Escrow.cpp:88
ripple::AccountRootsNotDeleted::accountsDeleted_
std::uint32_t accountsDeleted_
Definition: InvariantCheck.h:148
ripple::XRPBalanceChecks::visitEntry
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Definition: InvariantCheck.cpp:163
ripple::ltDIR_NODE
@ ltDIR_NODE
Directory node.
Definition: LedgerFormats.h:64
ripple::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:72
ripple::XRPBalanceChecks::bad_
bool bad_
Definition: InvariantCheck.h:175
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
Definition: LedgerFormats.h:53
ripple::LedgerEntryTypesMatch::invalidTypeAdded_
bool invalidTypeAdded_
Definition: InvariantCheck.h:200
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::XRPNotCreated::drops_
std::int64_t drops_
Definition: InvariantCheck.h:120
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:576
ripple::XRPAmount
Definition: XRPAmount.h:46