rippled
Loading...
Searching...
No Matches
DepositPreauth.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2018 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 <xrpld/app/tx/detail/DepositPreauth.h>
21
22#include <xrpl/basics/Log.h>
23#include <xrpl/ledger/CredentialHelpers.h>
24#include <xrpl/ledger/View.h>
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/Indexes.h>
27#include <xrpl/protocol/TxFlags.h>
28
29#include <optional>
30
31namespace ripple {
32
35{
36 if (!ctx.rules.enabled(featureDepositPreauth))
37 return temDISABLED;
38
39 bool const authArrPresent = ctx.tx.isFieldPresent(sfAuthorizeCredentials);
40 bool const unauthArrPresent =
41 ctx.tx.isFieldPresent(sfUnauthorizeCredentials);
42 int const authCredPresent =
43 static_cast<int>(authArrPresent) + static_cast<int>(unauthArrPresent);
44
45 if (authCredPresent && !ctx.rules.enabled(featureCredentials))
46 return temDISABLED;
47
48 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
49 return ret;
50
51 auto& tx = ctx.tx;
52
53 if (tx.getFlags() & tfUniversalMask)
54 {
55 JLOG(ctx.j.trace()) << "Malformed transaction: Invalid flags set.";
56 return temINVALID_FLAG;
57 }
58
59 auto const optAuth = ctx.tx[~sfAuthorize];
60 auto const optUnauth = ctx.tx[~sfUnauthorize];
61 int const authPresent = static_cast<int>(optAuth.has_value()) +
62 static_cast<int>(optUnauth.has_value());
63
64 if (authPresent + authCredPresent != 1)
65 {
66 // There can only be 1 field out of 4 or the transaction is malformed.
67 JLOG(ctx.j.trace())
68 << "Malformed transaction: "
69 "Invalid Authorize and Unauthorize field combination.";
70 return temMALFORMED;
71 }
72
73 if (authPresent)
74 {
75 // Make sure that the passed account is valid.
76 AccountID const& target(optAuth ? *optAuth : *optUnauth);
77 if (!target)
78 {
79 JLOG(ctx.j.trace())
80 << "Malformed transaction: Authorized or Unauthorized "
81 "field zeroed.";
83 }
84
85 // An account may not preauthorize itself.
86 if (optAuth && (target == ctx.tx[sfAccount]))
87 {
88 JLOG(ctx.j.trace())
89 << "Malformed transaction: Attempting to DepositPreauth self.";
91 }
92 }
93 else
94 {
95 if (auto err = credentials::checkArray(
96 ctx.tx.getFieldArray(
97 authArrPresent ? sfAuthorizeCredentials
98 : sfUnauthorizeCredentials),
100 ctx.j);
101 !isTesSuccess(err))
102 return err;
103 }
104
105 return preflight2(ctx);
106}
107
108TER
110{
111 AccountID const account(ctx.tx[sfAccount]);
112
113 // Determine which operation we're performing: authorizing or unauthorizing.
114 if (ctx.tx.isFieldPresent(sfAuthorize))
115 {
116 // Verify that the Authorize account is present in the ledger.
117 AccountID const auth{ctx.tx[sfAuthorize]};
118 if (!ctx.view.exists(keylet::account(auth)))
119 return tecNO_TARGET;
120
121 // Verify that the Preauth entry they asked to add is not already
122 // in the ledger.
123 if (ctx.view.exists(keylet::depositPreauth(account, auth)))
124 return tecDUPLICATE;
125 }
126 else if (ctx.tx.isFieldPresent(sfUnauthorize))
127 {
128 // Verify that the Preauth entry they asked to remove is in the ledger.
129 if (!ctx.view.exists(
130 keylet::depositPreauth(account, ctx.tx[sfUnauthorize])))
131 return tecNO_ENTRY;
132 }
133 else if (ctx.tx.isFieldPresent(sfAuthorizeCredentials))
134 {
135 STArray const& authCred(ctx.tx.getFieldArray(sfAuthorizeCredentials));
137 for (auto const& o : authCred)
138 {
139 auto const& issuer = o[sfIssuer];
140 if (!ctx.view.exists(keylet::account(issuer)))
141 return tecNO_ISSUER;
142 auto [it, ins] = sorted.emplace(issuer, o[sfCredentialType]);
143 if (!ins)
144 return tefINTERNAL;
145 }
146
147 // Verify that the Preauth entry they asked to add is not already
148 // in the ledger.
149 if (ctx.view.exists(keylet::depositPreauth(account, sorted)))
150 return tecDUPLICATE;
151 }
152 else if (ctx.tx.isFieldPresent(sfUnauthorizeCredentials))
153 {
154 // Verify that the Preauth entry is in the ledger.
156 account,
158 ctx.tx.getFieldArray(sfUnauthorizeCredentials)))))
159 return tecNO_ENTRY;
160 }
161 return tesSUCCESS;
162}
163
164TER
166{
167 if (ctx_.tx.isFieldPresent(sfAuthorize))
168 {
169 auto const sleOwner = view().peek(keylet::account(account_));
170 if (!sleOwner)
171 return {tefINTERNAL};
172
173 // A preauth counts against the reserve of the issuing account, but we
174 // check the starting balance because we want to allow dipping into the
175 // reserve to pay fees.
176 {
177 STAmount const reserve{view().fees().accountReserve(
178 sleOwner->getFieldU32(sfOwnerCount) + 1)};
179
180 if (mPriorBalance < reserve)
182 }
183
184 // Preclaim already verified that the Preauth entry does not yet exist.
185 // Create and populate the Preauth entry.
186 AccountID const auth{ctx_.tx[sfAuthorize]};
187 Keylet const preauthKeylet = keylet::depositPreauth(account_, auth);
188 auto slePreauth = std::make_shared<SLE>(preauthKeylet);
189
190 slePreauth->setAccountID(sfAccount, account_);
191 slePreauth->setAccountID(sfAuthorize, auth);
192 view().insert(slePreauth);
193
194 auto const page = view().dirInsert(
196 preauthKeylet,
198
199 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
200 << to_string(preauthKeylet.key) << ": "
201 << (page ? "success" : "failure");
202
203 if (!page)
204 return tecDIR_FULL;
205
206 slePreauth->setFieldU64(sfOwnerNode, *page);
207
208 // If we succeeded, the new entry counts against the creator's reserve.
209 adjustOwnerCount(view(), sleOwner, 1, j_);
210 }
211 else if (ctx_.tx.isFieldPresent(sfUnauthorize))
212 {
213 auto const preauth =
214 keylet::depositPreauth(account_, ctx_.tx[sfUnauthorize]);
215
216 return DepositPreauth::removeFromLedger(view(), preauth.key, j_);
217 }
218 else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
219 {
220 auto const sleOwner = view().peek(keylet::account(account_));
221 if (!sleOwner)
222 return tefINTERNAL;
223
224 // A preauth counts against the reserve of the issuing account, but we
225 // check the starting balance because we want to allow dipping into the
226 // reserve to pay fees.
227 {
228 STAmount const reserve{view().fees().accountReserve(
229 sleOwner->getFieldU32(sfOwnerCount) + 1)};
230
231 if (mPriorBalance < reserve)
233 }
234
235 // Preclaim already verified that the Preauth entry does not yet exist.
236 // Create and populate the Preauth entry.
237
238 auto const sortedTX = credentials::makeSorted(
239 ctx_.tx.getFieldArray(sfAuthorizeCredentials));
240 STArray sortedLE(sfAuthorizeCredentials, sortedTX.size());
241 for (auto const& p : sortedTX)
242 {
243 auto cred = STObject::makeInnerObject(sfCredential);
244 cred.setAccountID(sfIssuer, p.first);
245 cred.setFieldVL(sfCredentialType, p.second);
246 sortedLE.push_back(std::move(cred));
247 }
248
249 Keylet const preauthKey = keylet::depositPreauth(account_, sortedTX);
250 auto slePreauth = std::make_shared<SLE>(preauthKey);
251 if (!slePreauth)
252 return tefINTERNAL;
253
254 slePreauth->setAccountID(sfAccount, account_);
255 slePreauth->peekFieldArray(sfAuthorizeCredentials) =
256 std::move(sortedLE);
257
258 view().insert(slePreauth);
259
260 auto const page = view().dirInsert(
262
263 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
264 << to_string(preauthKey.key) << ": "
265 << (page ? "success" : "failure");
266
267 if (!page)
268 return tecDIR_FULL;
269
270 slePreauth->setFieldU64(sfOwnerNode, *page);
271
272 // If we succeeded, the new entry counts against the creator's reserve.
273 adjustOwnerCount(view(), sleOwner, 1, j_);
274 }
275 else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
276 {
277 auto const preauthKey = keylet::depositPreauth(
278 account_,
280 ctx_.tx.getFieldArray(sfUnauthorizeCredentials)));
281 return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_);
282 }
283
284 return tesSUCCESS;
285}
286
287TER
289 ApplyView& view,
290 uint256 const& preauthIndex,
292{
293 // Existence already checked in preclaim and DeleteAccount
294 auto const slePreauth{view.peek(keylet::depositPreauth(preauthIndex))};
295 if (!slePreauth)
296 {
297 JLOG(j.warn()) << "Selected DepositPreauth does not exist.";
298 return tecNO_ENTRY;
299 }
300
301 AccountID const account{(*slePreauth)[sfAccount]};
302 std::uint64_t const page{(*slePreauth)[sfOwnerNode]};
303 if (!view.dirRemove(keylet::ownerDir(account), page, preauthIndex, false))
304 {
305 JLOG(j.fatal()) << "Unable to delete DepositPreauth from owner.";
306 return tefBAD_LEDGER;
307 }
308
309 // If we succeeded, update the DepositPreauth owner's reserve.
310 auto const sleOwner = view.peek(keylet::account(account));
311 if (!sleOwner)
312 return tefINTERNAL;
313
314 adjustOwnerCount(view, sleOwner, -1, j);
315
316 // Remove DepositPreauth from ledger.
317 view.erase(slePreauth);
318
319 return tesSUCCESS;
320}
321
322} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:60
Stream fatal() const
Definition Journal.h:352
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:143
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:317
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
static NotTEC preflight(PreflightContext const &ctx)
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
static TER preclaim(PreclaimContext const &ctx)
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
void push_back(STObject const &object)
Definition STArray.h:212
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:686
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:95
AccountID const account_
Definition Transactor.h:143
ApplyView & view()
Definition Transactor.h:159
beast::Journal const j_
Definition Transactor.h:141
XRPAmount mPriorBalance
Definition Transactor.h:144
ApplyContext & ctx_
Definition Transactor.h:140
T emplace(T... args)
T is_same_v
NotTEC checkArray(STArray const &credentials, unsigned maxSize, beast::Journal j)
std::set< std::pair< AccountID, Slice > > makeSorted(STArray const &credentials)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:342
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1029
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1047
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
@ tefBAD_LEDGER
Definition TER.h:170
@ tefINTERNAL
Definition TER.h:173
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition Protocol.h:107
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
@ tecNO_ENTRY
Definition TER.h:306
@ tecNO_ISSUER
Definition TER.h:299
@ tecNO_TARGET
Definition TER.h:304
@ tecDIR_FULL
Definition TER.h:287
@ tecDUPLICATE
Definition TER.h:315
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temMALFORMED
Definition TER.h:87
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
@ temCANNOT_PREAUTH_SELF
Definition TER.h:120
@ temINVALID_ACCOUNT_ID
Definition TER.h:119
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:39
uint256 key
Definition Keylet.h:40
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:79
ReadView const & view
Definition Transactor.h:82
State information when preflighting a tx.
Definition Transactor.h:34
beast::Journal const j
Definition Transactor.h:41