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
33bool
35{
36 bool const authArrPresent = ctx.tx.isFieldPresent(sfAuthorizeCredentials);
37 bool const unauthArrPresent =
38 ctx.tx.isFieldPresent(sfUnauthorizeCredentials);
39 bool const authCredPresent = authArrPresent || unauthArrPresent;
40
41 if (authCredPresent && !ctx.rules.enabled(featureCredentials))
42 return false;
43
44 return true;
45}
46
49{
50 bool const authArrPresent = ctx.tx.isFieldPresent(sfAuthorizeCredentials);
51 bool const unauthArrPresent =
52 ctx.tx.isFieldPresent(sfUnauthorizeCredentials);
53 int const authCredPresent =
54 static_cast<int>(authArrPresent) + static_cast<int>(unauthArrPresent);
55
56 auto const optAuth = ctx.tx[~sfAuthorize];
57 auto const optUnauth = ctx.tx[~sfUnauthorize];
58 int const authPresent = static_cast<int>(optAuth.has_value()) +
59 static_cast<int>(optUnauth.has_value());
60
61 if (authPresent + authCredPresent != 1)
62 {
63 // There can only be 1 field out of 4 or the transaction is malformed.
64 JLOG(ctx.j.trace())
65 << "Malformed transaction: "
66 "Invalid Authorize and Unauthorize field combination.";
67 return temMALFORMED;
68 }
69
70 if (authPresent)
71 {
72 // Make sure that the passed account is valid.
73 AccountID const& target(optAuth ? *optAuth : *optUnauth);
74 if (!target)
75 {
76 JLOG(ctx.j.trace())
77 << "Malformed transaction: Authorized or Unauthorized "
78 "field zeroed.";
80 }
81
82 // An account may not preauthorize itself.
83 if (optAuth && (target == ctx.tx[sfAccount]))
84 {
85 JLOG(ctx.j.trace())
86 << "Malformed transaction: Attempting to DepositPreauth self.";
88 }
89 }
90 else
91 {
92 if (auto err = credentials::checkArray(
93 ctx.tx.getFieldArray(
94 authArrPresent ? sfAuthorizeCredentials
95 : sfUnauthorizeCredentials),
97 ctx.j);
98 !isTesSuccess(err))
99 return err;
100 }
101
102 return tesSUCCESS;
103}
104
105TER
107{
108 AccountID const account(ctx.tx[sfAccount]);
109
110 // Determine which operation we're performing: authorizing or unauthorizing.
111 if (ctx.tx.isFieldPresent(sfAuthorize))
112 {
113 // Verify that the Authorize account is present in the ledger.
114 AccountID const auth{ctx.tx[sfAuthorize]};
115 if (!ctx.view.exists(keylet::account(auth)))
116 return tecNO_TARGET;
117
118 // Verify that the Preauth entry they asked to add is not already
119 // in the ledger.
120 if (ctx.view.exists(keylet::depositPreauth(account, auth)))
121 return tecDUPLICATE;
122 }
123 else if (ctx.tx.isFieldPresent(sfUnauthorize))
124 {
125 // Verify that the Preauth entry they asked to remove is in the ledger.
126 if (!ctx.view.exists(
127 keylet::depositPreauth(account, ctx.tx[sfUnauthorize])))
128 return tecNO_ENTRY;
129 }
130 else if (ctx.tx.isFieldPresent(sfAuthorizeCredentials))
131 {
132 STArray const& authCred(ctx.tx.getFieldArray(sfAuthorizeCredentials));
134 for (auto const& o : authCred)
135 {
136 auto const& issuer = o[sfIssuer];
137 if (!ctx.view.exists(keylet::account(issuer)))
138 return tecNO_ISSUER;
139 auto [it, ins] = sorted.emplace(issuer, o[sfCredentialType]);
140 if (!ins)
141 return tefINTERNAL; // LCOV_EXCL_LINE
142 }
143
144 // Verify that the Preauth entry they asked to add is not already
145 // in the ledger.
146 if (ctx.view.exists(keylet::depositPreauth(account, sorted)))
147 return tecDUPLICATE;
148 }
149 else if (ctx.tx.isFieldPresent(sfUnauthorizeCredentials))
150 {
151 // Verify that the Preauth entry is in the ledger.
153 account,
155 ctx.tx.getFieldArray(sfUnauthorizeCredentials)))))
156 return tecNO_ENTRY;
157 }
158 return tesSUCCESS;
159}
160
161TER
163{
164 if (ctx_.tx.isFieldPresent(sfAuthorize))
165 {
166 auto const sleOwner = view().peek(keylet::account(account_));
167 if (!sleOwner)
168 return {tefINTERNAL};
169
170 // A preauth counts against the reserve of the issuing account, but we
171 // check the starting balance because we want to allow dipping into the
172 // reserve to pay fees.
173 {
174 STAmount const reserve{view().fees().accountReserve(
175 sleOwner->getFieldU32(sfOwnerCount) + 1)};
176
177 if (mPriorBalance < reserve)
179 }
180
181 // Preclaim already verified that the Preauth entry does not yet exist.
182 // Create and populate the Preauth entry.
183 AccountID const auth{ctx_.tx[sfAuthorize]};
184 Keylet const preauthKeylet = keylet::depositPreauth(account_, auth);
185 auto slePreauth = std::make_shared<SLE>(preauthKeylet);
186
187 slePreauth->setAccountID(sfAccount, account_);
188 slePreauth->setAccountID(sfAuthorize, auth);
189 view().insert(slePreauth);
190
191 auto const page = view().dirInsert(
193 preauthKeylet,
195
196 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
197 << to_string(preauthKeylet.key) << ": "
198 << (page ? "success" : "failure");
199
200 if (!page)
201 return tecDIR_FULL; // LCOV_EXCL_LINE
202
203 slePreauth->setFieldU64(sfOwnerNode, *page);
204
205 // If we succeeded, the new entry counts against the creator's reserve.
206 adjustOwnerCount(view(), sleOwner, 1, j_);
207 }
208 else if (ctx_.tx.isFieldPresent(sfUnauthorize))
209 {
210 auto const preauth =
211 keylet::depositPreauth(account_, ctx_.tx[sfUnauthorize]);
212
213 return DepositPreauth::removeFromLedger(view(), preauth.key, j_);
214 }
215 else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
216 {
217 auto const sleOwner = view().peek(keylet::account(account_));
218 if (!sleOwner)
219 return tefINTERNAL; // LCOV_EXCL_LINE
220
221 // A preauth counts against the reserve of the issuing account, but we
222 // check the starting balance because we want to allow dipping into the
223 // reserve to pay fees.
224 {
225 STAmount const reserve{view().fees().accountReserve(
226 sleOwner->getFieldU32(sfOwnerCount) + 1)};
227
228 if (mPriorBalance < reserve)
230 }
231
232 // Preclaim already verified that the Preauth entry does not yet exist.
233 // Create and populate the Preauth entry.
234
235 auto const sortedTX = credentials::makeSorted(
236 ctx_.tx.getFieldArray(sfAuthorizeCredentials));
237 STArray sortedLE(sfAuthorizeCredentials, sortedTX.size());
238 for (auto const& p : sortedTX)
239 {
240 auto cred = STObject::makeInnerObject(sfCredential);
241 cred.setAccountID(sfIssuer, p.first);
242 cred.setFieldVL(sfCredentialType, p.second);
243 sortedLE.push_back(std::move(cred));
244 }
245
246 Keylet const preauthKey = keylet::depositPreauth(account_, sortedTX);
247 auto slePreauth = std::make_shared<SLE>(preauthKey);
248 if (!slePreauth)
249 return tefINTERNAL; // LCOV_EXCL_LINE
250
251 slePreauth->setAccountID(sfAccount, account_);
252 slePreauth->peekFieldArray(sfAuthorizeCredentials) =
253 std::move(sortedLE);
254
255 view().insert(slePreauth);
256
257 auto const page = view().dirInsert(
259
260 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
261 << to_string(preauthKey.key) << ": "
262 << (page ? "success" : "failure");
263
264 if (!page)
265 return tecDIR_FULL; // LCOV_EXCL_LINE
266
267 slePreauth->setFieldU64(sfOwnerNode, *page);
268
269 // If we succeeded, the new entry counts against the creator's reserve.
270 adjustOwnerCount(view(), sleOwner, 1, j_);
271 }
272 else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
273 {
274 auto const preauthKey = keylet::depositPreauth(
275 account_,
277 ctx_.tx.getFieldArray(sfUnauthorizeCredentials)));
278 return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_);
279 }
280
281 return tesSUCCESS;
282}
283
284TER
286 ApplyView& view,
287 uint256 const& preauthIndex,
289{
290 // Existence already checked in preclaim and DeleteAccount
291 auto const slePreauth{view.peek(keylet::depositPreauth(preauthIndex))};
292 if (!slePreauth)
293 {
294 JLOG(j.warn()) << "Selected DepositPreauth does not exist.";
295 return tecNO_ENTRY;
296 }
297
298 AccountID const account{(*slePreauth)[sfAccount]};
299 std::uint64_t const page{(*slePreauth)[sfOwnerNode]};
300 if (!view.dirRemove(keylet::ownerDir(account), page, preauthIndex, false))
301 {
302 // LCOV_EXCL_START
303 JLOG(j.fatal()) << "Unable to delete DepositPreauth from owner.";
304 return tefBAD_LEDGER;
305 // LCOV_EXCL_STOP
306 }
307
308 // If we succeeded, update the DepositPreauth owner's reserve.
309 auto const sleOwner = view.peek(keylet::account(account));
310 if (!sleOwner)
311 return tefINTERNAL; // LCOV_EXCL_LINE
312
313 adjustOwnerCount(view, sleOwner, -1, j);
314
315 // Remove DepositPreauth from ledger.
316 view.erase(slePreauth);
317
318 return tesSUCCESS;
319}
320
321} // 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:319
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 bool checkExtraFeatures(PreflightContext const &ctx)
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:702
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:147
ApplyView & view()
Definition Transactor.h:163
beast::Journal const j_
Definition Transactor.h:145
XRPAmount mPriorBalance
Definition Transactor.h:148
ApplyContext & ctx_
Definition Transactor.h:143
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:1032
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1050
@ 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:109
@ 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
@ temMALFORMED
Definition TER.h:87
@ 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:80
ReadView const & view
Definition Transactor.h:83
State information when preflighting a tx.
Definition Transactor.h:35
beast::Journal const j
Definition Transactor.h:42