From e5897751706d1a0189983cb3e997ac07700e27a3 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Thu, 3 Oct 2024 17:59:56 -0500 Subject: [PATCH] progress --- include/xrpl/protocol/AMMCore.h | 8 --- include/xrpl/protocol/STIssue.h | 9 +++ include/xrpl/protocol/STNumber.h | 2 + include/xrpl/protocol/STObject.h | 14 ++++ include/xrpl/protocol/detail/ledger_entries.m | 6 +- src/libxrpl/protocol/AMMCore.cpp | 12 ---- src/libxrpl/protocol/Keylet.cpp | 2 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 38 +--------- src/xrpld/app/tx/detail/VaultSet.cpp | 70 +++++++++++++------ src/xrpld/ledger/View.h | 6 ++ src/xrpld/ledger/detail/View.cpp | 47 +++++++++++++ 11 files changed, 134 insertions(+), 80 deletions(-) diff --git a/include/xrpl/protocol/AMMCore.h b/include/xrpl/protocol/AMMCore.h index 32988af5fc7..442f24d8785 100644 --- a/include/xrpl/protocol/AMMCore.h +++ b/include/xrpl/protocol/AMMCore.h @@ -48,14 +48,6 @@ class STObject; class STAmount; class Rules; -/** Calculate AMM account ID. - */ -AccountID -ammAccountID( - std::uint16_t prefix, - uint256 const& parentHash, - uint256 const& ammID); - /** Calculate Liquidity Provider Token (LPT) Currency. */ Currency diff --git a/include/xrpl/protocol/STIssue.h b/include/xrpl/protocol/STIssue.h index e97c2ab38ae..09a8c6c862e 100644 --- a/include/xrpl/protocol/STIssue.h +++ b/include/xrpl/protocol/STIssue.h @@ -45,6 +45,15 @@ class STIssue final : public STBase, CountedObject explicit STIssue(SField const& name); + STIssue& + operator=(STIssue const& rhs) = default; + STIssue& + operator=(Asset const& rhs) + { + asset_ = rhs; + return *this; + } + template TIss const& get() const; diff --git a/include/xrpl/protocol/STNumber.h b/include/xrpl/protocol/STNumber.h index b7aca4ee3b7..4303d039e24 100644 --- a/include/xrpl/protocol/STNumber.h +++ b/include/xrpl/protocol/STNumber.h @@ -31,6 +31,8 @@ class STNumber final : public STBase, public Number { public: + using value_type = Number; + STNumber(SField const& n, Number const& v = Number()); STNumber(SerialIter& sit, SField const& name); diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index e55351cbc24..0f9e07fd77e 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -517,6 +517,20 @@ class STObject::ValueProxy : private Proxy operator value_type() const; + template + friend bool + operator==(U&& lhs, STObject::ValueProxy const& rhs) + { + return rhs.value() == lhs; + } + + template + friend bool + operator!=(U&& lhs, STObject::ValueProxy const& rhs) + { + return !(lhs == rhs); + } + private: friend class STObject; diff --git a/include/xrpl/protocol/detail/ledger_entries.m b/include/xrpl/protocol/detail/ledger_entries.m index 2e46ccb1176..b4875624302 100644 --- a/include/xrpl/protocol/detail/ledger_entries.m +++ b/include/xrpl/protocol/detail/ledger_entries.m @@ -437,14 +437,14 @@ to implement skip lists that allow for efficient backwards (and, in {sfOwnerNode, soeREQUIRED}, {sfOwner, soeREQUIRED}, {sfAccount, soeREQUIRED}, - // no Data ever (YAGNI) + {sfData, soeOPTIONAL}, {sfAsset, soeREQUIRED}, {sfAssetTotal, soeREQUIRED}, {sfAssetAvailable, soeREQUIRED}, - {sfAssetMaximum, soeREQUIRED}, + {sfAssetMaximum, soeOPTIONAL}, {sfMPTokenIssuanceID, soeREQUIRED}, // sfShare // no ShareTotal ever (use MPTIssuance.sfOutstandingAmount) // no WithdrawalPolicy ever (YAGNI) // no PermissionedDomainID yet - {sfLossUnrealized, soeREQUIRED}, // not in spec + {sfLossUnrealized, soeOPTIONAL}, // not in spec })) diff --git a/src/libxrpl/protocol/AMMCore.cpp b/src/libxrpl/protocol/AMMCore.cpp index 7ac27041e76..57ff0fd376a 100644 --- a/src/libxrpl/protocol/AMMCore.cpp +++ b/src/libxrpl/protocol/AMMCore.cpp @@ -26,18 +26,6 @@ namespace ripple { -AccountID -ammAccountID( - std::uint16_t prefix, - uint256 const& parentHash, - uint256 const& ammID) -{ - ripesha_hasher rsh; - auto const hash = sha512Half(prefix, parentHash, ammID); - rsh(hash.data(), hash.size()); - return AccountID{static_cast(rsh)}; -} - Currency ammLPTCurrency(Currency const& cur1, Currency const& cur2) { diff --git a/src/libxrpl/protocol/Keylet.cpp b/src/libxrpl/protocol/Keylet.cpp index 23847811d3b..f5c1fb1b553 100644 --- a/src/libxrpl/protocol/Keylet.cpp +++ b/src/libxrpl/protocol/Keylet.cpp @@ -33,7 +33,7 @@ Keylet::check(STLedgerEntry const& sle) const if (type == ltCHILD) return sle.getType() != ltDIR_NODE; - return sle.getType() == type; + return sle.getType() == type && sle.key() == key; } } // namespace ripple diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 237e1afa240..54881832409 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -214,18 +214,7 @@ applyCreate( auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue()); // Mitigate same account exists possibility - auto const ammAccount = [&]() -> Expected { - std::uint16_t constexpr maxAccountAttempts = 256; - for (auto p = 0; p < maxAccountAttempts; ++p) - { - auto const ammAccount = - ammAccountID(p, sb.info().parentHash, ammKeylet.key); - if (!sb.read(keylet::account(ammAccount))) - return ammAccount; - } - return Unexpected(tecDUPLICATE); - }(); - + auto const ammAccount = createPseudoAccount(sb, ammKeylet.key); // AMM account already exists (should not happen) if (!ammAccount) { @@ -243,28 +232,14 @@ applyCreate( } // Create AMM Root Account. - auto sleAMMRoot = std::make_shared(keylet::account(*ammAccount)); - sleAMMRoot->setAccountID(sfAccount, *ammAccount); - sleAMMRoot->setFieldAmount(sfBalance, STAmount{}); - std::uint32_t const seqno{ - ctx_.view().rules().enabled(featureDeletableAccounts) - ? ctx_.view().seq() - : 1}; - sleAMMRoot->setFieldU32(sfSequence, seqno); - // Ignore reserves requirement, disable the master key, allow default - // rippling (AMM LPToken can be used in payments and offer crossing but - // not as a token in another AMM), and enable deposit authorization to - // prevent payments into AMM. + auto sleAMMRoot = sb.peek(keylet::account(*ammAccount)); // Note, that the trustlines created by AMM have 0 credit limit. // This prevents shifting the balance between accounts via AMM, // or sending unsolicited LPTokens. This is a desired behavior. // A user can only receive LPTokens through affirmative action - // either an AMMDeposit, TrustSet, crossing an offer, etc. - sleAMMRoot->setFieldU32( - sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); // Link the root account and AMM object sleAMMRoot->setFieldH256(sfAMMID, ammKeylet.key); - sb.insert(sleAMMRoot); // Calculate initial LPT balance. auto const lpTokens = ammLPTokens(amount, amount2, lptIss); @@ -281,14 +256,7 @@ applyCreate( ctx_.view(), ammSle, account_, lptIss, ctx_.tx[sfTradingFee]); // Add owner directory to link the root account and AMM object. - if (auto const page = sb.dirInsert( - keylet::ownerDir(*ammAccount), - ammSle->key(), - describeOwnerDir(*ammAccount))) - { - ammSle->setFieldU64(sfOwnerNode, *page); - } - else + if (auto ter = dirLink(sb, *ammAccount, ammSle); ter) { JLOG(j_.debug()) << "AMM Instance: failed to insert owner dir"; return {tecDIR_FULL, false}; diff --git a/src/xrpld/app/tx/detail/VaultSet.cpp b/src/xrpld/app/tx/detail/VaultSet.cpp index 0f62e6e09cc..a20a94d3002 100644 --- a/src/xrpld/app/tx/detail/VaultSet.cpp +++ b/src/xrpld/app/tx/detail/VaultSet.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -43,47 +44,74 @@ VaultSet::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } -#define TX_ASSERT(condition) condition -#undef TX_ASSERT -#define TX_ASSERT(condition) -#define TX_MATCH(...) -#define TX_OVERWRITE(lhs, rhs) \ - if (rhs) \ - { \ - lhs = rhs; \ - } -#undef TX_OVERWRITE -#define TX_OVERWRITE(lhs, rhs) - TER VaultSet::doApply() { + // All return codes in `doApply` must be `tec`, `ter`, or `tes`. + // As we move checks into `preflight` and `preclaim`, + // we can consider downgrading them to `tef` or `tem`. + auto const& tx = ctx_.tx; auto const& vaultId = tx[~sfVaultID]; - auto klVault = vaultId ? Keylet{ltVAULT, *vaultId} - : keylet::vault(tx[sfAccount], tx[sfSequence]); + auto keylet = vaultId ? Keylet{ltVAULT, *vaultId} + : keylet::vault(tx[sfAccount], tx[sfSequence]); - auto vault = view().peek(klVault); + auto vault = view().peek(keylet); if (vault) { - TX_ASSERT(vault->getType() == ltVAULT); - TX_ASSERT(vault->key() == klVault.key); - TX_ASSERT((*vault)[sfOwner] == tx[sfAccount]); - TX_MATCH((*vault)[sfAsset], tx[~sfAsset]); + // Update existing object. + // Assert that submitter is the Owner. + if (tx[sfAccount] != (*vault)[sfOwner]) + return tecNO_PERMISSION; + // Assert that Asset is the same if given. + if (tx.isFieldPresent(sfAsset) && tx[sfAsset] != (*vault)[sfAsset]) + return tecNO_PERMISSION; view().update(vault); } else if (tx.isFieldPresent(sfVaultID)) { + // Update missing object. return tecOBJECT_NOT_FOUND; } else { - // create new vault + // Create new object. + if (!tx.isFieldPresent(sfAsset)) + return tecINCOMPLETE; + + vault = std::make_shared(keylet); + if (auto ter = dirLink(view(), tx[sfAccount], vault); ter) + return ter; + auto pseudo = createPseudoAccount(view(), vault->key()); + if (!pseudo) + return pseudo.error(); + (*vault)[sfSequence] = tx[sfSequence]; + (*vault)[sfOwner] = tx[sfAccount]; + (*vault)[sfAccount] = pseudo.value(); + (*vault)[~sfData] = tx[~sfData]; + (*vault)[sfAsset] = tx[sfAsset]; + (*vault)[sfAssetTotal] = 0; + (*vault)[sfAssetAvailable] = 0; + (*vault)[~sfAssetMaximum] = tx[~sfAssetMaximum]; + // TODO: create MPT for share + // No `LossUnrealized`. + view().insert(vault); } - TX_OVERWRITE((*vault)[sfAssetMaximum], tx[~sfAssetMaximum]); + // Set or clear field AssetMaximum. + if (tx.isFieldPresent(sfAssetMaximum)) + { + if (tx[sfAssetMaximum] == 0) + { + vault->delField(sfAssetMaximum); + } + else + { + (*vault)[sfAssetMaximum] = tx[sfAssetMaximum]; + } + } return tesSUCCESS; } diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 44125d7f84f..1e9b3a47133 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -383,6 +383,12 @@ dirNext( [[nodiscard]] std::function describeOwnerDir(AccountID const& account); +[[nodiscard]] TER +dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr& object); + +[[nodiscard]] Expected +createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey); + // VFALCO NOTE Both STAmount parameters should just // be "Amount", a unit-less number. // diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index e1d85d73422..bc56d3def6d 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -832,6 +833,52 @@ describeOwnerDir(AccountID const& account) }; } +TER +dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr& object) +{ + auto const page = view.dirInsert( + keylet::ownerDir(owner), object->key(), describeOwnerDir(owner)); + if (!page) + return tecDIR_FULL; + object->setFieldU64(sfOwnerNode, *page); + return tesSUCCESS; +} + +Expected +createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey) +{ + AccountID accountId; + for (auto i = 0;; ++i) + { + if (i >= 256) + return Unexpected(tecDUPLICATE); + ripesha_hasher rsh; + auto const hash = sha512Half(i, view.info().parentHash, pseudoOwnerKey); + rsh(hash.data(), hash.size()); + accountId = static_cast(rsh); + if (!view.read(keylet::account(accountId))) + break; + } + + // Create pseudo-account. + auto account = std::make_shared(keylet::account(accountId)); + account->setAccountID(sfAccount, accountId); + account->setFieldAmount(sfBalance, STAmount{}); + std::uint32_t const seqno{ + view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1}; + account->setFieldU32(sfSequence, seqno); + // Ignore reserves requirement, disable the master key, allow default + // rippling, and enable deposit authorization to prevent payments into + // pseudo-account. + account->setFieldU32( + sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); + // Link the pseudo-account with its owner object. + // account->setFieldH256(sfPseudoOwner, pseudoOwnerKey); + view.insert(account); + + return accountId; +} + TER trustCreate( ApplyView& view,