rippled
Loading...
Searching...
No Matches
AccountRootHelpers.cpp
1#include <xrpl/ledger/helpers/AccountRootHelpers.h>
2//
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/LedgerFormats.h>
7#include <xrpl/protocol/digest.h>
8
9#include <algorithm>
10#include <limits>
11
12namespace xrpl {
13
14bool
15isGlobalFrozen(ReadView const& view, AccountID const& issuer)
16{
17 if (isXRP(issuer))
18 return false;
19 if (auto const sle = view.read(keylet::account(issuer)))
20 return sle->isFlag(lsfGlobalFreeze);
21 return false;
22}
23
24// An owner count cannot be negative. If adjustment would cause a negative
25// owner count, clamp the owner count at 0. Similarly for overflow. This
26// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
27// If id != std::nullopt, then do error reporting.
28//
29// Returns adjusted owner count.
30static std::uint32_t
33 std::int32_t adjustment,
36{
37 std::uint32_t adjusted{current + adjustment};
38 if (adjustment > 0)
39 {
40 // Overflow is well defined on unsigned
41 if (adjusted < current)
42 {
43 if (id)
44 {
45 JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!";
46 }
48 }
49 }
50 else
51 {
52 // Underflow is well defined on unsigned
53 if (adjusted > current)
54 {
55 if (id)
56 {
57 JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!";
58 }
59 adjusted = 0;
60 XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set");
61 }
62 }
63 return adjusted;
64}
65
66XRPAmount
67xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
68{
69 auto const sle = view.read(keylet::account(id));
70 if (sle == nullptr)
71 return beast::zero;
72
73 // Return balance minus reserve
74 std::uint32_t const ownerCount =
75 confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
76
77 // Pseudo-accounts have no reserve requirement
78 auto const reserve =
79 isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount);
80
81 auto const fullBalance = sle->getFieldAmount(sfBalance);
82
83 auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
84
85 STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve;
86
87 JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
88 << " amount=" << amount.getFullText()
89 << " fullBalance=" << fullBalance.getFullText()
90 << " balance=" << balance.getFullText() << " reserve=" << reserve
91 << " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj;
92
93 return amount.xrp();
94}
95
96Rate
97transferRate(ReadView const& view, AccountID const& issuer)
98{
99 auto const sle = view.read(keylet::account(issuer));
100
101 if (sle && sle->isFieldPresent(sfTransferRate))
102 return Rate{sle->getFieldU32(sfTransferRate)};
103
104 return parityRate;
105}
106
107void
109 ApplyView& view,
110 std::shared_ptr<SLE> const& sle,
111 std::int32_t amount,
113{
114 if (!sle)
115 return;
116 XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input");
117 std::uint32_t const current{sle->getFieldU32(sfOwnerCount)};
118 AccountID const id = (*sle)[sfAccount];
119 std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
120 view.adjustOwnerCountHook(id, current, adjusted);
121 sle->at(sfOwnerCount) = adjusted;
122 view.update(sle);
123}
124
126pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
127{
128 // This number must not be changed without an amendment
129 constexpr std::uint16_t maxAccountAttempts = 256;
130 for (std::uint16_t i = 0; i < maxAccountAttempts; ++i)
131 {
132 ripesha_hasher rsh;
133 auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
134 rsh(hash.data(), hash.size());
135 AccountID const ret{static_cast<ripesha_hasher::result_type>(rsh)};
136 if (!view.read(keylet::account(ret)))
137 return ret;
138 }
139 return beast::zero;
140}
141
142// Pseudo-account designator fields MUST be maintained by including the
143// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
144// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
145// since a non-active amendment will not set any field, by definition.
146// Specific properties of a pseudo-account are NOT checked here, that's what
147// InvariantCheck is for.
148[[nodiscard]] std::vector<SField const*> const&
150{
151 static std::vector<SField const*> const pseudoFields = []() {
152 auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
153 if (!ar)
154 {
155 // LCOV_EXCL_START
157 "xrpl::getPseudoAccountFields : unable to find account root "
158 "ledger format");
159 // LCOV_EXCL_STOP
160 }
161 auto const& soTemplate = ar->getSOTemplate();
162
163 std::vector<SField const*> pseudoFields;
164 for (auto const& field : soTemplate)
165 {
166 if (field.sField().shouldMeta(SField::sMD_PseudoAccount))
167 pseudoFields.emplace_back(&field.sField());
168 }
169 return pseudoFields;
170 }();
171 return pseudoFields;
172}
173
174[[nodiscard]] bool
177 std::set<SField const*> const& pseudoFieldFilter)
178{
179 auto const& fields = getPseudoAccountFields();
180
181 // Intentionally use defensive coding here because it's cheap and makes the
182 // semantics of true return value clean.
183 return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
185 fields.begin(), fields.end(), [&sleAcct, &pseudoFieldFilter](SField const* sf) -> bool {
186 return sleAcct->isFieldPresent(*sf) &&
187 (pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf));
188 }) > 0;
189}
190
191Expected<std::shared_ptr<SLE>, TER>
192createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField)
193{
194 [[maybe_unused]]
195 auto const& fields = getPseudoAccountFields();
196 XRPL_ASSERT(
198 fields.begin(),
199 fields.end(),
200 [&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1,
201 "xrpl::createPseudoAccount : valid owner field");
202
203 auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey);
204 if (accountId == beast::zero)
205 return Unexpected(tecDUPLICATE);
206
207 // Create pseudo-account.
208 auto account = std::make_shared<SLE>(keylet::account(accountId));
209 account->setAccountID(sfAccount, accountId);
210 account->setFieldAmount(sfBalance, STAmount{});
211
212 // Pseudo-accounts can't submit transactions, so set the sequence number
213 // to 0 to make them easier to spot and verify, and add an extra level
214 // of protection.
215 std::uint32_t const seqno = //
216 view.rules().enabled(featureSingleAssetVault) || //
217 view.rules().enabled(featureLendingProtocol) //
218 ? 0 //
219 : view.seq();
220 account->setFieldU32(sfSequence, seqno);
221 // Ignore reserves requirement, disable the master key, allow default
222 // rippling, and enable deposit authorization to prevent payments into
223 // pseudo-account.
224 account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
225 // Link the pseudo-account with its owner object.
226 account->setFieldH256(ownerField, pseudoOwnerKey);
227
228 view.insert(account);
229
230 return account;
231}
232
233[[nodiscard]] TER
234checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
235{
236 if (toSle == nullptr)
237 return tecNO_DST;
238
239 // The tag is basically account-specific information we don't
240 // understand, but we can require someone to fill it in.
241 if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag)
242 return tecDST_TAG_NEEDED; // Cannot send without a tag
243
244 return tesSUCCESS;
245}
246
247} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
static Sink & getNullSink()
Returns a Sink which does nothing.
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:116
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next)
Definition ApplyView.h:227
Item const * findByType(KeyType type) const
Retrieve a format based on its type.
static LedgerFormats const & getInstance()
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition ReadView.h:155
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:97
virtual std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const
Definition ReadView.h:166
virtual std::shared_ptr< SLE const > 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:120
Identifies fields.
Definition SField.h:126
@ sMD_PseudoAccount
Definition SField.h:136
T count_if(T... args)
T emplace_back(T... args)
T is_same_v
T max(T... args)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::vector< SField const * > const & getPseudoAccountFields()
Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if set.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Generate a pseudo-account address from a pseudo owner key.
bool isXRP(AccountID const &c)
Definition AccountID.h:70
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:204
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
@ current
This was a new validation and was added.
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
static std::uint32_t confineOwnerCount(std::uint32_t current, std::int32_t adjustment, std::optional< AccountID > const &id=std::nullopt, beast::Journal j=beast::Journal{beast::Journal::getNullSink()})
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Check if the issuer has the global freeze flag set.
TERSubset< CanCvtToTER > TER
Definition TER.h:622
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecDST_TAG_NEEDED
Definition TER.h:290
@ tecDUPLICATE
Definition TER.h:296
@ tecNO_DST
Definition TER.h:271
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
TER checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
Checks the destination and tag.
@ tesSUCCESS
Definition TER.h:225
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Represents a transfer rate.
Definition Rate.h:20
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
Definition digest.h:116