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/TokenHelpers.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/Asset.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/Indexes.h>
15#include <xrpl/protocol/Issue.h>
16#include <xrpl/protocol/Keylet.h>
17#include <xrpl/protocol/LedgerFormats.h>
18#include <xrpl/protocol/MPTIssue.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STLedgerEntry.h>
22#include <xrpl/protocol/STTx.h>
23#include <xrpl/protocol/TER.h>
24#include <xrpl/protocol/XRPAmount.h>
25#include <xrpl/tx/Transactor.h>
26
27#include <cstdint>
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 {
198 STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};
199
200 if (preFeeBalance_ < reserve)
202 }
203
204 // Note that we use the value from the sequence or ticket as the
205 // Check sequence. For more explanation see comments in SeqProxy.h.
206 std::uint32_t const seq = ctx_.tx.getSeqValue();
207 Keylet const checkKeylet = keylet::check(accountID_, seq);
208 auto sleCheck = std::make_shared<SLE>(checkKeylet);
209
210 sleCheck->setAccountID(sfAccount, accountID_);
211 AccountID const dstAccountId = ctx_.tx[sfDestination];
212 sleCheck->setAccountID(sfDestination, dstAccountId);
213 sleCheck->setFieldU32(sfSequence, seq);
214 sleCheck->setFieldAmount(sfSendMax, ctx_.tx[sfSendMax]);
215 if (auto const srcTag = ctx_.tx[~sfSourceTag])
216 sleCheck->setFieldU32(sfSourceTag, *srcTag);
217 if (auto const dstTag = ctx_.tx[~sfDestinationTag])
218 sleCheck->setFieldU32(sfDestinationTag, *dstTag);
219 if (auto const invoiceId = ctx_.tx[~sfInvoiceID])
220 sleCheck->setFieldH256(sfInvoiceID, *invoiceId);
221 if (auto const expiry = ctx_.tx[~sfExpiration])
222 sleCheck->setFieldU32(sfExpiration, *expiry);
223
224 view().insert(sleCheck);
225
226 auto viewJ = ctx_.registry.get().getJournal("View");
227 // If it's not a self-send (and it shouldn't be), add Check to the
228 // destination's owner directory.
229 if (dstAccountId != accountID_)
230 {
231 auto const page = view().dirInsert(
232 keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId));
233
234 JLOG(j_.trace()) << "Adding Check to destination directory " << to_string(checkKeylet.key)
235 << ": " << (page ? "success" : "failure");
236
237 if (!page)
238 return tecDIR_FULL; // LCOV_EXCL_LINE
239
240 sleCheck->setFieldU64(sfDestinationNode, *page);
241 }
242
243 {
244 auto const page = view().dirInsert(
246
247 JLOG(j_.trace()) << "Adding Check to owner directory " << to_string(checkKeylet.key) << ": "
248 << (page ? "success" : "failure");
249
250 if (!page)
251 return tecDIR_FULL; // LCOV_EXCL_LINE
252
253 sleCheck->setFieldU64(sfOwnerNode, *page);
254 }
255 // If we succeeded, the new entry counts against the creator's reserve.
256 adjustOwnerCount(view(), sle, 1, viewJ);
257 return tesSUCCESS;
258}
259
260void
262{
263 // No transaction-specific invariants yet (future work).
264}
265
266bool
268{
269 // No transaction-specific invariants yet (future work).
270 return true;
271}
272
273} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Stream warn() const
Definition Journal.h:309
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:340
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:107
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:13
Currency currency
Definition Issue.h:15
A view into a ledger.
Definition ReadView.h:31
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
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:171
std::string getFullText() const override
Definition STAmount.cpp:636
int signum() const noexcept
Definition STAmount.h:504
bool native() const noexcept
Definition STAmount.h:453
Asset const & asset() const
Definition STAmount.h:478
AccountID const & getIssuer() const
Definition STAmount.h:498
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:454
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:647
beast::Journal const j_
Definition Transactor.h:118
ApplyView & view()
Definition Transactor.h:136
AccountID const accountID_
Definition Transactor.h:120
XRPAmount preFeeBalance_
Definition Transactor.h:121
ApplyContext & ctx_
Definition Transactor.h:116
T make_shared(T... args)
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:357
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition Indexes.cpp:322
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
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:241
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)
Determines whether the given expiration time has passed.
Definition View.cpp:47
@ tefINTERNAL
Definition TER.h:163
bool isLegalNet(STAmount const &value)
Definition STAmount.h:598
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:633
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
void adjustOwnerCount(ApplyView &view, SLE::ref sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
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:28
@ temBAD_CURRENCY
Definition TER.h:76
@ temBAD_EXPIRATION
Definition TER.h:77
@ temBAD_AMOUNT
Definition TER.h:75
@ temREDUNDANT
Definition TER.h:98
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
TERSubset< CanCvtToTER > TER
Definition TER.h:634
@ tecDIR_FULL
Definition TER.h:285
@ tecLOCKED
Definition TER.h:356
@ tecFROZEN
Definition TER.h:301
@ tecEXPIRED
Definition TER.h:312
@ tecINSUFFICIENT_RESERVE
Definition TER.h:305
@ tecNO_PERMISSION
Definition TER.h:303
@ tecDST_TAG_NEEDED
Definition TER.h:307
@ tecNO_DST
Definition TER.h:288
BadAsset const & badAsset()
Definition Asset.h:31
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:240
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
uint256 key
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:18
beast::Journal const j
Definition Transactor.h:25