1#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/beast/utility/Zero.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/helpers/LendingHelpers.h>
9#include <xrpl/ledger/helpers/TokenHelpers.h>
10#include <xrpl/protocol/Asset.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/LedgerFormats.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/STAmount.h>
16#include <xrpl/protocol/STLedgerEntry.h>
17#include <xrpl/protocol/STNumber.h>
18#include <xrpl/protocol/STTx.h>
19#include <xrpl/protocol/TER.h>
20#include <xrpl/protocol/TxFormats.h>
21#include <xrpl/protocol/XRPAmount.h>
37 if (isDelete && before && before->getType() == ltLOAN_BROKER)
50 if (
after->getType() == ltLOAN_BROKER)
53 broker.brokerBefore = before;
54 broker.brokerAfter =
after;
56 else if (
after->getType() == ltACCOUNT_ROOT &&
after->isFieldPresent(sfLoanBrokerID))
58 auto const& loanBrokerID =
after->at(sfLoanBrokerID);
62 else if (
after->getType() == ltRIPPLE_STATE)
66 else if (
after->getType() == ltMPTOKEN)
76 auto const next = dir->at(~sfIndexNext);
77 auto const prev = dir->at(~sfIndexPrevious);
78 if ((prev && (*prev != 0u)) || (next && (*next != 0u)))
80 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker with zero "
81 "OwnerCount has multiple directory pages";
84 auto indexes = dir->getFieldV256(sfIndexes);
85 if (indexes.size() > 1)
87 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker with zero "
88 "OwnerCount has multiple indexes in the Directory root";
91 if (indexes.size() == 1)
93 auto const index = indexes.value().front();
97 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker directory corrupt";
100 if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN)
102 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker with zero "
103 "OwnerCount has an unexpected entry in the directory";
136 <<
"Invariant failed: more than one Loan Broker deleted in a single transaction";
143 JLOG(j.
fatal()) <<
"Invariant failed: " <<
144 "Loan Broker deleted by a transaction other than LoanBrokerDelete";
161 Asset{vault->at(sfAsset)},
167 <<
"Invariant failed: Loan Broker deleted with non-zero debt total";
174 <<
"Invariant failed: Loan Broker deleted with non-zero owner count";
180 for (
auto const& line :
lines_)
182 for (
auto const& field : {&sfLowLimit, &sfHighLimit})
188 if (account && account->isFieldPresent(sfLoanBrokerID))
190 auto const& loanBrokerID = account->at(sfLoanBrokerID);
196 for (
auto const& mpt :
mpts_)
202 if (account && account->isFieldPresent(sfLoanBrokerID))
204 auto const& loanBrokerID = account->at(sfLoanBrokerID);
211 auto const& [brokerID, broker] = entry;
217 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker missing";
221 auto const& before = broker.brokerBefore;
226 if (
after->at(sfOwnerCount) == 0)
237 if (before && before->at(sfLoanSequence) >
after->at(sfLoanSequence))
239 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker sequence number "
243 if (
after->at(sfDebtTotal) < 0)
245 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker debt total is negative";
248 if (
after->at(sfCoverAvailable) < 0)
250 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker cover available is negative";
256 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker vault ID is invalid";
259 auto const& vaultAsset = vault->at(sfAsset);
262 after->at(sfAccount),
267 if (
after->at(sfCoverAvailable) < pseudoBalance)
269 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker cover available "
270 "is less than pseudo-account asset balance";
278 if (tx.
getTxnType() != ttLOAN_BROKER_DELETE &&
279 after->at(sfCoverAvailable) > pseudoBalance)
281 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker cover available is greater "
282 "than pseudo-account asset balance";
A generic endpoint for log messages.
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
std::shared_ptr< STLedgerEntry const > const & ConstRef
TxType getTxnType() const
static bool goodZeroDirectory(ReadView const &view, SLE::ConstRef dir, beast::Journal const &j)
std::map< UInt256, BrokerInfo > brokers_
void visitEntry(bool, SLE::ConstRef, SLE::ConstRef)
bool multipleBrokerDeletions_
SLE::const_pointer deletedBroker_
std::vector< SLE::const_pointer > mpts_
std::vector< SLE::const_pointer > lines_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet unchecked(UInt256 const &key) noexcept
Any ledger entry.
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet loanBroker(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
int getAssetsTotalScale(SLE::ConstRef vaultSle)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
TERSubset< CanCvtToTER > TER
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)