xrpld
Loading...
Searching...
No Matches
CheckCreate.cpp
1#include <xrpl/tx/transactors/check/CheckCreate.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/core/ServiceRegistry.h>
6#include <xrpl/ledger/View.h>
7#include <xrpl/ledger/helpers/AccountRootHelpers.h>
8#include <xrpl/ledger/helpers/DirectoryHelpers.h>
9#include <xrpl/ledger/helpers/MPTokenHelpers.h>
10#include <xrpl/ledger/helpers/SponsorHelpers.h>
11#include <xrpl/ledger/helpers/TokenHelpers.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Asset.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/Keylet.h>
18#include <xrpl/protocol/LedgerFormats.h>
19#include <xrpl/protocol/MPTIssue.h>
20#include <xrpl/protocol/SField.h>
21#include <xrpl/protocol/STAmount.h>
22#include <xrpl/protocol/STLedgerEntry.h>
23#include <xrpl/protocol/STTx.h>
24#include <xrpl/protocol/TER.h>
25#include <xrpl/protocol/XRPAmount.h>
26#include <xrpl/tx/Transactor.h>
27
28#include <memory>
29#include <optional>
30
31namespace xrpl {
32
33bool
35{
36 return ctx.rules.enabled(featureMPTokensV2) || !ctx.tx[sfSendMax].holds<MPTIssue>();
37}
38
41{
42 if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
43 {
44 // They wrote a check to themselves.
45 JLOG(ctx.j.warn()) << "Malformed transaction: Check to self.";
46 return temREDUNDANT;
47 }
48
49 {
50 STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)};
51 if (!isLegalNet(sendMax) || sendMax.signum() <= 0)
52 {
53 JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: "
54 << sendMax.getFullText();
55 return temBAD_AMOUNT;
56 }
57
58 if (badAsset() == sendMax.asset())
59 {
60 JLOG(ctx.j.warn()) << "Malformed transaction: Bad currency.";
61 return temBAD_CURRENCY;
62 }
63 }
64
65 if (auto const optExpiry = ctx.tx[~sfExpiration])
66 {
67 if (*optExpiry == 0)
68 {
69 JLOG(ctx.j.warn()) << "Malformed transaction: bad expiration";
70 return temBAD_EXPIRATION;
71 }
72 }
73
74 return tesSUCCESS;
75}
76
77TER
79{
80 AccountID const dstId{ctx.tx[sfDestination]};
81 AccountID const srcId{ctx.tx[sfAccount]};
82 auto const sleDst = ctx.view.read(keylet::account(dstId));
83 if (!sleDst)
84 {
85 JLOG(ctx.j.warn()) << "Destination account does not exist.";
86 return tecNO_DST;
87 }
88
89 // Check if the destination has disallowed incoming checks
90 if (sleDst->isFlag(lsfDisallowIncomingCheck))
91 return tecNO_PERMISSION;
92
93 // Pseudo-accounts cannot cash checks. Note, this is not amendment-gated
94 // because all writes to pseudo-account discriminator fields **are**
95 // amendment gated, hence the behaviour of this check will always match the
96 // currently active amendments.
97 if (isPseudoAccount(sleDst))
98 return tecNO_PERMISSION;
99
100 if (sleDst->isFlag(lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
101 {
102 // The tag is basically account-specific information we don't
103 // understand, but we can require someone to fill it in.
104 JLOG(ctx.j.warn()) << "Malformed transaction: DestinationTag required.";
105 return tecDST_TAG_NEEDED;
106 }
107
108 {
109 STAmount const sendMax{ctx.tx[sfSendMax]};
110 if (!sendMax.native())
111 {
112 // The currency may not be globally frozen
113 AccountID const& issuerId{sendMax.getIssuer()};
114 if (auto const ter = checkGlobalFrozen(ctx.view, sendMax.asset()); !isTesSuccess(ter))
115 {
116 JLOG(ctx.j.warn()) << "Creating a check for frozen or locked asset";
117 return ter;
118 }
119 auto const err = sendMax.asset().visit(
120 [&](Issue const& issue) -> std::optional<TER> {
121 // If this account has a trustline for the currency,
122 // that trustline may not be frozen.
123 //
124 // Note that we DO allow create check for a currency
125 // that the account does not yet have a trustline to.
126 if (issuerId != srcId)
127 {
128 // Check if the issuer froze the line
129 auto const sleTrust =
130 ctx.view.read(keylet::trustLine(srcId, issuerId, issue.currency));
131 if (sleTrust &&
132 sleTrust->isFlag((issuerId > srcId) ? lsfHighFreeze : lsfLowFreeze))
133 {
134 JLOG(ctx.j.warn()) << "Creating a check for frozen trustline.";
135 return tecFROZEN;
136 }
137 }
138 if (issuerId != dstId)
139 {
140 // Check if dst froze the line.
141 auto const sleTrust =
142 ctx.view.read(keylet::trustLine(issuerId, dstId, issue.currency));
143 if (sleTrust &&
144 sleTrust->isFlag((dstId > issuerId) ? lsfHighFreeze : lsfLowFreeze))
145 {
146 JLOG(ctx.j.warn()) << "Creating a check for "
147 "destination frozen trustline.";
148 return tecFROZEN;
149 }
150 }
151
152 return std::nullopt;
153 },
154 [&](MPTIssue const& issue) -> std::optional<TER> {
155 if (srcId != issuerId && isFrozen(ctx.view, srcId, issue))
156 {
157 JLOG(ctx.j.warn()) << "Creating a check for locked MPT.";
158 return tecLOCKED;
159 }
160 if (dstId != issuerId && isFrozen(ctx.view, dstId, issue))
161 {
162 JLOG(ctx.j.warn()) << "Creating a check for locked MPT.";
163 return tecLOCKED;
164 }
165 if (auto const ter = canTransfer(ctx.view, issue, srcId, dstId);
166 !isTesSuccess(ter))
167 {
168 JLOG(ctx.j.warn()) << "MPT transfer is disabled.";
169 return ter;
170 }
171
172 return std::nullopt;
173 });
174 if (err)
175 return *err;
176 }
177 }
178 if (hasExpired(ctx.view, ctx.tx[~sfExpiration]))
179 {
180 JLOG(ctx.j.warn()) << "Creating a check that has already expired.";
181 return tecEXPIRED;
182 }
183
184 return tesSUCCESS;
185}
186
187TER
189{
190 auto const sle = view().peek(keylet::account(accountID_));
191 if (!sle)
192 return tefINTERNAL; // LCOV_EXCL_LINE
193
194 // A check counts against the reserve of the issuing account, but we
195 // check the starting balance because we want to allow dipping into the
196 // reserve to pay fees.
197 if (auto const ret = checkReserve(
198 ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal);
199 !isTesSuccess(ret))
200 return ret;
201 // Note that we use the value from the sequence or ticket as the
202 // Check sequence. For more explanation see comments in SeqProxy.h.
203 auto const seq = ctx_.tx.getSeqProxy();
204 Keylet const checkKeylet = keylet::check(accountID_, seq);
205 auto sleCheck = std::make_shared<SLE>(checkKeylet);
206
207 sleCheck->setAccountID(sfAccount, accountID_);
208 AccountID const dstAccountId = ctx_.tx[sfDestination];
209 sleCheck->setAccountID(sfDestination, dstAccountId);
210 sleCheck->setFieldU32(sfSequence, seq.value());
211 sleCheck->setFieldAmount(sfSendMax, ctx_.tx[sfSendMax]);
212 if (auto const srcTag = ctx_.tx[~sfSourceTag])
213 sleCheck->setFieldU32(sfSourceTag, *srcTag);
214 if (auto const dstTag = ctx_.tx[~sfDestinationTag])
215 sleCheck->setFieldU32(sfDestinationTag, *dstTag);
216 if (auto const invoiceId = ctx_.tx[~sfInvoiceID])
217 sleCheck->setFieldH256(sfInvoiceID, *invoiceId);
218 if (auto const expiry = ctx_.tx[~sfExpiration])
219 sleCheck->setFieldU32(sfExpiration, *expiry);
220
221 view().insert(sleCheck);
222
223 auto viewJ = ctx_.registry.get().getJournal("View");
224 // If it's not a self-send (and it shouldn't be), add Check to the
225 // destination's owner directory.
226 if (dstAccountId != accountID_)
227 {
228 auto const page = view().dirInsert(
229 keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId));
230
231 JLOG(j_.trace()) << "Adding Check to destination directory " << to_string(checkKeylet.key)
232 << ": " << (page ? "success" : "failure");
233
234 if (!page)
235 return tecDIR_FULL; // LCOV_EXCL_LINE
236
237 sleCheck->setFieldU64(sfDestinationNode, *page);
238 }
239
240 {
241 auto const page = view().dirInsert(
243
244 JLOG(j_.trace()) << "Adding Check to owner directory " << to_string(checkKeylet.key) << ": "
245 << (page ? "success" : "failure");
246
247 if (!page)
248 return tecDIR_FULL; // LCOV_EXCL_LINE
249
250 sleCheck->setFieldU64(sfOwnerNode, *page);
251 }
252 // If we succeeded, the new entry counts against the creator's reserve.
253
254 increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ);
255 addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck);
256 return tesSUCCESS;
257}
258
259void
261{
262 // No transaction-specific invariants yet (future work).
263}
264
265bool
267{
268 // No transaction-specific invariants yet (future work).
269 return true;
270}
271
272} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream warn() const
Definition Journal.h:356
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void insert(SLE::ref sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(SLE::ref)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:366
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:117
static TER preclaim(PreclaimContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static bool checkExtraFeatures(xrpl::PreflightContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
TER doApply() override
A currency issued by an account.
Definition Issue.h:18
Currency currency
Definition Issue.h:20
A view into a ledger.
Definition ReadView.h:41
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.
Definition Rules.cpp:180
std::string getFullText() const override
Definition STAmount.cpp:636
int signum() const noexcept
Definition STAmount.h:522
bool native() const noexcept
Definition STAmount.h:471
Asset const & asset() const
Definition STAmount.h:496
AccountID const & getIssuer() const
Definition STAmount.h:516
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:657
beast::Journal const j_
Definition Transactor.h:155
ApplyView & view()
Definition Transactor.h:175
AccountID const accountID_
Definition Transactor.h:157
XRPAmount preFeeBalance_
Definition Transactor.h:158
ApplyContext & ctx_
Definition Transactor.h:153
T make_shared(T... args)
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet check(AccountID const &id, SeqProxy const &seq) noexcept
A Check.
Definition Indexes.cpp:338
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:253
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp, ExpiryComparison comparison=ExpiryComparison::Inclusive)
Determines whether the given expiration time has passed.
Definition View.cpp:48
void increaseOwnerCount(ApplyView &view, SLE::ref accountSle, SLE::ref sponsorSle, std::uint32_t count, beast::Journal j)
Increase owner-count fields when the caller supplies the sponsor.
@ tefINTERNAL
Definition TER.h:165
bool isLegalNet(STAmount const &value)
Definition STAmount.h:616
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to, WaiveMPTCanTransfer waive=WaiveMPTCanTransfer::No, std::uint8_t depth=0)
Check whether to may receive the given MPT from from.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
void addSponsorToLedgerEntry(SLE::ref sle, SLE::const_ref sponsorSle, SF_ACCOUNT const &field=sfSponsor)
Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, std::uint8_t depth=0)
TER checkGlobalFrozen(ReadView const &view, Asset const &asset)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
TER checkReserve(ApplyViewContext ctx, SLE::const_ref accSle, XRPAmount accBalance, SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j, TER insufReserveCode=tecINSUFFICIENT_RESERVE)
Check if an account has sufficient reserve.
@ temBAD_CURRENCY
Definition TER.h:78
@ temBAD_EXPIRATION
Definition TER.h:79
@ temBAD_AMOUNT
Definition TER.h:77
@ temREDUNDANT
Definition TER.h:100
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecDIR_FULL
Definition TER.h:290
@ tecLOCKED
Definition TER.h:361
@ tecFROZEN
Definition TER.h:306
@ tecEXPIRED
Definition TER.h:317
@ tecNO_PERMISSION
Definition TER.h:308
@ tecDST_TAG_NEEDED
Definition TER.h:312
@ tecNO_DST
Definition TER.h:293
BadAsset const & badAsset()
Definition Asset.h:40
bool isPseudoAccount(SLE::const_pointer sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
@ tesSUCCESS
Definition TER.h:245
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
beast::Journal const j
Definition Transactor.h:91
State information when preflighting a tx.
Definition Transactor.h:38
beast::Journal const j
Definition Transactor.h:45