-
Notifications
You must be signed in to change notification settings - Fork 1.7k
refactor: Start making ledger helpers type-safe, beginning with AccountRoot
#6620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 82 commits
659f455
4157e36
a8987cf
e0f487b
43caa7d
e6369c0
b5562cc
1cc7424
1ccd84e
c24432f
add3d7e
0ffb3e2
7900fa9
56c173a
a423813
f41c02d
9b42e17
638477c
deaa23f
e9c4ed6
5b0b1ff
c0895c6
dcdc5e1
819d3fc
e928781
ffd96e3
fa77338
5096811
b3829e5
27e9d0d
e2238b1
b0ca6ad
97629e7
737d128
03863d9
945b4f7
8269964
2d64509
9c9281e
683ff84
b99a766
be9913f
d616aff
c00491b
7b27096
ea3ca12
3d9eba7
0de37c2
dc2430e
fabf328
fb37cc2
5dbbd9b
d10bb71
d7b6c8e
3d2e078
700cd6a
cedebd5
ffaf510
bcd2639
6cb5842
7661483
554c0ba
144835c
1306d35
9b1406a
8c80a6a
b557403
d38f457
fb76e29
0ac76df
07b3d2d
12d8221
7add8b3
e5555c5
19d1b24
1ac402f
c14fdd7
07f3954
1015e7c
45a4585
b1f4e61
77120e7
f4cb0b8
805d12d
3a5eb97
7098e49
052bb02
f91fbc9
095a588
a0d2d17
6c7a8ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,8 +2,10 @@ | |||||||
|
|
||||||||
| #include <xrpl/basics/Expected.h> | ||||||||
| #include <xrpl/beast/utility/Journal.h> | ||||||||
| #include <xrpl/beast/utility/instrumentation.h> | ||||||||
| #include <xrpl/ledger/ApplyView.h> | ||||||||
| #include <xrpl/ledger/ReadView.h> | ||||||||
| #include <xrpl/ledger/helpers/SLEBase.h> | ||||||||
| #include <xrpl/protocol/Indexes.h> | ||||||||
| #include <xrpl/protocol/Rate.h> | ||||||||
| #include <xrpl/protocol/STLedgerEntry.h> | ||||||||
|
|
@@ -14,42 +16,160 @@ | |||||||
|
|
||||||||
| namespace xrpl { | ||||||||
|
|
||||||||
| /** Check if the issuer has the global freeze flag set. | ||||||||
| @param issuer The account to check | ||||||||
| @return true if the account has global freeze set | ||||||||
| */ | ||||||||
| [[nodiscard]] bool | ||||||||
| isGlobalFrozen(ReadView const& view, AccountID const& issuer); | ||||||||
|
|
||||||||
| // Calculate liquid XRP balance for an account. | ||||||||
| // This function may be used to calculate the amount of XRP that | ||||||||
| // the holder is able to freely spend. It subtracts reserve requirements. | ||||||||
| // | ||||||||
| // ownerCountAdj adjusts the owner count in case the caller calculates | ||||||||
| // before ledger entries are added or removed. Positive to add, negative | ||||||||
| // to subtract. | ||||||||
| // | ||||||||
| // @param ownerCountAdj positive to add to count, negative to reduce count. | ||||||||
| [[nodiscard]] XRPAmount | ||||||||
| xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); | ||||||||
|
|
||||||||
| /** Adjust the owner count up or down. */ | ||||||||
| void | ||||||||
| adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j); | ||||||||
|
|
||||||||
| /** Returns IOU issuer transfer fee as Rate. Rate specifies | ||||||||
| * the fee as fractions of 1 billion. For example, 1% transfer rate | ||||||||
| * is represented as 1,010,000,000. | ||||||||
| * @param issuer The IOU issuer | ||||||||
| /** | ||||||||
| * View-parameterized wrapper for AccountRoot ledger entries. | ||||||||
| * | ||||||||
| * AccountRoot<ReadView> — read-only access to account data | ||||||||
| * AccountRoot<ApplyView> — read-write access, with insert/update/erase | ||||||||
| * and domain-specific write methods | ||||||||
| */ | ||||||||
| [[nodiscard]] Rate | ||||||||
| transferRate(ReadView const& view, AccountID const& issuer); | ||||||||
| template <typename ViewT> | ||||||||
|
Tapanito marked this conversation as resolved.
|
||||||||
| class AccountRoot : public SLEBase<ViewT> | ||||||||
|
Tapanito marked this conversation as resolved.
Tapanito marked this conversation as resolved.
|
||||||||
| { | ||||||||
| static constexpr bool kIsWritable = SLEBase<ViewT>::kIsWritable; | ||||||||
|
|
||||||||
| AccountID const id_; | ||||||||
|
|
||||||||
| public: | ||||||||
| /** Constructor for read-only context */ | ||||||||
| AccountRoot( | ||||||||
| AccountID const& id, | ||||||||
| ReadView const& view, | ||||||||
| beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) | ||||||||
| requires(!kIsWritable) | ||||||||
| : SLEBase<ViewT>(view.read(keylet::account(id)), view, j), id_(id) | ||||||||
| { | ||||||||
| } | ||||||||
|
|
||||||||
| /** Constructor for writable context */ | ||||||||
| AccountRoot( | ||||||||
| AccountID const& id, | ||||||||
| ApplyView& view, | ||||||||
| beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) | ||||||||
| requires kIsWritable | ||||||||
| : SLEBase<ViewT>(keylet::account(id), view, j), id_(id) | ||||||||
| { | ||||||||
| } | ||||||||
|
|
||||||||
| /** Converting constructor: writable → read-only. */ | ||||||||
| template <WritableView OtherViewT> | ||||||||
| AccountRoot(AccountRoot<OtherViewT> const& other) | ||||||||
| requires(!kIsWritable) | ||||||||
| : SLEBase<ViewT>(other), id_(other.id()) | ||||||||
| { | ||||||||
| } | ||||||||
|
|
||||||||
| /** Create an AccountRoot backed by a brand-new SLE. | ||||||||
| */ | ||||||||
| [[nodiscard]] static AccountRoot | ||||||||
| makeNew( | ||||||||
| AccountID const& id, | ||||||||
| ApplyView& view, | ||||||||
| beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) | ||||||||
| requires kIsWritable | ||||||||
| { | ||||||||
| return AccountRoot(id, view, j, std::make_shared<SLE>(keylet::account(id))); | ||||||||
| } | ||||||||
|
|
||||||||
| [[nodiscard]] AccountID const& | ||||||||
| id() const | ||||||||
| { | ||||||||
| return id_; | ||||||||
| } | ||||||||
|
|
||||||||
| // --- Read-only domain methods (available on both specializations) --- | ||||||||
|
|
||||||||
| /** Check if the issuer has the global freeze flag set. | ||||||||
| @return true if the account has global freeze set | ||||||||
| */ | ||||||||
| [[nodiscard]] bool | ||||||||
| isGlobalFrozen() const; | ||||||||
|
|
||||||||
| /** Returns IOU issuer transfer fee as Rate. Rate specifies | ||||||||
| * the fee as fractions of 1 billion. For example, 1% transfer rate | ||||||||
| * is represented as 1,010,000,000. | ||||||||
| */ | ||||||||
| [[nodiscard]] Rate | ||||||||
| transferRate() const; | ||||||||
|
|
||||||||
| // Calculate liquid XRP balance for an account. | ||||||||
| // This function may be used to calculate the amount of XRP that | ||||||||
| // the holder is able to freely spend. It subtracts reserve requirements. | ||||||||
| // | ||||||||
| // ownerCountAdj adjusts the owner count in case the caller calculates | ||||||||
| // before ledger entries are added or removed. Positive to add, negative | ||||||||
| // to subtract. | ||||||||
| // | ||||||||
| // @param ownerCountAdj positive to add to count, negative to reduce count. | ||||||||
| [[nodiscard]] XRPAmount | ||||||||
| xrpLiquid(std::int32_t ownerCountAdj) const; | ||||||||
|
|
||||||||
| /** Checks the destination and tag. | ||||||||
|
|
||||||||
| - Checks that the SLE is not null. | ||||||||
| - If the SLE requires a destination tag, checks that there is a tag. | ||||||||
| */ | ||||||||
| [[nodiscard]] TER | ||||||||
| checkDestinationAndTag(bool hasDestinationTag) const; | ||||||||
|
|
||||||||
| /** Returns true if and only if sleAcct is a pseudo-account or specific | ||||||||
| pseudo-accounts in pseudoFieldFilter. | ||||||||
|
|
||||||||
| Returns false if sleAcct is: | ||||||||
| - NOT a pseudo-account OR | ||||||||
| - NOT a ltACCOUNT_ROOT OR | ||||||||
| - null pointer | ||||||||
| */ | ||||||||
| [[nodiscard]] bool | ||||||||
| isPseudoAccount(std::set<SField const*> const& pseudoFieldFilter = {}) const; | ||||||||
|
|
||||||||
| [[nodiscard]] bool | ||||||||
| operator==(AccountRoot const& other) const | ||||||||
| { | ||||||||
| return id_ == other.id_; | ||||||||
| } | ||||||||
|
|
||||||||
| [[nodiscard]] bool | ||||||||
| operator==(AccountID const& other) const | ||||||||
| { | ||||||||
| return id_ == other; | ||||||||
| } | ||||||||
|
|
||||||||
| // --- Write-only domain methods (compile-time gated) --- | ||||||||
|
|
||||||||
| /** Adjust the owner count up or down. */ | ||||||||
| void | ||||||||
| adjustOwnerCount(std::int32_t amount) | ||||||||
| requires kIsWritable; | ||||||||
|
|
||||||||
| private: | ||||||||
| // Private constructor only used by `makeNew` | ||||||||
| AccountRoot(AccountID const& id, ApplyView& view, beast::Journal j, std::shared_ptr<SLE> sle) | ||||||||
| requires kIsWritable | ||||||||
| : SLEBase<ViewT>(std::move(sle), view, j), id_(id) | ||||||||
| { | ||||||||
| this->insert(); | ||||||||
| } | ||||||||
| }; | ||||||||
|
|
||||||||
| // CTAD deduction guide — bare AccountRoot(id, view) always deduces read-only. | ||||||||
| // For writable access, use WAccountRoot(id, applyView) explicitly. | ||||||||
|
Comment on lines
+157
to
+158
|
||||||||
| AccountRoot(AccountID const&, ReadView const&) -> AccountRoot<ReadView>; | ||||||||
| AccountRoot(AccountID const&, ReadView const&, beast::Journal) -> AccountRoot<ReadView>; | ||||||||
|
|
||||||||
| // Backward-compatible aliases | ||||||||
| using RAccountRoot = AccountRoot<ReadView>; | ||||||||
| using WAccountRoot = AccountRoot<ApplyView>; | ||||||||
|
|
||||||||
|
mvadari marked this conversation as resolved.
|
||||||||
| // Explicit instantiation declarations (definitions in .cpp) | ||||||||
| extern template class AccountRoot<ReadView>; | ||||||||
| extern template class AccountRoot<ApplyView>; | ||||||||
|
|
||||||||
| /** Generate a pseudo-account address from a pseudo owner key. | ||||||||
| @param pseudoOwnerKey The key to generate the address from | ||||||||
| @return The generated account ID | ||||||||
| */ | ||||||||
| AccountID | ||||||||
| [[nodiscard]] AccountID | ||||||||
| pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey); | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a good rule of thumb would be that anything that takes a At the same time, since
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's the rule of thumb I've been using as well. |
||||||||
|
|
||||||||
| /** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account | ||||||||
|
|
@@ -62,27 +182,6 @@ | |||||||
| [[nodiscard]] std::vector<SField const*> const& | ||||||||
| getPseudoAccountFields(); | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||
|
|
||||||||
| /** Returns true if and only if sleAcct is a pseudo-account or specific | ||||||||
| pseudo-accounts in pseudoFieldFilter. | ||||||||
|
|
||||||||
| Returns false if sleAcct is: | ||||||||
| - NOT a pseudo-account OR | ||||||||
| - NOT a ltACCOUNT_ROOT OR | ||||||||
| - null pointer | ||||||||
| */ | ||||||||
| [[nodiscard]] bool | ||||||||
| isPseudoAccount(SLE::const_pointer sleAcct, std::set<SField const*> const& pseudoFieldFilter = {}); | ||||||||
|
|
||||||||
| /** Convenience overload that reads the account from the view. */ | ||||||||
| [[nodiscard]] inline bool | ||||||||
| isPseudoAccount( | ||||||||
| ReadView const& view, | ||||||||
| AccountID const& accountId, | ||||||||
| std::set<SField const*> const& pseudoFieldFilter = {}) | ||||||||
| { | ||||||||
| return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Create pseudo-account, storing pseudoOwnerKey into ownerField. | ||||||||
| * | ||||||||
|
|
@@ -94,12 +193,4 @@ | |||||||
| [[nodiscard]] Expected<SLE::pointer, TER> | ||||||||
| createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); | ||||||||
|
|
||||||||
| /** Checks the destination and tag. | ||||||||
|
|
||||||||
| - Checks that the SLE is not null. | ||||||||
| - If the SLE requires a destination tag, checks that there is a tag. | ||||||||
| */ | ||||||||
| [[nodiscard]] TER | ||||||||
| checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag); | ||||||||
|
|
||||||||
| } // namespace xrpl | ||||||||
Uh oh!
There was an error while loading. Please reload this page.