mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-10 21:58:03 +00:00
Compare commits
4 Commits
ripple/tok
...
mvadari/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
beae9832c7 | ||
|
|
cc7aab0398 | ||
|
|
6c3647c68c | ||
|
|
d87e49bf3b |
@@ -3,11 +3,17 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -30,6 +36,40 @@ public:
|
||||
: Base(keylet::payChannel(src, dst, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a payment channel time field represents an expired
|
||||
* time.
|
||||
*
|
||||
* @param timeField The optional expiry timestamp (seconds since the XRP
|
||||
* Ledger epoch). If empty, returns false.
|
||||
* @return @c true if @p timeField is set and the indicated time
|
||||
* is in the past relative to the view's parent close
|
||||
* time; @c false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isExpired(std::optional<std::uint32_t> timeField) const
|
||||
{
|
||||
if (!timeField)
|
||||
return false;
|
||||
auto const& view = this->readView();
|
||||
if (view.rules().enabled(fixCleanup3_2_0))
|
||||
return after(view.header().parentCloseTime, *timeField);
|
||||
return view.header().parentCloseTime.time_since_epoch().count() >= *timeField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this payment channel: returns its remaining funds to the
|
||||
* channel's source account, removes it from the source's and (if
|
||||
* present) the destination's owner directories, decrements the source's
|
||||
* owner count, and erases the channel from the ledger.
|
||||
*
|
||||
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
removeFromLedger()
|
||||
requires Base::kIsWritable;
|
||||
};
|
||||
|
||||
using PayChannelEntryR = PayChannelEntry<ReadView>;
|
||||
|
||||
@@ -1,30 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Close a payment channel and return its remaining funds to the channel owner.
|
||||
*
|
||||
* @param slep The SLE for the PayChannel object to close.
|
||||
* @param view The apply view in which ledger state modifications are made.
|
||||
* @param key The ledger key identifying the PayChannel entry.
|
||||
* @param j Journal used for fatal-level diagnostic messages.
|
||||
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
*/
|
||||
TER
|
||||
closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal j);
|
||||
|
||||
/**
|
||||
* Add two uint32_t values with saturation at UINT32_MAX.
|
||||
*
|
||||
@@ -37,17 +18,4 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
|
||||
uint32_t
|
||||
saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs);
|
||||
|
||||
/**
|
||||
* Determine whether a payment channel time field represents an expired time.
|
||||
*
|
||||
* @param view The apply view providing the parent close time and rules.
|
||||
* @param timeField The optional expiry timestamp (seconds since the XRP
|
||||
* Ledger epoch). If empty, the function returns false.
|
||||
* @return @c true if @p timeField is set and the indicated time is
|
||||
* in the past relative to the view's parent close time;
|
||||
* @c false otherwise.
|
||||
*/
|
||||
bool
|
||||
isChannelExpired(ApplyView const& view, std::optional<std::uint32_t> timeField);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
68
src/libxrpl/ledger/entries/PayChannelEntry.cpp
Normal file
68
src/libxrpl/ledger/entries/PayChannelEntry.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <xrpl/ledger/entries/PayChannelEntry.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
TER
|
||||
PayChannelEntry<ViewT>::removeFromLedger()
|
||||
requires Base::kIsWritable
|
||||
{
|
||||
auto& view = this->applyView();
|
||||
auto const j = this->journal();
|
||||
auto const& slep = this->mutableRawSle();
|
||||
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
|
||||
// Remove PayChan from owner directory
|
||||
if (!view.dirRemove(keylet::ownerDir(src), (*slep)[sfOwnerNode], this->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from src owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// Remove PayChan from recipient's owner directory, if present.
|
||||
if (auto const page = (*slep)[~sfDestinationNode])
|
||||
{
|
||||
auto const dst = (*slep)[sfDestination];
|
||||
if (!view.dirRemove(keylet::ownerDir(dst), *page, this->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from dst owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer amount back to owner, decrement owner count
|
||||
auto const sle = view.peek(keylet::account(src));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*slep)[sfAmount] >= (*slep)[sfBalance],
|
||||
"xrpl::PayChannelEntry::removeFromLedger : minimum channel amount");
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
decreaseOwnerCountForObject(view, sle, slep, 1, j);
|
||||
view.update(sle);
|
||||
|
||||
// Remove PayChan from ledger
|
||||
this->erase();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template class PayChannelEntry<ReadView>;
|
||||
template class PayChannelEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,71 +1,14 @@
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
TER
|
||||
closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal j)
|
||||
{
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
// Remove PayChan from owner directory
|
||||
{
|
||||
auto const page = (*slep)[sfOwnerNode];
|
||||
if (!view.dirRemove(keylet::ownerDir(src), page, key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from src owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Remove PayChan from recipient's owner directory, if present.
|
||||
if (auto const page = (*slep)[~sfDestinationNode])
|
||||
{
|
||||
auto const dst = (*slep)[sfDestination];
|
||||
if (!view.dirRemove(keylet::ownerDir(dst), *page, key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from dst owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer amount back to owner, decrement owner count
|
||||
auto const sle = view.peek(keylet::account(src));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
decreaseOwnerCountForObject(view, sle, slep, 1, j);
|
||||
view.update(sle);
|
||||
|
||||
// Remove PayChan from ledger
|
||||
view.erase(slep);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs)
|
||||
{
|
||||
@@ -80,14 +23,4 @@ saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs)
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
bool
|
||||
isChannelExpired(ApplyView const& view, std::optional<uint32_t> timeField)
|
||||
{
|
||||
if (!timeField)
|
||||
return false;
|
||||
if (view.rules().enabled(fixCleanup3_2_0))
|
||||
return after(view.header().parentCloseTime, *timeField);
|
||||
return view.header().parentCloseTime.time_since_epoch().count() >= *timeField;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/entries/PayChannelEntry.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -109,8 +110,8 @@ PaymentChannelClaim::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
PaymentChannelClaim::doApply()
|
||||
{
|
||||
Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
PayChannelEntryW slep(
|
||||
Keylet(ltPAYCHAN, ctx_.tx[sfChannel]), ctx_.view(), ctx_.registry.get().getJournal("View"));
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
@@ -119,10 +120,9 @@ PaymentChannelClaim::doApply()
|
||||
AccountID const txAccount = ctx_.tx[sfAccount];
|
||||
|
||||
auto const curExpiration = (*slep)[~sfExpiration];
|
||||
if (isChannelExpired(ctx_.view(), (*slep)[~sfCancelAfter]) ||
|
||||
isChannelExpired(ctx_.view(), curExpiration))
|
||||
if (slep.isExpired((*slep)[~sfCancelAfter]) || slep.isExpired(curExpiration))
|
||||
{
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View"));
|
||||
return slep.removeFromLedger();
|
||||
}
|
||||
|
||||
if (txAccount != src && txAccount != dst)
|
||||
@@ -174,7 +174,7 @@ PaymentChannelClaim::doApply()
|
||||
reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
|
||||
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
|
||||
ctx_.view().update(sled);
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
|
||||
if (ctx_.tx.isFlag(tfRenew))
|
||||
@@ -182,14 +182,14 @@ PaymentChannelClaim::doApply()
|
||||
if (src != txAccount)
|
||||
return tecNO_PERMISSION;
|
||||
(*slep)[~sfExpiration] = std::nullopt;
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
|
||||
if (ctx_.tx.isFlag(tfClose))
|
||||
{
|
||||
// Channel will close immediately if dry or the receiver closes
|
||||
if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount])
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View"));
|
||||
return slep.removeFromLedger();
|
||||
|
||||
auto const settleExpiration = saturatingAdd(
|
||||
ctx_.view().rules(),
|
||||
@@ -199,7 +199,7 @@ PaymentChannelClaim::doApply()
|
||||
if (!curExpiration || *curExpiration > settleExpiration)
|
||||
{
|
||||
(*slep)[~sfExpiration] = settleExpiration;
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/PayChannelEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -22,8 +22,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/*
|
||||
@@ -169,8 +167,8 @@ PaymentChannelCreate::doApply()
|
||||
//
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// payChan sequence. For more explanation see comments in SeqProxy.h.
|
||||
Keylet const payChanKeylet = keylet::payChannel(account, dst, ctx_.tx.getSeqProxy());
|
||||
auto const slep = std::make_shared<SLE>(payChanKeylet);
|
||||
PayChannelEntryW slep(account, dst, ctx_.tx.getSeqProxy(), ctx_.view());
|
||||
slep.newSLE();
|
||||
|
||||
// Funds held in this channel
|
||||
(*slep)[sfAmount] = ctx_.tx[sfAmount];
|
||||
@@ -188,12 +186,12 @@ PaymentChannelCreate::doApply()
|
||||
(*slep)[sfSequence] = ctx_.tx.getSeqProxy().value();
|
||||
}
|
||||
|
||||
ctx_.view().insert(slep);
|
||||
slep.insert();
|
||||
|
||||
// Add PayChan to owner directory
|
||||
{
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account));
|
||||
keylet::ownerDir(account), slep.keylet(), describeOwnerDir(account));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfOwnerNode] = *page;
|
||||
@@ -202,7 +200,7 @@ PaymentChannelCreate::doApply()
|
||||
// Add PayChan to the recipient's owner directory
|
||||
{
|
||||
auto const page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst));
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dst), slep.keylet(), describeOwnerDir(dst));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfDestinationNode] = *page;
|
||||
@@ -211,7 +209,7 @@ PaymentChannelCreate::doApply()
|
||||
// Deduct owner's balance, increment owner count
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep.mutableRawSle());
|
||||
ctx_.view().update(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/PayChannelEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -43,8 +44,8 @@ PaymentChannelFund::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
PaymentChannelFund::doApply()
|
||||
{
|
||||
Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
PayChannelEntryW slep(
|
||||
Keylet(ltPAYCHAN, ctx_.tx[sfChannel]), ctx_.view(), ctx_.registry.get().getJournal("View"));
|
||||
if (!slep)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -52,10 +53,9 @@ PaymentChannelFund::doApply()
|
||||
auto const txAccount = ctx_.tx[sfAccount];
|
||||
auto const curExpiration = (*slep)[~sfExpiration];
|
||||
|
||||
if (isChannelExpired(ctx_.view(), (*slep)[~sfCancelAfter]) ||
|
||||
isChannelExpired(ctx_.view(), curExpiration))
|
||||
if (slep.isExpired((*slep)[~sfCancelAfter]) || slep.isExpired(curExpiration))
|
||||
{
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View"));
|
||||
return slep.removeFromLedger();
|
||||
}
|
||||
|
||||
if (src != txAccount)
|
||||
@@ -79,7 +79,7 @@ PaymentChannelFund::doApply()
|
||||
: TER{temBAD_EXPIRATION};
|
||||
}
|
||||
(*slep)[~sfExpiration] = *newExpiration;
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(txAccount));
|
||||
@@ -110,7 +110,7 @@ PaymentChannelFund::doApply()
|
||||
}
|
||||
|
||||
(*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount];
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
ctx_.view().update(sle);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -76,12 +77,22 @@ template class SLEBase<ApplyView>;
|
||||
#pragma push_macro("LEDGER_ENTRY")
|
||||
#undef LEDGER_ENTRY
|
||||
|
||||
// No explicit instantiation here, neither a definition nor a declaration.
|
||||
// A definition would instantiate an entry a second time once its member
|
||||
// bodies live in its own .cpp (MSVC rejects that with C4661 under /WX), and a
|
||||
// declaration (`extern template`) stops clang from emitting the inline
|
||||
// constructors of the header-only entries at -O0, which leaves them undefined
|
||||
// at link time in the debug builds. Naming the class in a static_assert is
|
||||
// enough to instantiate its definition, which is all this exhaustiveness
|
||||
// check needs; the per-type suites exercise the members.
|
||||
#define LEDGER_ENTRY(tag, value, name, ...) \
|
||||
template class name##Entry<ReadView>; \
|
||||
template class name##Entry<ApplyView>; \
|
||||
static_assert( \
|
||||
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
|
||||
#name "Entry must be bound to " #tag);
|
||||
#name "Entry must be bound to " #tag); \
|
||||
static_assert( \
|
||||
std::is_constructible_v<name##Entry<ReadView>, Keylet const&, ReadView const&> && \
|
||||
std::is_constructible_v<name##Entry<ApplyView>, Keylet const&, ApplyView&>, \
|
||||
#name "Entry must be constructible from a Keylet and a view");
|
||||
|
||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user