xrpld
Loading...
Searching...
No Matches
SponsorHelpers.cpp
1#include <xrpl/ledger/helpers/SponsorHelpers.h>
2
3#include <xrpl/beast/utility/instrumentation.h>
4#include <xrpl/ledger/ApplyView.h>
5#include <xrpl/ledger/ReadView.h>
6#include <xrpl/ledger/helpers/AccountRootHelpers.h>
7#include <xrpl/ledger/helpers/OracleHelpers.h>
8#include <xrpl/protocol/AccountID.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/LedgerFormats.h>
12#include <xrpl/protocol/Rules.h>
13#include <xrpl/protocol/SField.h>
14#include <xrpl/protocol/STArray.h>
15#include <xrpl/protocol/STLedgerEntry.h>
16#include <xrpl/protocol/STTx.h>
17#include <xrpl/protocol/TER.h>
18#include <xrpl/protocol/TxFormats.h>
19
20#include <cstdint>
21#include <expected>
22#include <optional>
23#include <unordered_set>
24
25namespace xrpl {
26
27bool
29{
30 // Transaction types explicitly allow-listed for reserve sponsorship, for
31 // v1. Lazily-initialized function-local static: constructed once on first
32 // use, with no startup cost paid by clients that never call this.
33 static std::unordered_set<TxType> const kReserveSponsorAllowed = {
34 ttDELEGATE_SET,
35 ttDEPOSIT_PREAUTH,
36 ttPAYMENT,
37 ttSIGNER_LIST_SET,
38 ttCHECK_CANCEL,
39 ttCHECK_CASH,
40 ttCHECK_CREATE,
41 ttESCROW_CANCEL,
42 ttESCROW_CREATE,
43 ttESCROW_FINISH,
44 ttPAYCHAN_CLAIM,
45 ttPAYCHAN_CREATE,
46 ttPAYCHAN_FUND,
47 ttCLAWBACK,
48 ttMPTOKEN_AUTHORIZE,
49 ttMPTOKEN_ISSUANCE_CREATE,
50 ttMPTOKEN_ISSUANCE_DESTROY,
51 ttMPTOKEN_ISSUANCE_SET,
52 ttTRUST_SET,
53 ttCREDENTIAL_ACCEPT,
54 ttCREDENTIAL_CREATE,
55 ttCREDENTIAL_DELETE,
56 ttACCOUNT_SET,
57 ttREGULAR_KEY_SET,
58 ttSPONSORSHIP_TRANSFER,
59 };
60 return kReserveSponsorAllowed.contains(txType);
61}
62
65{
66 if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx))
67 {
68 XRPL_ASSERT(
69 getCurrentTransactionRules()->enabled( // NOLINT(bugprone-unchecked-optional-access)
70 featureSponsor),
71 "xrpl::getTxReserveSponsorID : sponsor exists + Sponsor enabled");
72 return tx.getAccountID(sfSponsor);
73 }
74 return {};
75}
76
77std::expected<SLE::pointer, TER>
79{
80 auto const sponsorID = getTxReserveSponsorID(ctx.tx);
81 if (sponsorID)
82 {
83 XRPL_ASSERT(
84 ctx.view.rules().enabled(featureSponsor),
85 "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
86 auto sle = ctx.view.peek(keylet::account(*sponsorID));
87
88 // already checked in Transactor::checkSponsor
89 if (!sle)
90 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
91 return sle;
92 }
93 return SLE::pointer();
94}
95
96std::expected<SLE::const_pointer, TER>
97getTxReserveSponsor(ReadView const& view, STTx const& tx)
98{
99 auto const sponsorID = getTxReserveSponsorID(tx);
100 if (sponsorID)
101 {
102 XRPL_ASSERT(
103 view.rules().enabled(featureSponsor),
104 "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
105 auto sle = view.read(keylet::account(*sponsorID));
106
107 // already checked in Transactor::checkSponsor
108 if (!sle)
109 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
110 return sle;
111 }
112 return SLE::pointer();
113}
114
115std::expected<SLE::pointer, TER>
117{
118 // A reserve sponsor only covers tx.Account's own objects.
119 if (ctx.view.rules().enabled(fixCleanup3_2_0))
120 {
121 XRPL_ASSERT(
122 accountSle && accountSle->getType() == ltACCOUNT_ROOT,
123 "xrpl::getEffectiveTxReserveSponsor : accountSle exists and is account type");
124 }
125 else
126 {
127 XRPL_ASSERT(
128 accountSle &&
129 ((accountSle->getType() == ltACCOUNT_ROOT) || (accountSle->getType() == ltESCROW)),
130 "xrpl::getEffectiveTxReserveSponsor : accountSle exists and is account type");
131 }
132
133 if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount])
134 return SLE::pointer();
135 return getTxReserveSponsor(ctx);
136}
137
140{
141 XRPL_ASSERT(
142 (sle &&
143 ((sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) ||
144 (sle->getType() != ltRIPPLE_STATE && field == sfSponsor))),
145 "xrpl::getLedgerEntryReserveSponsorID : correct sfield");
146
147 if (sle->isFieldPresent(field))
148 return sle->getAccountID(field);
149 return {};
150}
151
154{
155 auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
156 if (sponsorID)
157 {
158 XRPL_ASSERT(
159 view.rules().enabled(featureSponsor),
160 "xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled");
161 return view.peek(keylet::account(*sponsorID));
162 }
163 return {};
164}
165
166void
168{
169 XRPL_ASSERT(
170 (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) ||
171 (sle->getType() != ltRIPPLE_STATE && field == sfSponsor),
172 "addSponsorToLedgerEntry : Invalid field to the LedgerEntry");
173 if (sponsorSle)
174 {
175 XRPL_ASSERT(
176 getCurrentTransactionRules()->enabled( // NOLINT(bugprone-unchecked-optional-access)
177 featureSponsor),
178 "xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled");
179 sle->setAccountID(field, sponsorSle->getAccountID(sfAccount));
180 }
181}
182
183void
185{
186 // getTxReserveSponsor yields a null pointer when the tx is not
187 // reserve-sponsored, so addSponsorToLedgerEntry becomes a no-op then. The
188 // error case (tecINTERNAL) is an already-checked invariant; skip stamping.
189 auto const sponsorSle = getTxReserveSponsor(ctx);
190 if (sponsorSle && *sponsorSle)
191 {
192 XRPL_ASSERT(
193 ctx.view.rules().enabled(featureSponsor),
194 "xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled");
195 addSponsorToLedgerEntry(sle, *sponsorSle, field);
196 }
197}
198
199void
201{
202 XRPL_ASSERT(
203 (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) ||
204 (sle->getType() != ltRIPPLE_STATE && field == sfSponsor),
205 "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry");
206 if (sle->isFieldPresent(field))
207 {
208 XRPL_ASSERT(
209 getCurrentTransactionRules()->enabled( // NOLINT(bugprone-unchecked-optional-access)
210 featureSponsor),
211 "xrpl::removeSponsorFromLedgerEntry : sponsor exists + Sponsor enabled");
212 sle->makeFieldAbsent(field);
213 }
214}
215
216bool
217isLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account)
218{
219 switch (sle.getType())
220 {
221 case ltCHECK:
222 case ltESCROW:
223 case ltPAYCHAN:
224 case ltMPTOKEN:
225 case ltDELEGATE:
226 case ltDEPOSIT_PREAUTH:
227 return sle.getAccountID(sfAccount) == account;
228 case ltMPTOKEN_ISSUANCE:
229 return sle.getAccountID(sfIssuer) == account;
230 case ltSIGNER_LIST: {
231 auto const signerList = view.read(keylet::signerList(account));
232 if (!signerList)
233 return false;
234 return signerList->key() == sle.key();
235 }
236 case ltCREDENTIAL: {
237 auto const& ownerField = sle.isFlag(lsfAccepted) ? sfSubject : sfIssuer;
238 return sle.getAccountID(ownerField) == account;
239 }
240 case ltRIPPLE_STATE: {
241 if (sle.isFlag(lsfHighReserve))
242 {
243 auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer();
244 if (highAccount == account)
245 return true;
246 }
247 if (sle.isFlag(lsfLowReserve))
248 {
249 auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer();
250 if (lowAccount == account)
251 return true;
252 }
253 // Reachable: the sponsee may be a third party or the side of the
254 // line that holds no reserve (e.g. the issuer). Callers map this
255 // to tecNO_PERMISSION.
256 return false;
257 }
258 default:
259 // LCOV_EXCL_START
260 UNREACHABLE("xrpl::isLedgerEntryOwner : object is not supported by sponsorship.");
261 return false;
262 // LCOV_EXCL_STOP
263 };
264}
265
266bool
268{
269 switch (sle.getType())
270 {
271 case ltCHECK:
272 case ltESCROW:
273 case ltPAYCHAN:
274 case ltMPTOKEN:
275 case ltDELEGATE:
276 case ltDEPOSIT_PREAUTH:
277 case ltMPTOKEN_ISSUANCE:
278 case ltSIGNER_LIST:
279 case ltCREDENTIAL:
280 case ltRIPPLE_STATE:
281 return true;
282 default:
283 return false;
284 };
285}
286
289{
290 switch (sle.getType())
291 {
292 case ltORACLE: {
293 return calculateOracleReserve(sle.getFieldArray(sfPriceDataSeries));
294 }
295 // Vaults require 2 owner counts (the vault and a pseudo-account)
296 case ltVAULT:
297 return 2;
298 case ltSIGNER_LIST: {
299 // Mirror SignerListSet's owner-count accounting so that create and
300 // delete agree. Modern lists (post-MultiSignReserve) carry the
301 // lsfOneOwnerCount flag and cost a single owner count. Legacy
302 // pre-MultiSignReserve lists cost 2 + signer_count owner counts
303 if (sle.isFlag(lsfOneOwnerCount))
304 return 1;
305 return 2 + static_cast<std::uint32_t>(sle.getFieldArray(sfSignerEntries).size());
306 }
307 case ltACCOUNT_ROOT:
308 // LCOV_EXCL_START
309 UNREACHABLE("AccountRoots are not supported by object sponsorship.");
310 return 0;
311 // LCOV_EXCL_STOP
312 default:
313 return 1;
314 }
315}
316
317SF_ACCOUNT const&
318getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner)
319{
320 switch (sle.getType())
321 {
322 case ltRIPPLE_STATE: {
323 if (sle.isFlag(lsfHighReserve))
324 {
325 auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer();
326 if (highAccount == owner)
327 return sfHighSponsor;
328 }
329 if (sle.isFlag(lsfLowReserve))
330 {
331 auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer();
332 if (lowAccount == owner)
333 return sfLowSponsor;
334 }
335 // LCOV_EXCL_START
336 UNREACHABLE("xrpl::getLedgerEntrySponsorField : unknown owner for RippleState");
337 return sfSponsor;
338 // LCOV_EXCL_STOP
339 }
340 default:
341 return sfSponsor;
342 }
343}
344
345} // namespace xrpl
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:134
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A view into a ledger.
Definition ReadView.h:41
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.
Definition Rules.cpp:180
AccountID const & getIssuer() const
Definition STAmount.h:516
std::shared_ptr< STLedgerEntry > const & ref
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
std::shared_ptr< STLedgerEntry > pointer
LedgerEntryType getType() const
std::shared_ptr< STLedgerEntry const > const & const_ref
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:688
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:643
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:657
T contains(T... args)
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:326
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TxType
Transaction type identifiers.
Definition TxFormats.h:45
std::optional< Rules > const & getCurrentTransactionRules()
Definition Rules.cpp:30
bool isReserveSponsored(STTx const &tx)
Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set).
std::optional< AccountID > getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const &field=sfSponsor)
Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent.
STLedgerEntry SLE
std::expected< SLE::pointer, TER > getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle)
The transaction's reserve sponsor for the given account, if applicable.
bool isReserveSponsorAllowed(TxType txType)
Whether the given transaction type may use reserve sponsorship (v1).
std::expected< SLE::pointer, TER > getTxReserveSponsor(ApplyViewContext ctx)
Return a mutable SLE for the transaction's reserve sponsor account.
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.
void removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const &field=sfSponsor)
Remove the reserve sponsor field from a ledger entry.
std::uint32_t getLedgerEntryOwnerCount(SLE const &sle)
Return the number of owner-count units the ledger entry consumes.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
std::uint32_t calculateOracleReserve(T const &priceDataSeries)
@ tecINTERNAL
Definition TER.h:313
SLE::pointer getLedgerEntryReserveSponsor(ApplyView &view, SLE::const_ref sle, SF_ACCOUNT const &field=sfSponsor)
Return a mutable SLE for the reserve sponsor recorded on a ledger entry.
std::optional< AccountID > getTxReserveSponsorID(STTx const &tx)
Return the AccountID of the transaction's reserve sponsor, or nullopt if unsponsored.
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...
TypedField< STAccount > SF_ACCOUNT
Definition SField.h:354
bool isLedgerEntryOwner(ReadView const &view, SLE const &sle, AccountID const &account)
Whether account is the owner of a ledger entry for sponsorship purposes.
SF_ACCOUNT const & getLedgerEntrySponsorField(SLE const &sle, AccountID const &owner)
Return the SField used to store the reserve sponsor for owner on sle.
bool isLedgerEntrySupportedBySponsorship(SLE const &sle)
Whether this ledger entry type can have a reserve sponsor attached to it.
Bundles the mutable ledger view and the transaction being applied.
Definition ApplyView.h:444
uint256 key
Definition Keylet.h:21
T unexpected(T... args)