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  break;
369  default:
370  invalidTypeAdded_ = true;
371  break;
372  }
373  }
374 }
375 
376 bool
378  STTx const&,
379  TER const,
380  XRPAmount const,
381  ReadView const&,
382  beast::Journal const& j)
383 {
384  if ((!typeMismatch_) && (!invalidTypeAdded_))
385  return true;
386 
387  if (typeMismatch_)
388  {
389  JLOG(j.fatal()) << "Invariant failed: ledger entry type mismatch";
390  }
391 
392  if (invalidTypeAdded_)
393  {
394  JLOG(j.fatal()) << "Invariant failed: invalid ledger entry type added";
395  }
396 
397  return false;
398 }
399 
400 //------------------------------------------------------------------------------
401 
402 void
404  bool,
407 {
408  if (after && after->getType() == ltRIPPLE_STATE)
409  {
410  // checking the issue directly here instead of
411  // relying on .native() just in case native somehow
412  // were systematically incorrect
413  xrpTrustLine_ =
414  after->getFieldAmount(sfLowLimit).issue() == xrpIssue() ||
415  after->getFieldAmount(sfHighLimit).issue() == xrpIssue();
416  }
417 }
418 
419 bool
421  STTx const&,
422  TER const,
423  XRPAmount const,
424  ReadView const&,
425  beast::Journal const& j)
426 {
427  if (!xrpTrustLine_)
428  return true;
429 
430  JLOG(j.fatal()) << "Invariant failed: an XRP trust line was created";
431  return false;
432 }
433 
434 //------------------------------------------------------------------------------
435 
436 void
438  bool,
439  std::shared_ptr<SLE const> const& before,
441 {
442  if (!before && after->getType() == ltACCOUNT_ROOT)
443  {
445  accountSeq_ = (*after)[sfSequence];
446  }
447 }
448 
449 bool
451  STTx const& tx,
452  TER const result,
453  XRPAmount const,
454  ReadView const& view,
455  beast::Journal const& j)
456 {
457  if (accountsCreated_ == 0)
458  return true;
459 
460  if (accountsCreated_ > 1)
461  {
462  JLOG(j.fatal()) << "Invariant failed: multiple accounts "
463  "created in a single transaction";
464  return false;
465  }
466 
467  // From this point on we know exactly one account was created.
468  if (tx.getTxnType() == ttPAYMENT && result == tesSUCCESS)
469  {
470  std::uint32_t const startingSeq{
471  view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1};
472 
473  if (accountSeq_ != startingSeq)
474  {
475  JLOG(j.fatal()) << "Invariant failed: account created with "
476  "wrong starting sequence number";
477  return false;
478  }
479  return true;
480  }
481 
482  JLOG(j.fatal()) << "Invariant failed: account root created "
483  "by a non-Payment or by an unsuccessful transaction";
484  return false;
485 }
486 
487 } // 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:403
ripple::ltESCROW
@ ltESCROW
Definition: LedgerFormats.h:80
ripple::sfSequence
const SF_U32 sfSequence(access, STI_UINT32, 4, "Sequence")
Definition: SField.h:355
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:440
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:437
ripple::sfHighLimit
const SF_Amount sfHighLimit(access, STI_AMOUNT, 7, "HighLimit")
Definition: SField.h:443
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:442
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:42
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:450
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
Definition: Feature.cpp:183
ripple::NoXRPTrustLines::finalize
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Definition: InvariantCheck.cpp:420
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:377
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:437
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:444
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::STAmount::native
bool native() const noexcept
Definition: STAmount.h:182
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:188
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:438
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:256
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:441
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:213
ripple::XRPNotCreated::drops_
std::int64_t drops_
Definition: InvariantCheck.h:120
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:587
ripple::XRPAmount
Definition: XRPAmount.h:46