diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 6a20f76ea3..b87bc3f8a4 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -225,6 +225,8 @@ enum TERcodes : TERUnderlyingType { terQUEUED, // Transaction is being held in TxQ until fee drops terPRE_TICKET, // Ticket is not yet in ledger but might be on its way terNO_AMM, // AMM doesn't exist for the asset pair + terADDRESS_COLLISION, // Failed to allocate AccountID when trying to + // create a pseudo-account }; //------------------------------------------------------------------------------ diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 87e07bc7c9..d14d8e3861 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -231,6 +231,7 @@ transResults() MAKE_ERROR(terQUEUED, "Held until escalated fee drops."), MAKE_ERROR(terPRE_TICKET, "Ticket is not yet in ledger."), MAKE_ERROR(terNO_AMM, "AMM doesn't exist for the asset pair."), + MAKE_ERROR(terADDRESS_COLLISION, "Failed to allocate an unique account address"), MAKE_ERROR(tesSUCCESS, "The transaction was applied. Only final in a validated ledger."), }; diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index d2f9736e5b..1abfbccdd7 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -119,6 +119,12 @@ VaultCreate::preclaim(PreclaimContext const& ctx) return tecOBJECT_NOT_FOUND; } + auto sequence = ctx.tx.getSeqValue(); + if (auto const accountId = pseudoAccountAddress( + ctx.view, keylet::vault(account, sequence).key); + accountId == beast::zero) + return terADDRESS_COLLISION; + return tesSUCCESS; } diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 3232b023e7..ea2eee098c 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -488,6 +488,9 @@ describeOwnerDir(AccountID const& account); [[nodiscard]] TER dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr& object); +AccountID +pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey); + // Which of the owner-object fields should we set: sfAMMID, sfVaultID enum class PseudoAccountOwnerType : int { AMM, Vault }; diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 39e2c4d665..c752ac2963 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1037,29 +1037,31 @@ dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr& object) return tesSUCCESS; } +AccountID +pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) +{ + AccountID ret = beast::zero; + // This number must not be changed without an amendment + constexpr int maxAccountAttempts = 256; + for (auto i = 0; i < maxAccountAttempts; ++i) + { + ripesha_hasher rsh; + auto const hash = sha512Half(i, view.info().parentHash, pseudoOwnerKey); + rsh(hash.data(), hash.size()); + ret = static_cast(rsh); + if (!view.read(keylet::account(ret))) + return ret; + } + return ret; +} + Expected, TER> createPseudoAccount( ApplyView& view, uint256 const& pseudoOwnerKey, PseudoAccountOwnerType type) { - auto const accountId = [&]() -> AccountID { - AccountID ret = beast::zero; - // This number must not be changed without an amendment - constexpr int maxAccountAttempts = 256; - for (auto i = 0; i < maxAccountAttempts; ++i) - { - ripesha_hasher rsh; - auto const hash = - sha512Half(i, view.info().parentHash, pseudoOwnerKey); - rsh(hash.data(), hash.size()); - ret = static_cast(rsh); - if (!view.read(keylet::account(ret))) - return ret; - } - return ret; - }(); - + auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey); if (accountId == beast::zero) return Unexpected(tecDUPLICATE);