diff --git a/src/ripple/app/tx/impl/Escrow.cpp b/src/ripple/app/tx/impl/Escrow.cpp index a5b2047069f..c3a68d8d381 100644 --- a/src/ripple/app/tx/impl/Escrow.cpp +++ b/src/ripple/app/tx/impl/Escrow.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +31,8 @@ #include #include #include -#include + +#include // During an EscrowFinish, the transaction must specify both // a condition and a fulfillment. We track whether that @@ -228,9 +228,9 @@ EscrowCreate::doApply() return tecNO_TARGET; } - // Create escrow in ledger. Note that we we use the value from the + // Create escrow in ledger. Note that we use the value from the // sequence or ticket. For more explanation see comments in SeqProxy.h. - Keylet const escrowKeylet = + EscrowKeylet const escrowKeylet = keylet::escrow(account, ctx_.tx.getSeqProxy().value()); auto const slep = std::make_shared(escrowKeylet); (*slep)[sfAmount] = ctx_.tx[sfAmount]; @@ -355,8 +355,8 @@ TER EscrowFinish::doApply() { auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); - auto const slep = ctx_.view().peekSLE(k); - if (!slep) + std::optional escrowObject = ctx_.view().peek(k); + if (!escrowObject) return tecNO_TARGET; // If a cancel time is present, a finish operation should only succeed prior @@ -367,25 +367,27 @@ EscrowFinish::doApply() auto const now = ctx_.view().info().parentCloseTime; // Too soon: can't execute before the finish time - if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter])) + if (escrowObject->finishTime() && + !after(now, *escrowObject->finishTime())) return tecNO_PERMISSION; // Too late: can't execute after the cancel time - if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter])) + if (escrowObject->cancelTime() && + after(now, *escrowObject->cancelTime())) return tecNO_PERMISSION; } else { // Too soon? - if ((*slep)[~sfFinishAfter] && + if (escrowObject->finishTime() && ctx_.view().info().parentCloseTime.time_since_epoch().count() <= - (*slep)[sfFinishAfter]) + escrowObject->finishTime()) return tecNO_PERMISSION; // Too late? - if ((*slep)[~sfCancelAfter] && + if (escrowObject->cancelTime() && ctx_.view().info().parentCloseTime.time_since_epoch().count() <= - (*slep)[sfCancelAfter]) + escrowObject->cancelTime()) return tecNO_PERMISSION; } @@ -420,7 +422,7 @@ EscrowFinish::doApply() return tecCRYPTOCONDITION_ERROR; // Check against condition in the ledger entry: - auto const cond = (*slep)[~sfCondition]; + auto const cond = escrowObject->checkCondition(); // If a condition wasn't specified during creation, // one shouldn't be included now. @@ -437,7 +439,7 @@ EscrowFinish::doApply() } // NOTE: Escrow payments cannot be used to fund accounts. - AccountID const destID = (*slep)[sfDestination]; + AccountID const destID = escrowObject->getEscrowRecipient(); auto destAcctRoot = ctx_.view().peek(keylet::account(destID)); if (!destAcctRoot) return tecNO_DST; @@ -459,11 +461,11 @@ EscrowFinish::doApply() } } - AccountID const account = (*slep)[sfAccount]; + AccountID const account = escrowObject->accountID(); // Remove escrow from owner directory { - auto const page = (*slep)[sfOwnerNode]; + auto const page = escrowObject->getOwnerNode(); if (!ctx_.view().dirRemove( keylet::ownerDir(account), page, k.key, true)) { @@ -473,7 +475,7 @@ EscrowFinish::doApply() } // Remove escrow from recipient's owner directory, if present. - if (auto const optPage = (*slep)[~sfDestinationNode]) + if (auto const optPage = escrowObject->getRecipientNode()) { if (!ctx_.view().dirRemove( keylet::ownerDir(destID), *optPage, k.key, true)) @@ -484,7 +486,7 @@ EscrowFinish::doApply() } // Transfer amount to destination - destAcctRoot->setBalance(destAcctRoot->balance() + (*slep)[sfAmount]); + destAcctRoot->setBalance(destAcctRoot->balance() + escrowObject->amount()); ctx_.view().update(*destAcctRoot); // Adjust source owner count @@ -493,7 +495,7 @@ EscrowFinish::doApply() ctx_.view().update(*acctRoot); // Remove escrow from ledger - ctx_.view().erase(slep); + ctx_.view().erase(*escrowObject); return tesSUCCESS; } @@ -516,8 +518,8 @@ TER EscrowCancel::doApply() { auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); - auto const slep = ctx_.view().peekSLE(k); - if (!slep) + std::optional escrowObject = ctx_.view().peek(k); + if (!escrowObject) return tecNO_TARGET; if (ctx_.view().rules().enabled(fix1571)) @@ -525,27 +527,27 @@ EscrowCancel::doApply() auto const now = ctx_.view().info().parentCloseTime; // No cancel time specified: can't execute at all. - if (!(*slep)[~sfCancelAfter]) + if (!escrowObject->cancelTime()) return tecNO_PERMISSION; // Too soon: can't execute before the cancel time. - if (!after(now, (*slep)[sfCancelAfter])) + if (!after(now, *escrowObject->cancelTime())) return tecNO_PERMISSION; } else { // Too soon? - if (!(*slep)[~sfCancelAfter] || + if (!escrowObject->cancelTime() || ctx_.view().info().parentCloseTime.time_since_epoch().count() <= - (*slep)[sfCancelAfter]) + escrowObject->cancelTime()) return tecNO_PERMISSION; } - AccountID const account = (*slep)[sfAccount]; + AccountID const account = escrowObject->accountID(); // Remove escrow from owner directory { - auto const page = (*slep)[sfOwnerNode]; + auto const page = escrowObject->getOwnerNode(); if (!ctx_.view().dirRemove( keylet::ownerDir(account), page, k.key, true)) { @@ -555,10 +557,10 @@ EscrowCancel::doApply() } // Remove escrow from recipient's owner directory, if present. - if (auto const optPage = (*slep)[~sfDestinationNode]; optPage) + if (auto const optPage = escrowObject->getRecipientNode(); optPage) { if (!ctx_.view().dirRemove( - keylet::ownerDir((*slep)[sfDestination]), + keylet::ownerDir(escrowObject->getEscrowRecipient()), *optPage, k.key, true)) @@ -570,12 +572,12 @@ EscrowCancel::doApply() // Transfer amount back to owner, decrement owner count auto acctRoot = ctx_.view().peek(keylet::account(account)); - acctRoot->setBalance(acctRoot->balance() + (*slep)[sfAmount]); + acctRoot->setBalance(acctRoot->balance() + escrowObject->amount()); adjustOwnerCount(ctx_.view(), *acctRoot, -1, ctx_.journal); ctx_.view().update(*acctRoot); // Remove escrow from ledger - ctx_.view().erase(slep); + ctx_.view().erase(*escrowObject); return tesSUCCESS; } diff --git a/src/ripple/protocol/Escrow.h b/src/ripple/protocol/Escrow.h new file mode 100644 index 00000000000..989939dd863 --- /dev/null +++ b/src/ripple/protocol/Escrow.h @@ -0,0 +1,115 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2023 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_ESCROW_H_INCLUDED +#define RIPPLE_PROTOCOL_ESCROW_H_INCLUDED + +#include + +namespace ripple { + +template +class EscrowImpl final : public LedgerEntryWrapper +{ +private: + using Base = LedgerEntryWrapper; + using SleT = typename Base::SleT; + using Base::wrapped_; + + // This constructor is private so only the factory functions can + // construct an EscrowImpl. + EscrowImpl(std::shared_ptr&& w) : Base(std::move(w)) + { + } + + // Friend declarations of factory functions. + // + // For classes that contain factories we must declare the entire class + // as a friend unless the class declaration is visible at this point. + friend class ReadView; + friend class ApplyView; + +public: + // Conversion operator from EscrowImpl to EscrowImpl. + operator EscrowImpl() const + { + return EscrowImpl( + std::const_pointer_cast>( + wrapped_)); + } + + [[nodiscard]] std::optional + finishTime() const + { + return wrapped_->at(~sfFinishAfter); + } + + [[nodiscard]] std::optional + cancelTime() const + { + return wrapped_->at(~sfCancelAfter); + } + + [[nodiscard]] const std::optional + checkCondition() const + { + return wrapped_->at(~sfCondition); + } + + [[nodiscard]] AccountID + getEscrowRecipient() const + { + return wrapped_->at(sfDestination); + } + + [[nodiscard]] AccountID + accountID() const + { + return wrapped_->at(sfAccount); + } + + // This function returns the appropriate page from inside a ledger object. + [[nodiscard]] std::uint64_t + getOwnerNode() const + { + return wrapped_->at(sfOwnerNode); + } + + // Keshava: Should I explicitly specify the return type or use auto keyword? + // returns ripple::OptionalProxy> + // but OptionalProxy is not accesible from this file + [[nodiscard]] auto + getRecipientNode() const + { + return wrapped_->at(~sfDestinationNode); + } + + [[nodiscard]] STAmount + amount() const + { + return wrapped_->at(sfAmount); + } +}; + +using Escrow = EscrowImpl; +using EscrowRd = EscrowImpl; + +} // namespace ripple + +#endif // RIPPLE_PROTOCOL_ESCROW_H_INCLUDED diff --git a/src/ripple/protocol/Indexes.h b/src/ripple/protocol/Indexes.h index e7170eff7cc..5c28bcb2e3c 100644 --- a/src/ripple/protocol/Indexes.h +++ b/src/ripple/protocol/Indexes.h @@ -218,7 +218,7 @@ page(Keylet const& root, std::uint64_t index = 0) noexcept /** @} */ /** An escrow entry */ -Keylet +EscrowKeylet escrow(AccountID const& src, std::uint32_t seq) noexcept; /** A PaymentChannel */ diff --git a/src/ripple/protocol/Keylet.h b/src/ripple/protocol/Keylet.h index c34b484a150..2451e91a8e1 100644 --- a/src/ripple/protocol/Keylet.h +++ b/src/ripple/protocol/Keylet.h @@ -120,6 +120,21 @@ static_assert(std::is_move_assignable_v); static_assert(std::is_nothrow_destructible_v); #endif +template +class EscrowImpl; + +struct EscrowKeylet final : public KeyletBase +{ + template + using TWrapped = EscrowImpl; + + using KeyletBase::check; + + EscrowKeylet(uint256 const& key) : KeyletBase(ltESCROW, key) + { + } +}; + } // namespace ripple #endif diff --git a/src/ripple/protocol/impl/Indexes.cpp b/src/ripple/protocol/impl/Indexes.cpp index 2cc80ff41e6..921086b3ef2 100644 --- a/src/ripple/protocol/impl/Indexes.cpp +++ b/src/ripple/protocol/impl/Indexes.cpp @@ -314,10 +314,10 @@ page(uint256 const& key, std::uint64_t index) noexcept return {ltDIR_NODE, indexHash(LedgerNameSpace::DIR_NODE, key, index)}; } -Keylet +EscrowKeylet escrow(AccountID const& src, std::uint32_t seq) noexcept { - return {ltESCROW, indexHash(LedgerNameSpace::ESCROW, src, seq)}; + return {indexHash(LedgerNameSpace::ESCROW, src, seq)}; } Keylet