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