mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-29 07:25:51 +00:00
move changes over
This commit is contained in:
@@ -20,8 +20,9 @@
|
|||||||
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
|
|
||||||
#include <xrpl/ledger/OpenView.h>
|
#include <xrpld/ledger/OpenView.h>
|
||||||
#include <xrpl/ledger/detail/ApplyViewBase.h>
|
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||||
|
|
||||||
#include <xrpl/protocol/STAmount.h>
|
#include <xrpl/protocol/STAmount.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
|
|
||||||
@@ -74,6 +75,18 @@ public:
|
|||||||
deliver_ = amount;
|
deliver_ = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||||
|
{
|
||||||
|
gasUsed_ = gasUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||||
|
{
|
||||||
|
wasmReturnCode_ = wasmReturnCode;
|
||||||
|
}
|
||||||
|
|
||||||
/** Get the number of modified entries
|
/** Get the number of modified entries
|
||||||
*/
|
*/
|
||||||
std::size_t
|
std::size_t
|
||||||
@@ -92,6 +105,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<STAmount> deliver_;
|
std::optional<STAmount> deliver_;
|
||||||
|
std::optional<std::uint32_t> gasUsed_;
|
||||||
|
std::optional<std::int32_t> wasmReturnCode_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -17,147 +17,98 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef RIPPLE_LEDGER_APPLYSTATETABLE_H_INCLUDED
|
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
#define RIPPLE_LEDGER_APPLYSTATETABLE_H_INCLUDED
|
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
|
|
||||||
#include <xrpl/beast/utility/Journal.h>
|
#include <xrpld/ledger/OpenView.h>
|
||||||
#include <xrpl/ledger/OpenView.h>
|
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||||
#include <xrpl/ledger/RawView.h>
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/protocol/STAmount.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
#include <xrpl/protocol/TxMeta.h>
|
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// Helper class that buffers modifications
|
/** Editable, discardable view that can build metadata for one tx.
|
||||||
class ApplyStateTable
|
|
||||||
|
Iteration of the tx map is delegated to the base.
|
||||||
|
|
||||||
|
@note Presented as ApplyView to clients.
|
||||||
|
*/
|
||||||
|
class ApplyViewImpl final : public detail::ApplyViewBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using key_type = ReadView::key_type;
|
ApplyViewImpl() = delete;
|
||||||
|
ApplyViewImpl(ApplyViewImpl const&) = delete;
|
||||||
|
ApplyViewImpl&
|
||||||
|
operator=(ApplyViewImpl&&) = delete;
|
||||||
|
ApplyViewImpl&
|
||||||
|
operator=(ApplyViewImpl const&) = delete;
|
||||||
|
|
||||||
private:
|
ApplyViewImpl(ApplyViewImpl&&) = default;
|
||||||
enum class Action {
|
ApplyViewImpl(ReadView const* base, ApplyFlags flags);
|
||||||
cache,
|
|
||||||
erase,
|
|
||||||
insert,
|
|
||||||
modify,
|
|
||||||
};
|
|
||||||
|
|
||||||
using items_t = std::map<key_type, std::pair<Action, std::shared_ptr<SLE>>>;
|
/** Apply the transaction.
|
||||||
|
|
||||||
items_t items_;
|
|
||||||
XRPAmount dropsDestroyed_{0};
|
|
||||||
|
|
||||||
public:
|
|
||||||
ApplyStateTable() = default;
|
|
||||||
ApplyStateTable(ApplyStateTable&&) = default;
|
|
||||||
|
|
||||||
ApplyStateTable(ApplyStateTable const&) = delete;
|
|
||||||
ApplyStateTable&
|
|
||||||
operator=(ApplyStateTable&&) = delete;
|
|
||||||
ApplyStateTable&
|
|
||||||
operator=(ApplyStateTable const&) = delete;
|
|
||||||
|
|
||||||
void
|
|
||||||
apply(RawView& to) const;
|
|
||||||
|
|
||||||
|
After a call to `apply`, the only valid
|
||||||
|
operation on this object is to call the
|
||||||
|
destructor.
|
||||||
|
*/
|
||||||
std::optional<TxMeta>
|
std::optional<TxMeta>
|
||||||
apply(
|
apply(
|
||||||
OpenView& to,
|
OpenView& to,
|
||||||
STTx const& tx,
|
STTx const& tx,
|
||||||
TER ter,
|
TER ter,
|
||||||
std::optional<STAmount> const& deliver,
|
std::optional<uint256> parentBatchId,
|
||||||
std::optional<uint256 const> const& parentBatchId,
|
|
||||||
bool isDryRun,
|
bool isDryRun,
|
||||||
beast::Journal j);
|
beast::Journal j);
|
||||||
|
|
||||||
bool
|
/** Set the amount of currency delivered.
|
||||||
exists(ReadView const& base, Keylet const& k) const;
|
|
||||||
|
|
||||||
std::optional<key_type>
|
This value is used when generating metadata
|
||||||
succ(
|
for payments, to set the DeliveredAmount field.
|
||||||
ReadView const& base,
|
If the amount is not specified, the field is
|
||||||
key_type const& key,
|
excluded from the resulting metadata.
|
||||||
std::optional<key_type> const& last) const;
|
*/
|
||||||
|
void
|
||||||
std::shared_ptr<SLE const>
|
deliver(STAmount const& amount)
|
||||||
read(ReadView const& base, Keylet const& k) const;
|
{
|
||||||
|
deliver_ = amount;
|
||||||
std::shared_ptr<SLE>
|
}
|
||||||
peek(ReadView const& base, Keylet const& k);
|
|
||||||
|
|
||||||
std::size_t
|
|
||||||
size() const;
|
|
||||||
|
|
||||||
|
void
|
||||||
|
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||||
|
{
|
||||||
|
gasUsed_ = gasUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||||
|
{
|
||||||
|
wasmReturnCode_ = wasmReturnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the number of modified entries
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
size();
|
||||||
|
|
||||||
|
/** Visit modified entries
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
visit(
|
visit(
|
||||||
ReadView const& base,
|
OpenView& target,
|
||||||
std::function<void(
|
std::function<void(
|
||||||
uint256 const& key,
|
uint256 const& key,
|
||||||
bool isDelete,
|
bool isDelete,
|
||||||
std::shared_ptr<SLE const> const& before,
|
std::shared_ptr<SLE const> const& before,
|
||||||
std::shared_ptr<SLE const> const& after)> const& func) const;
|
std::shared_ptr<SLE const> const& after)> const& func);
|
||||||
|
|
||||||
void
|
|
||||||
erase(ReadView const& base, std::shared_ptr<SLE> const& sle);
|
|
||||||
|
|
||||||
void
|
|
||||||
rawErase(ReadView const& base, std::shared_ptr<SLE> const& sle);
|
|
||||||
|
|
||||||
void
|
|
||||||
insert(ReadView const& base, std::shared_ptr<SLE> const& sle);
|
|
||||||
|
|
||||||
void
|
|
||||||
update(ReadView const& base, std::shared_ptr<SLE> const& sle);
|
|
||||||
|
|
||||||
void
|
|
||||||
replace(ReadView const& base, std::shared_ptr<SLE> const& sle);
|
|
||||||
|
|
||||||
void
|
|
||||||
destroyXRP(XRPAmount const& fee);
|
|
||||||
|
|
||||||
// For debugging
|
|
||||||
XRPAmount const&
|
|
||||||
dropsDestroyed() const
|
|
||||||
{
|
|
||||||
return dropsDestroyed_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Mods = hash_map<key_type, std::shared_ptr<SLE>>;
|
std::optional<STAmount> deliver_;
|
||||||
|
std::optional<std::uint32_t> gasUsed_;
|
||||||
static void
|
std::optional<std::int32_t> wasmReturnCode_;
|
||||||
threadItem(TxMeta& meta, std::shared_ptr<SLE> const& to);
|
|
||||||
|
|
||||||
std::shared_ptr<SLE>
|
|
||||||
getForMod(
|
|
||||||
ReadView const& base,
|
|
||||||
key_type const& key,
|
|
||||||
Mods& mods,
|
|
||||||
beast::Journal j);
|
|
||||||
|
|
||||||
void
|
|
||||||
threadTx(
|
|
||||||
ReadView const& base,
|
|
||||||
TxMeta& meta,
|
|
||||||
AccountID const& to,
|
|
||||||
Mods& mods,
|
|
||||||
beast::Journal j);
|
|
||||||
|
|
||||||
void
|
|
||||||
threadOwners(
|
|
||||||
ReadView const& base,
|
|
||||||
TxMeta& meta,
|
|
||||||
std::shared_ptr<SLE const> const& sle,
|
|
||||||
Mods& mods,
|
|
||||||
beast::Journal j);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
constexpr std::uint32_t MICRO_DROPS_PER_DROP{1'000'000};
|
||||||
|
|
||||||
/** Reflects the fee settings for a particular ledger.
|
/** Reflects the fee settings for a particular ledger.
|
||||||
|
|
||||||
The fees are always the same for any transactions applied
|
The fees are always the same for any transactions applied
|
||||||
@@ -34,6 +36,10 @@ struct Fees
|
|||||||
XRPAmount base{0}; // Reference tx cost (drops)
|
XRPAmount base{0}; // Reference tx cost (drops)
|
||||||
XRPAmount reserve{0}; // Reserve base (drops)
|
XRPAmount reserve{0}; // Reserve base (drops)
|
||||||
XRPAmount increment{0}; // Reserve increment (drops)
|
XRPAmount increment{0}; // Reserve increment (drops)
|
||||||
|
std::uint32_t extensionComputeLimit{
|
||||||
|
0}; // Extension compute limit (instructions)
|
||||||
|
std::uint32_t extensionSizeLimit{0}; // Extension size limit (bytes)
|
||||||
|
std::uint32_t gasPrice{0}; // price of WASM gas (micro-drops)
|
||||||
|
|
||||||
explicit Fees() = default;
|
explicit Fees() = default;
|
||||||
Fees(Fees const&) = default;
|
Fees(Fees const&) = default;
|
||||||
|
|||||||
@@ -231,6 +231,12 @@ page(Keylet const& root, std::uint64_t index = 0) noexcept
|
|||||||
Keylet
|
Keylet
|
||||||
escrow(AccountID const& src, std::uint32_t seq) noexcept;
|
escrow(AccountID const& src, std::uint32_t seq) noexcept;
|
||||||
|
|
||||||
|
inline Keylet
|
||||||
|
escrow(uint256 const& key) noexcept
|
||||||
|
{
|
||||||
|
return {ltESCROW, key};
|
||||||
|
}
|
||||||
|
|
||||||
/** A PaymentChannel */
|
/** A PaymentChannel */
|
||||||
Keylet
|
Keylet
|
||||||
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
|
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
|
||||||
@@ -287,11 +293,9 @@ delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept;
|
|||||||
Keylet
|
Keylet
|
||||||
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
|
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
|
||||||
|
|
||||||
// `seq` is stored as `sfXChainClaimID` in the object
|
|
||||||
Keylet
|
Keylet
|
||||||
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||||
|
|
||||||
// `seq` is stored as `sfXChainAccountCreateCount` in the object
|
|
||||||
Keylet
|
Keylet
|
||||||
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <xrpl/basics/ByteUtilities.h>
|
#include <xrpl/basics/ByteUtilities.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
|
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,8 @@ enum TEFcodes : TERUnderlyingType {
|
|||||||
tefNO_TICKET,
|
tefNO_TICKET,
|
||||||
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
||||||
tefINVALID_LEDGER_FIX_TYPE,
|
tefINVALID_LEDGER_FIX_TYPE,
|
||||||
|
tefNO_WASM,
|
||||||
|
tefWASM_FIELD_NOT_INCLUDED,
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -364,6 +366,7 @@ enum TECcodes : TERUnderlyingType {
|
|||||||
tecPSEUDO_ACCOUNT = 196,
|
tecPSEUDO_ACCOUNT = 196,
|
||||||
tecPRECISION_LOSS = 197,
|
tecPRECISION_LOSS = 197,
|
||||||
tecNO_DELEGATE_PERMISSION = 198,
|
tecNO_DELEGATE_PERMISSION = 198,
|
||||||
|
tecWASM_REJECTED = 199,
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ private:
|
|||||||
CtorHelper);
|
CtorHelper);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TxMeta(
|
TxMeta(uint256 const& transactionID, std::uint32_t ledger);
|
||||||
uint256 const& transactionID,
|
|
||||||
std::uint32_t ledger,
|
|
||||||
std::optional<uint256> parentBatchId = std::nullopt);
|
|
||||||
TxMeta(uint256 const& txID, std::uint32_t ledger, Blob const&);
|
TxMeta(uint256 const& txID, std::uint32_t ledger, Blob const&);
|
||||||
TxMeta(uint256 const& txID, std::uint32_t ledger, std::string const&);
|
TxMeta(uint256 const& txID, std::uint32_t ledger, std::string const&);
|
||||||
TxMeta(uint256 const& txID, std::uint32_t ledger, STObject const&);
|
TxMeta(uint256 const& txID, std::uint32_t ledger, STObject const&);
|
||||||
@@ -136,7 +133,7 @@ public:
|
|||||||
void
|
void
|
||||||
setParentBatchId(uint256 const& parentBatchId)
|
setParentBatchId(uint256 const& parentBatchId)
|
||||||
{
|
{
|
||||||
mParentBatchId = parentBatchId;
|
parentBatchId_ = parentBatchId;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256
|
uint256
|
||||||
@@ -145,13 +142,55 @@ public:
|
|||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
hasParentBatchId(),
|
hasParentBatchId(),
|
||||||
"ripple::TxMeta::getParentBatchId : non-null batch id");
|
"ripple::TxMeta::getParentBatchId : non-null batch id");
|
||||||
return *mParentBatchId;
|
return *parentBatchId_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
hasParentBatchId() const
|
hasParentBatchId() const
|
||||||
{
|
{
|
||||||
return static_cast<bool>(mParentBatchId);
|
return static_cast<bool>(parentBatchId_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setGasUsed(std::uint32_t const& gasUsed)
|
||||||
|
{
|
||||||
|
gasUsed_ = gasUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t
|
||||||
|
getGasUsed() const
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(
|
||||||
|
hasGasUsed(),
|
||||||
|
"ripple::TxMeta::getGasUsed : non-null gas used field");
|
||||||
|
return *gasUsed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
hasGasUsed() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(gasUsed_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setWasmReturnCode(std::int32_t const& wasmReturnCode)
|
||||||
|
{
|
||||||
|
wasmReturnCode_ = wasmReturnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int32_t
|
||||||
|
getWasmReturnCode() const
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(
|
||||||
|
hasWasmReturnCode(),
|
||||||
|
"ripple::TxMeta::getWasmReturnCode : non-null wasm return code");
|
||||||
|
return *wasmReturnCode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
hasWasmReturnCode() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(wasmReturnCode_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -161,7 +200,9 @@ private:
|
|||||||
int mResult;
|
int mResult;
|
||||||
|
|
||||||
std::optional<STAmount> mDelivered;
|
std::optional<STAmount> mDelivered;
|
||||||
std::optional<uint256> mParentBatchId;
|
std::optional<uint256> parentBatchId_;
|
||||||
|
std::optional<std::uint32_t> gasUsed_;
|
||||||
|
std::optional<std::int32_t> wasmReturnCode_;
|
||||||
|
|
||||||
STArray mNodes;
|
STArray mNodes;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,9 +32,7 @@
|
|||||||
// If you add an amendment here, then do not forget to increment `numFeatures`
|
// If you add an amendment here, then do not forget to increment `numFeatures`
|
||||||
// in include/xrpl/protocol/Feature.h.
|
// in include/xrpl/protocol/Feature.h.
|
||||||
|
|
||||||
XRPL_FIX (IncludeKeyletFields, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(SmartEscrow, Supported::no, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo)
|
|
||||||
XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo)
|
|
||||||
XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
|
||||||
@@ -47,7 +45,6 @@ XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo
|
|||||||
XRPL_FEATURE(SingleAssetVault, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(SingleAssetVault, Supported::no, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(PermissionDelegation, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(PermissionDelegation, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
// Check flags in Credential transactions
|
|
||||||
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
|
|||||||
// All fields are soeREQUIRED because there is always a SignerEntries.
|
// All fields are soeREQUIRED because there is always a SignerEntries.
|
||||||
// If there are no SignerEntries the node is deleted.
|
// If there are no SignerEntries the node is deleted.
|
||||||
LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
|
LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
|
||||||
{sfOwner, soeOPTIONAL},
|
|
||||||
{sfOwnerNode, soeREQUIRED},
|
{sfOwnerNode, soeREQUIRED},
|
||||||
{sfSignerQuorum, soeREQUIRED},
|
{sfSignerQuorum, soeREQUIRED},
|
||||||
{sfSignerEntries, soeREQUIRED},
|
{sfSignerEntries, soeREQUIRED},
|
||||||
@@ -189,7 +188,7 @@ LEDGER_ENTRY(ltDIR_NODE, 0x0064, DirectoryNode, directory, ({
|
|||||||
{sfNFTokenID, soeOPTIONAL},
|
{sfNFTokenID, soeOPTIONAL},
|
||||||
{sfPreviousTxnID, soeOPTIONAL},
|
{sfPreviousTxnID, soeOPTIONAL},
|
||||||
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
||||||
{sfDomainID, soeOPTIONAL} // order book directories
|
{sfDomainID, soeOPTIONAL}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** The ledger object which lists details about amendments on the network.
|
/** The ledger object which lists details about amendments on the network.
|
||||||
@@ -320,6 +319,7 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
|
|||||||
{sfBaseFeeDrops, soeOPTIONAL},
|
{sfBaseFeeDrops, soeOPTIONAL},
|
||||||
{sfReserveBaseDrops, soeOPTIONAL},
|
{sfReserveBaseDrops, soeOPTIONAL},
|
||||||
{sfReserveIncrementDrops, soeOPTIONAL},
|
{sfReserveIncrementDrops, soeOPTIONAL},
|
||||||
|
|
||||||
{sfPreviousTxnID, soeOPTIONAL},
|
{sfPreviousTxnID, soeOPTIONAL},
|
||||||
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
@@ -344,12 +344,13 @@ LEDGER_ENTRY(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, 0x0074, XChainOwnedCreateAc
|
|||||||
*/
|
*/
|
||||||
LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||||
{sfAccount, soeREQUIRED},
|
{sfAccount, soeREQUIRED},
|
||||||
{sfSequence, soeOPTIONAL},
|
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
{sfCondition, soeOPTIONAL},
|
{sfCondition, soeOPTIONAL},
|
||||||
{sfCancelAfter, soeOPTIONAL},
|
{sfCancelAfter, soeOPTIONAL},
|
||||||
{sfFinishAfter, soeOPTIONAL},
|
{sfFinishAfter, soeOPTIONAL},
|
||||||
|
{sfFinishFunction, soeOPTIONAL},
|
||||||
|
{sfData, soeOPTIONAL},
|
||||||
{sfSourceTag, soeOPTIONAL},
|
{sfSourceTag, soeOPTIONAL},
|
||||||
{sfDestinationTag, soeOPTIONAL},
|
{sfDestinationTag, soeOPTIONAL},
|
||||||
{sfOwnerNode, soeREQUIRED},
|
{sfOwnerNode, soeREQUIRED},
|
||||||
@@ -367,7 +368,6 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
|||||||
LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
|
LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
|
||||||
{sfAccount, soeREQUIRED},
|
{sfAccount, soeREQUIRED},
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
{sfSequence, soeOPTIONAL},
|
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
{sfBalance, soeREQUIRED},
|
{sfBalance, soeREQUIRED},
|
||||||
{sfPublicKey, soeREQUIRED},
|
{sfPublicKey, soeREQUIRED},
|
||||||
@@ -415,7 +415,6 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
|
|||||||
{sfPreviousTxnID, soeREQUIRED},
|
{sfPreviousTxnID, soeREQUIRED},
|
||||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||||
{sfDomainID, soeOPTIONAL},
|
{sfDomainID, soeOPTIONAL},
|
||||||
{sfMutableFlags, soeDEFAULT},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** A ledger object which tracks MPToken
|
/** A ledger object which tracks MPToken
|
||||||
@@ -436,7 +435,6 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
|||||||
*/
|
*/
|
||||||
LEDGER_ENTRY(ltORACLE, 0x0080, Oracle, oracle, ({
|
LEDGER_ENTRY(ltORACLE, 0x0080, Oracle, oracle, ({
|
||||||
{sfOwner, soeREQUIRED},
|
{sfOwner, soeREQUIRED},
|
||||||
{sfOracleDocumentID, soeOPTIONAL},
|
|
||||||
{sfProvider, soeREQUIRED},
|
{sfProvider, soeREQUIRED},
|
||||||
{sfPriceDataSeries, soeREQUIRED},
|
{sfPriceDataSeries, soeREQUIRED},
|
||||||
{sfAssetClass, soeREQUIRED},
|
{sfAssetClass, soeREQUIRED},
|
||||||
|
|||||||
@@ -114,7 +114,11 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48)
|
|||||||
TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50)
|
TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50)
|
||||||
TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
|
TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
|
||||||
TYPED_SFIELD(sfPermissionValue, UINT32, 52)
|
TYPED_SFIELD(sfPermissionValue, UINT32, 52)
|
||||||
TYPED_SFIELD(sfMutableFlags, UINT32, 53)
|
TYPED_SFIELD(sfExtensionComputeLimit, UINT32, 53)
|
||||||
|
TYPED_SFIELD(sfExtensionSizeLimit, UINT32, 54)
|
||||||
|
TYPED_SFIELD(sfGasPrice, UINT32, 55)
|
||||||
|
TYPED_SFIELD(sfComputationAllowance, UINT32, 56)
|
||||||
|
TYPED_SFIELD(sfGasUsed, UINT32, 57)
|
||||||
|
|
||||||
// 64-bit integers (common)
|
// 64-bit integers (common)
|
||||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||||
@@ -174,8 +178,7 @@ TYPED_SFIELD(sfNFTokenID, UINT256, 10)
|
|||||||
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
|
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
|
||||||
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
|
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
|
||||||
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
|
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
|
||||||
TYPED_SFIELD(sfAMMID, UINT256, 14,
|
TYPED_SFIELD(sfAMMID, UINT256, 14)
|
||||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
|
||||||
|
|
||||||
// 256-bit (uncommon)
|
// 256-bit (uncommon)
|
||||||
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
|
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
|
||||||
@@ -197,8 +200,7 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
|
|||||||
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
||||||
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||||
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
||||||
TYPED_SFIELD(sfVaultID, UINT256, 35,
|
TYPED_SFIELD(sfVaultID, UINT256, 35)
|
||||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
|
||||||
TYPED_SFIELD(sfParentBatchID, UINT256, 36)
|
TYPED_SFIELD(sfParentBatchID, UINT256, 36)
|
||||||
|
|
||||||
// number (common)
|
// number (common)
|
||||||
@@ -208,6 +210,9 @@ TYPED_SFIELD(sfAssetsMaximum, NUMBER, 3)
|
|||||||
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4)
|
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4)
|
||||||
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5)
|
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5)
|
||||||
|
|
||||||
|
// 32-bit signed (common)
|
||||||
|
TYPED_SFIELD(sfWasmReturnCode, INT32, 1)
|
||||||
|
|
||||||
// currency amount (common)
|
// currency amount (common)
|
||||||
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
||||||
TYPED_SFIELD(sfBalance, AMOUNT, 2)
|
TYPED_SFIELD(sfBalance, AMOUNT, 2)
|
||||||
@@ -236,7 +241,7 @@ TYPED_SFIELD(sfBaseFeeDrops, AMOUNT, 22)
|
|||||||
TYPED_SFIELD(sfReserveBaseDrops, AMOUNT, 23)
|
TYPED_SFIELD(sfReserveBaseDrops, AMOUNT, 23)
|
||||||
TYPED_SFIELD(sfReserveIncrementDrops, AMOUNT, 24)
|
TYPED_SFIELD(sfReserveIncrementDrops, AMOUNT, 24)
|
||||||
|
|
||||||
// currency amount (AMM)
|
// currency amount (more)
|
||||||
TYPED_SFIELD(sfLPTokenOut, AMOUNT, 25)
|
TYPED_SFIELD(sfLPTokenOut, AMOUNT, 25)
|
||||||
TYPED_SFIELD(sfLPTokenIn, AMOUNT, 26)
|
TYPED_SFIELD(sfLPTokenIn, AMOUNT, 26)
|
||||||
TYPED_SFIELD(sfEPrice, AMOUNT, 27)
|
TYPED_SFIELD(sfEPrice, AMOUNT, 27)
|
||||||
@@ -278,6 +283,7 @@ TYPED_SFIELD(sfAssetClass, VL, 28)
|
|||||||
TYPED_SFIELD(sfProvider, VL, 29)
|
TYPED_SFIELD(sfProvider, VL, 29)
|
||||||
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
|
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
|
||||||
TYPED_SFIELD(sfCredentialType, VL, 31)
|
TYPED_SFIELD(sfCredentialType, VL, 31)
|
||||||
|
TYPED_SFIELD(sfFinishFunction, VL, 32)
|
||||||
|
|
||||||
// account (common)
|
// account (common)
|
||||||
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
||||||
|
|||||||
@@ -22,31 +22,16 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TRANSACTION(tag, value, name, delegatable, amendments, privileges, fields)
|
* TRANSACTION(tag, value, name, delegatable, amendments, fields)
|
||||||
*
|
|
||||||
* To ease maintenance, you may replace any unneeded values with "..."
|
|
||||||
* e.g. #define TRANSACTION(tag, value, name, ...)
|
|
||||||
*
|
*
|
||||||
* You must define a transactor class in the `ripple` namespace named `name`,
|
* You must define a transactor class in the `ripple` namespace named `name`,
|
||||||
* and include its header alongside the TRANSACTOR definition using this
|
* and include its header in `src/xrpld/app/tx/detail/applySteps.cpp`.
|
||||||
* format:
|
|
||||||
* #if TRANSACTION_INCLUDE
|
|
||||||
* # include <xrpld/app/tx/detail/HEADER.h>
|
|
||||||
* #endif
|
|
||||||
*
|
|
||||||
* The `privileges` parameter of the TRANSACTION macro is a bitfield
|
|
||||||
* defining which operations the transaction can perform.
|
|
||||||
* The values are defined and used in InvariantCheck.cpp
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This transaction type executes a payment. */
|
/** This transaction type executes a payment. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Payment.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttPAYMENT, 0, Payment,
|
TRANSACTION(ttPAYMENT, 0, Payment,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
createAcct,
|
|
||||||
({
|
({
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||||
@@ -60,44 +45,38 @@ TRANSACTION(ttPAYMENT, 0, Payment,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates an escrow object. */
|
/** This transaction type creates an escrow object. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Escrow.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
|
{sfDestinationTag, soeOPTIONAL},
|
||||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||||
{sfCondition, soeOPTIONAL},
|
{sfCondition, soeOPTIONAL},
|
||||||
{sfCancelAfter, soeOPTIONAL},
|
{sfCancelAfter, soeOPTIONAL},
|
||||||
{sfFinishAfter, soeOPTIONAL},
|
{sfFinishAfter, soeOPTIONAL},
|
||||||
{sfDestinationTag, soeOPTIONAL},
|
{sfFinishFunction, soeOPTIONAL},
|
||||||
|
{sfData, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type completes an existing escrow. */
|
/** This transaction type completes an existing escrow. */
|
||||||
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfOwner, soeREQUIRED},
|
{sfOwner, soeREQUIRED},
|
||||||
{sfOfferSequence, soeREQUIRED},
|
{sfOfferSequence, soeREQUIRED},
|
||||||
{sfFulfillment, soeOPTIONAL},
|
{sfFulfillment, soeOPTIONAL},
|
||||||
{sfCondition, soeOPTIONAL},
|
{sfCondition, soeOPTIONAL},
|
||||||
{sfCredentialIDs, soeOPTIONAL},
|
{sfCredentialIDs, soeOPTIONAL},
|
||||||
|
{sfComputationAllowance, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
/** This transaction type adjusts various account settings. */
|
/** This transaction type adjusts various account settings. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/SetAccount.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfEmailHash, soeOPTIONAL},
|
{sfEmailHash, soeOPTIONAL},
|
||||||
{sfWalletLocator, soeOPTIONAL},
|
{sfWalletLocator, soeOPTIONAL},
|
||||||
@@ -112,26 +91,18 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type cancels an existing escrow. */
|
/** This transaction type cancels an existing escrow. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Escrow.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfOwner, soeREQUIRED},
|
{sfOwner, soeREQUIRED},
|
||||||
{sfOfferSequence, soeREQUIRED},
|
{sfOfferSequence, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type sets or clears an account's "regular key". */
|
/** This transaction type sets or clears an account's "regular key". */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/SetRegularKey.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfRegularKey, soeOPTIONAL},
|
{sfRegularKey, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
@@ -139,13 +110,9 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
|||||||
// 6 deprecated
|
// 6 deprecated
|
||||||
|
|
||||||
/** This transaction type creates an offer to trade one asset for another. */
|
/** This transaction type creates an offer to trade one asset for another. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/CreateOffer.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfTakerPays, soeREQUIRED},
|
{sfTakerPays, soeREQUIRED},
|
||||||
{sfTakerGets, soeREQUIRED},
|
{sfTakerGets, soeREQUIRED},
|
||||||
@@ -155,13 +122,9 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type cancels existing offers to trade one asset for another. */
|
/** This transaction type cancels existing offers to trade one asset for another. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/CancelOffer.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfOfferSequence, soeREQUIRED},
|
{sfOfferSequence, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
@@ -169,13 +132,9 @@ TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
|||||||
// 9 deprecated
|
// 9 deprecated
|
||||||
|
|
||||||
/** This transaction type creates a new set of tickets. */
|
/** This transaction type creates a new set of tickets. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/CreateTicket.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureTicketBatch,
|
featureTicketBatch,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfTicketCount, soeREQUIRED},
|
{sfTicketCount, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
@@ -185,26 +144,18 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
|||||||
/** This transaction type modifies the signer list associated with an account. */
|
/** This transaction type modifies the signer list associated with an account. */
|
||||||
// The SignerEntries are optional because a SignerList is deleted by
|
// The SignerEntries are optional because a SignerList is deleted by
|
||||||
// setting the SignerQuorum to zero and omitting SignerEntries.
|
// setting the SignerQuorum to zero and omitting SignerEntries.
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/SetSignerList.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfSignerQuorum, soeREQUIRED},
|
{sfSignerQuorum, soeREQUIRED},
|
||||||
{sfSignerEntries, soeOPTIONAL},
|
{sfSignerEntries, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates a new unidirectional XRP payment channel. */
|
/** This transaction type creates a new unidirectional XRP payment channel. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/PayChan.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
@@ -218,7 +169,6 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
|||||||
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
|
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfChannel, soeREQUIRED},
|
{sfChannel, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
@@ -229,7 +179,6 @@ TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
|
|||||||
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfChannel, soeREQUIRED},
|
{sfChannel, soeREQUIRED},
|
||||||
{sfAmount, soeOPTIONAL},
|
{sfAmount, soeOPTIONAL},
|
||||||
@@ -240,13 +189,9 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates a new check. */
|
/** This transaction type creates a new check. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/CreateCheck.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureChecks,
|
featureChecks,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
{sfSendMax, soeREQUIRED},
|
{sfSendMax, soeREQUIRED},
|
||||||
@@ -256,13 +201,9 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type cashes an existing check. */
|
/** This transaction type cashes an existing check. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/CashCheck.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureChecks,
|
featureChecks,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfCheckID, soeREQUIRED},
|
{sfCheckID, soeREQUIRED},
|
||||||
{sfAmount, soeOPTIONAL},
|
{sfAmount, soeOPTIONAL},
|
||||||
@@ -270,25 +211,17 @@ TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type cancels an existing check. */
|
/** This transaction type cancels an existing check. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/CancelCheck.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureChecks,
|
featureChecks,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfCheckID, soeREQUIRED},
|
{sfCheckID, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type grants or revokes authorization to transfer funds. */
|
/** This transaction type grants or revokes authorization to transfer funds. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/DepositPreauth.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureDepositPreauth,
|
featureDepositPreauth,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfAuthorize, soeOPTIONAL},
|
{sfAuthorize, soeOPTIONAL},
|
||||||
{sfUnauthorize, soeOPTIONAL},
|
{sfUnauthorize, soeOPTIONAL},
|
||||||
@@ -297,13 +230,9 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type modifies a trustline between two accounts. */
|
/** This transaction type modifies a trustline between two accounts. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/SetTrust.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfLimitAmount, soeOPTIONAL},
|
{sfLimitAmount, soeOPTIONAL},
|
||||||
{sfQualityIn, soeOPTIONAL},
|
{sfQualityIn, soeOPTIONAL},
|
||||||
@@ -311,13 +240,9 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type deletes an existing account. */
|
/** This transaction type deletes an existing account. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/DeleteAccount.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
mustDeleteAcct,
|
|
||||||
({
|
({
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
{sfDestinationTag, soeOPTIONAL},
|
{sfDestinationTag, soeOPTIONAL},
|
||||||
@@ -327,13 +252,9 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
|||||||
// 22 reserved
|
// 22 reserved
|
||||||
|
|
||||||
/** This transaction mints a new NFT. */
|
/** This transaction mints a new NFT. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/NFTokenMint.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureNonFungibleTokensV1,
|
featureNonFungibleTokensV1,
|
||||||
changeNFTCounts,
|
|
||||||
({
|
({
|
||||||
{sfNFTokenTaxon, soeREQUIRED},
|
{sfNFTokenTaxon, soeREQUIRED},
|
||||||
{sfTransferFee, soeOPTIONAL},
|
{sfTransferFee, soeOPTIONAL},
|
||||||
@@ -345,26 +266,18 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction burns (i.e. destroys) an existing NFT. */
|
/** This transaction burns (i.e. destroys) an existing NFT. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/NFTokenBurn.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureNonFungibleTokensV1,
|
featureNonFungibleTokensV1,
|
||||||
changeNFTCounts,
|
|
||||||
({
|
({
|
||||||
{sfNFTokenID, soeREQUIRED},
|
{sfNFTokenID, soeREQUIRED},
|
||||||
{sfOwner, soeOPTIONAL},
|
{sfOwner, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction creates a new offer to buy or sell an NFT. */
|
/** This transaction creates a new offer to buy or sell an NFT. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/NFTokenCreateOffer.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureNonFungibleTokensV1,
|
featureNonFungibleTokensV1,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfNFTokenID, soeREQUIRED},
|
{sfNFTokenID, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
@@ -374,25 +287,17 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction cancels an existing offer to buy or sell an existing NFT. */
|
/** This transaction cancels an existing offer to buy or sell an existing NFT. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/NFTokenCancelOffer.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureNonFungibleTokensV1,
|
featureNonFungibleTokensV1,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfNFTokenOffers, soeREQUIRED},
|
{sfNFTokenOffers, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction accepts an existing offer to buy or sell an existing NFT. */
|
/** This transaction accepts an existing offer to buy or sell an existing NFT. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/NFTokenAcceptOffer.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureNonFungibleTokensV1,
|
featureNonFungibleTokensV1,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfNFTokenBuyOffer, soeOPTIONAL},
|
{sfNFTokenBuyOffer, soeOPTIONAL},
|
||||||
{sfNFTokenSellOffer, soeOPTIONAL},
|
{sfNFTokenSellOffer, soeOPTIONAL},
|
||||||
@@ -400,26 +305,18 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction claws back issued tokens. */
|
/** This transaction claws back issued tokens. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Clawback.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttCLAWBACK, 30, Clawback,
|
TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureClawback,
|
featureClawback,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||||
{sfHolder, soeOPTIONAL},
|
{sfHolder, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction claws back tokens from an AMM pool. */
|
/** This transaction claws back tokens from an AMM pool. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMClawback.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMMClawback,
|
featureAMMClawback,
|
||||||
mayDeleteAcct | overrideFreeze,
|
|
||||||
({
|
({
|
||||||
{sfHolder, soeREQUIRED},
|
{sfHolder, soeREQUIRED},
|
||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
@@ -428,13 +325,9 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates an AMM instance */
|
/** This transaction type creates an AMM instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMCreate.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMM,
|
featureAMM,
|
||||||
createPseudoAcct,
|
|
||||||
({
|
({
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
{sfAmount2, soeREQUIRED},
|
{sfAmount2, soeREQUIRED},
|
||||||
@@ -442,13 +335,9 @@ TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type deposits into an AMM instance */
|
/** This transaction type deposits into an AMM instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMDeposit.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMM,
|
featureAMM,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
{sfAsset2, soeREQUIRED},
|
{sfAsset2, soeREQUIRED},
|
||||||
@@ -460,13 +349,9 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type withdraws from an AMM instance */
|
/** This transaction type withdraws from an AMM instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMWithdraw.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMM,
|
featureAMM,
|
||||||
mayDeleteAcct,
|
|
||||||
({
|
({
|
||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
{sfAsset2, soeREQUIRED},
|
{sfAsset2, soeREQUIRED},
|
||||||
@@ -477,13 +362,9 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type votes for the trading fee */
|
/** This transaction type votes for the trading fee */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMVote.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMM,
|
featureAMM,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
{sfAsset2, soeREQUIRED},
|
{sfAsset2, soeREQUIRED},
|
||||||
@@ -491,13 +372,9 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type bids for the auction slot */
|
/** This transaction type bids for the auction slot */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMBid.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_BID, 39, AMMBid,
|
TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMM,
|
featureAMM,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
{sfAsset2, soeREQUIRED},
|
{sfAsset2, soeREQUIRED},
|
||||||
@@ -507,26 +384,18 @@ TRANSACTION(ttAMM_BID, 39, AMMBid,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type deletes AMM in the empty state */
|
/** This transaction type deletes AMM in the empty state */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/AMMDelete.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureAMM,
|
featureAMM,
|
||||||
mustDeleteAcct,
|
|
||||||
({
|
({
|
||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
{sfAsset2, soeREQUIRED},
|
{sfAsset2, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transactions creates a crosschain sequence number */
|
/** This transactions creates a crosschain sequence number */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/XChainBridge.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
|
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
{sfSignatureReward, soeREQUIRED},
|
{sfSignatureReward, soeREQUIRED},
|
||||||
@@ -537,7 +406,6 @@ TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
|
|||||||
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit,
|
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
{sfXChainClaimID, soeREQUIRED},
|
{sfXChainClaimID, soeREQUIRED},
|
||||||
@@ -549,7 +417,6 @@ TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit,
|
|||||||
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim,
|
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
{sfXChainClaimID, soeREQUIRED},
|
{sfXChainClaimID, soeREQUIRED},
|
||||||
@@ -562,7 +429,6 @@ TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim,
|
|||||||
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit,
|
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
{sfDestination, soeREQUIRED},
|
{sfDestination, soeREQUIRED},
|
||||||
@@ -574,7 +440,6 @@ TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit,
|
|||||||
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation,
|
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
createAcct,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
|
|
||||||
@@ -591,11 +456,9 @@ TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction adds an attestation to an account */
|
/** This transaction adds an attestation to an account */
|
||||||
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46,
|
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation,
|
||||||
XChainAddAccountCreateAttestation,
|
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
createAcct,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
|
|
||||||
@@ -616,7 +479,6 @@ TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46,
|
|||||||
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge,
|
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
{sfSignatureReward, soeOPTIONAL},
|
{sfSignatureReward, soeOPTIONAL},
|
||||||
@@ -627,7 +489,6 @@ TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge,
|
|||||||
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureXChainBridge,
|
featureXChainBridge,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfXChainBridge, soeREQUIRED},
|
{sfXChainBridge, soeREQUIRED},
|
||||||
{sfSignatureReward, soeREQUIRED},
|
{sfSignatureReward, soeREQUIRED},
|
||||||
@@ -635,13 +496,9 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates or updates a DID */
|
/** This transaction type creates or updates a DID */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/DID.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttDID_SET, 49, DIDSet,
|
TRANSACTION(ttDID_SET, 49, DIDSet,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureDID,
|
featureDID,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfDIDDocument, soeOPTIONAL},
|
{sfDIDDocument, soeOPTIONAL},
|
||||||
{sfURI, soeOPTIONAL},
|
{sfURI, soeOPTIONAL},
|
||||||
@@ -652,17 +509,12 @@ TRANSACTION(ttDID_SET, 49, DIDSet,
|
|||||||
TRANSACTION(ttDID_DELETE, 50, DIDDelete,
|
TRANSACTION(ttDID_DELETE, 50, DIDDelete,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureDID,
|
featureDID,
|
||||||
noPriv,
|
|
||||||
({}))
|
({}))
|
||||||
|
|
||||||
/** This transaction type creates an Oracle instance */
|
/** This transaction type creates an Oracle instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/SetOracle.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featurePriceOracle,
|
featurePriceOracle,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfOracleDocumentID, soeREQUIRED},
|
{sfOracleDocumentID, soeREQUIRED},
|
||||||
{sfProvider, soeOPTIONAL},
|
{sfProvider, soeOPTIONAL},
|
||||||
@@ -673,97 +525,65 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type deletes an Oracle instance */
|
/** This transaction type deletes an Oracle instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/DeleteOracle.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featurePriceOracle,
|
featurePriceOracle,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfOracleDocumentID, soeREQUIRED},
|
{sfOracleDocumentID, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type fixes a problem in the ledger state */
|
/** This transaction type fixes a problem in the ledger state */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/LedgerStateFix.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
fixNFTokenPageLinks,
|
fixNFTokenPageLinks,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfLedgerFixType, soeREQUIRED},
|
{sfLedgerFixType, soeREQUIRED},
|
||||||
{sfOwner, soeOPTIONAL},
|
{sfOwner, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates a MPTokensIssuance instance */
|
/** This transaction type creates a MPTokensIssuance instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureMPTokensV1,
|
featureMPTokensV1,
|
||||||
createMPTIssuance,
|
|
||||||
({
|
({
|
||||||
{sfAssetScale, soeOPTIONAL},
|
{sfAssetScale, soeOPTIONAL},
|
||||||
{sfTransferFee, soeOPTIONAL},
|
{sfTransferFee, soeOPTIONAL},
|
||||||
{sfMaximumAmount, soeOPTIONAL},
|
{sfMaximumAmount, soeOPTIONAL},
|
||||||
{sfMPTokenMetadata, soeOPTIONAL},
|
{sfMPTokenMetadata, soeOPTIONAL},
|
||||||
{sfDomainID, soeOPTIONAL},
|
{sfDomainID, soeOPTIONAL},
|
||||||
{sfMutableFlags, soeOPTIONAL},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type destroys a MPTokensIssuance instance */
|
/** This transaction type destroys a MPTokensIssuance instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/MPTokenIssuanceDestroy.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureMPTokensV1,
|
featureMPTokensV1,
|
||||||
destroyMPTIssuance,
|
|
||||||
({
|
({
|
||||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */
|
/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureMPTokensV1,
|
featureMPTokensV1,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||||
{sfHolder, soeOPTIONAL},
|
{sfHolder, soeOPTIONAL},
|
||||||
{sfDomainID, soeOPTIONAL},
|
{sfDomainID, soeOPTIONAL},
|
||||||
{sfMPTokenMetadata, soeOPTIONAL},
|
|
||||||
{sfTransferFee, soeOPTIONAL},
|
|
||||||
{sfMutableFlags, soeOPTIONAL},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type authorizes a MPToken instance */
|
/** This transaction type authorizes a MPToken instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureMPTokensV1,
|
featureMPTokensV1,
|
||||||
mustAuthorizeMPT,
|
|
||||||
({
|
({
|
||||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||||
{sfHolder, soeOPTIONAL},
|
{sfHolder, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type create an Credential instance */
|
/** This transaction type create an Credential instance */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Credentials.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureCredentials,
|
featureCredentials,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfSubject, soeREQUIRED},
|
{sfSubject, soeREQUIRED},
|
||||||
{sfCredentialType, soeREQUIRED},
|
{sfCredentialType, soeREQUIRED},
|
||||||
@@ -775,7 +595,6 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
|||||||
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
|
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureCredentials,
|
featureCredentials,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfIssuer, soeREQUIRED},
|
{sfIssuer, soeREQUIRED},
|
||||||
{sfCredentialType, soeREQUIRED},
|
{sfCredentialType, soeREQUIRED},
|
||||||
@@ -785,7 +604,6 @@ TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
|
|||||||
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureCredentials,
|
featureCredentials,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfSubject, soeOPTIONAL},
|
{sfSubject, soeOPTIONAL},
|
||||||
{sfIssuer, soeOPTIONAL},
|
{sfIssuer, soeOPTIONAL},
|
||||||
@@ -793,13 +611,9 @@ TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type modify a NFToken */
|
/** This transaction type modify a NFToken */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/NFTokenModify.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureDynamicNFT,
|
featureDynamicNFT,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfNFTokenID, soeREQUIRED},
|
{sfNFTokenID, soeREQUIRED},
|
||||||
{sfOwner, soeOPTIONAL},
|
{sfOwner, soeOPTIONAL},
|
||||||
@@ -807,51 +621,35 @@ TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type creates or modifies a Permissioned Domain */
|
/** This transaction type creates or modifies a Permissioned Domain */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/PermissionedDomainSet.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featurePermissionedDomains,
|
featurePermissionedDomains,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfDomainID, soeOPTIONAL},
|
{sfDomainID, soeOPTIONAL},
|
||||||
{sfAcceptedCredentials, soeREQUIRED},
|
{sfAcceptedCredentials, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type deletes a Permissioned Domain */
|
/** This transaction type deletes a Permissioned Domain */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/PermissionedDomainDelete.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featurePermissionedDomains,
|
featurePermissionedDomains,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfDomainID, soeREQUIRED},
|
{sfDomainID, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type delegates authorized account specified permissions */
|
/** This transaction type delegates authorized account specified permissions */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/DelegateSet.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
featurePermissionDelegation,
|
featurePermissionDelegation,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfAuthorize, soeREQUIRED},
|
{sfAuthorize, soeREQUIRED},
|
||||||
{sfPermissions, soeREQUIRED},
|
{sfPermissions, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction creates a single asset vault. */
|
/** This transaction creates a single asset vault. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/VaultCreate.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureSingleAssetVault,
|
featureSingleAssetVault,
|
||||||
createPseudoAcct | createMPTIssuance,
|
|
||||||
({
|
({
|
||||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||||
{sfAssetsMaximum, soeOPTIONAL},
|
{sfAssetsMaximum, soeOPTIONAL},
|
||||||
@@ -863,13 +661,9 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction updates a single asset vault. */
|
/** This transaction updates a single asset vault. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/VaultSet.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureSingleAssetVault,
|
featureSingleAssetVault,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfVaultID, soeREQUIRED},
|
{sfVaultID, soeREQUIRED},
|
||||||
{sfAssetsMaximum, soeOPTIONAL},
|
{sfAssetsMaximum, soeOPTIONAL},
|
||||||
@@ -878,38 +672,26 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction deletes a single asset vault. */
|
/** This transaction deletes a single asset vault. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/VaultDelete.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureSingleAssetVault,
|
featureSingleAssetVault,
|
||||||
mustDeleteAcct | destroyMPTIssuance,
|
|
||||||
({
|
({
|
||||||
{sfVaultID, soeREQUIRED},
|
{sfVaultID, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction trades assets for shares with a vault. */
|
/** This transaction trades assets for shares with a vault. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/VaultDeposit.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureSingleAssetVault,
|
featureSingleAssetVault,
|
||||||
mayAuthorizeMPT,
|
|
||||||
({
|
({
|
||||||
{sfVaultID, soeREQUIRED},
|
{sfVaultID, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction trades shares for assets with a vault. */
|
/** This transaction trades shares for assets with a vault. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/VaultWithdraw.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureSingleAssetVault,
|
featureSingleAssetVault,
|
||||||
mayDeleteMPT,
|
|
||||||
({
|
({
|
||||||
{sfVaultID, soeREQUIRED},
|
{sfVaultID, soeREQUIRED},
|
||||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||||
@@ -918,13 +700,9 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction claws back tokens from a vault. */
|
/** This transaction claws back tokens from a vault. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/VaultClawback.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||||
Delegation::delegatable,
|
Delegation::delegatable,
|
||||||
featureSingleAssetVault,
|
featureSingleAssetVault,
|
||||||
mayDeleteMPT,
|
|
||||||
({
|
({
|
||||||
{sfVaultID, soeREQUIRED},
|
{sfVaultID, soeREQUIRED},
|
||||||
{sfHolder, soeREQUIRED},
|
{sfHolder, soeREQUIRED},
|
||||||
@@ -932,13 +710,9 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/** This transaction type batches together transactions. */
|
/** This transaction type batches together transactions. */
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Batch.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttBATCH, 71, Batch,
|
TRANSACTION(ttBATCH, 71, Batch,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
featureBatch,
|
featureBatch,
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfRawTransactions, soeREQUIRED},
|
{sfRawTransactions, soeREQUIRED},
|
||||||
{sfBatchSigners, soeOPTIONAL},
|
{sfBatchSigners, soeOPTIONAL},
|
||||||
@@ -948,13 +722,9 @@ TRANSACTION(ttBATCH, 71, Batch,
|
|||||||
|
|
||||||
For details, see: https://xrpl.org/amendments.html
|
For details, see: https://xrpl.org/amendments.html
|
||||||
*/
|
*/
|
||||||
#if TRANSACTION_INCLUDE
|
|
||||||
# include <xrpld/app/tx/detail/Change.h>
|
|
||||||
#endif
|
|
||||||
TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
|
TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfLedgerSequence, soeREQUIRED},
|
{sfLedgerSequence, soeREQUIRED},
|
||||||
{sfAmendment, soeREQUIRED},
|
{sfAmendment, soeREQUIRED},
|
||||||
@@ -966,7 +736,6 @@ TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
|
|||||||
TRANSACTION(ttFEE, 101, SetFee,
|
TRANSACTION(ttFEE, 101, SetFee,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfLedgerSequence, soeOPTIONAL},
|
{sfLedgerSequence, soeOPTIONAL},
|
||||||
// Old version uses raw numbers
|
// Old version uses raw numbers
|
||||||
@@ -987,7 +756,6 @@ TRANSACTION(ttFEE, 101, SetFee,
|
|||||||
TRANSACTION(ttUNL_MODIFY, 102, UNLModify,
|
TRANSACTION(ttUNL_MODIFY, 102, UNLModify,
|
||||||
Delegation::notDelegatable,
|
Delegation::notDelegatable,
|
||||||
uint256{},
|
uint256{},
|
||||||
noPriv,
|
|
||||||
({
|
({
|
||||||
{sfUNLModifyDisabling, soeREQUIRED},
|
{sfUNLModifyDisabling, soeREQUIRED},
|
||||||
{sfLedgerSequence, soeREQUIRED},
|
{sfLedgerSequence, soeREQUIRED},
|
||||||
|
|||||||
@@ -17,681 +17,98 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <xrpl/basics/Log.h>
|
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
#include <xrpl/json/to_string.h>
|
|
||||||
#include <xrpl/ledger/detail/ApplyStateTable.h>
|
#include <xrpld/ledger/OpenView.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||||
#include <xrpl/protocol/st.h>
|
|
||||||
|
#include <xrpl/protocol/STAmount.h>
|
||||||
|
#include <xrpl/protocol/TER.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
void
|
/** Editable, discardable view that can build metadata for one tx.
|
||||||
ApplyStateTable::apply(RawView& to) const
|
|
||||||
|
Iteration of the tx map is delegated to the base.
|
||||||
|
|
||||||
|
@note Presented as ApplyView to clients.
|
||||||
|
*/
|
||||||
|
class ApplyViewImpl final : public detail::ApplyViewBase
|
||||||
{
|
{
|
||||||
to.rawDestroyXRP(dropsDestroyed_);
|
public:
|
||||||
for (auto const& item : items_)
|
ApplyViewImpl() = delete;
|
||||||
|
ApplyViewImpl(ApplyViewImpl const&) = delete;
|
||||||
|
ApplyViewImpl&
|
||||||
|
operator=(ApplyViewImpl&&) = delete;
|
||||||
|
ApplyViewImpl&
|
||||||
|
operator=(ApplyViewImpl const&) = delete;
|
||||||
|
|
||||||
|
ApplyViewImpl(ApplyViewImpl&&) = default;
|
||||||
|
ApplyViewImpl(ReadView const* base, ApplyFlags flags);
|
||||||
|
|
||||||
|
/** Apply the transaction.
|
||||||
|
|
||||||
|
After a call to `apply`, the only valid
|
||||||
|
operation on this object is to call the
|
||||||
|
destructor.
|
||||||
|
*/
|
||||||
|
std::optional<TxMeta>
|
||||||
|
apply(
|
||||||
|
OpenView& to,
|
||||||
|
STTx const& tx,
|
||||||
|
TER ter,
|
||||||
|
std::optional<uint256> parentBatchId,
|
||||||
|
bool isDryRun,
|
||||||
|
beast::Journal j);
|
||||||
|
|
||||||
|
/** Set the amount of currency delivered.
|
||||||
|
|
||||||
|
This value is used when generating metadata
|
||||||
|
for payments, to set the DeliveredAmount field.
|
||||||
|
If the amount is not specified, the field is
|
||||||
|
excluded from the resulting metadata.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
deliver(STAmount const& amount)
|
||||||
{
|
{
|
||||||
auto const& sle = item.second.second;
|
deliver_ = amount;
|
||||||
switch (item.second.first)
|
|
||||||
{
|
|
||||||
case Action::cache:
|
|
||||||
break;
|
|
||||||
case Action::erase:
|
|
||||||
to.rawErase(sle);
|
|
||||||
break;
|
|
||||||
case Action::insert:
|
|
||||||
to.rawInsert(sle);
|
|
||||||
break;
|
|
||||||
case Action::modify:
|
|
||||||
to.rawReplace(sle);
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t
|
|
||||||
ApplyStateTable::size() const
|
|
||||||
{
|
|
||||||
std::size_t ret = 0;
|
|
||||||
for (auto& item : items_)
|
|
||||||
{
|
|
||||||
switch (item.second.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
case Action::insert:
|
|
||||||
case Action::modify:
|
|
||||||
++ret;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::visit(
|
|
||||||
ReadView const& to,
|
|
||||||
std::function<void(
|
|
||||||
uint256 const& key,
|
|
||||||
bool isDelete,
|
|
||||||
std::shared_ptr<SLE const> const& before,
|
|
||||||
std::shared_ptr<SLE const> const& after)> const& func) const
|
|
||||||
{
|
|
||||||
for (auto& item : items_)
|
|
||||||
{
|
|
||||||
switch (item.second.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
func(
|
|
||||||
item.first,
|
|
||||||
true,
|
|
||||||
to.read(keylet::unchecked(item.first)),
|
|
||||||
item.second.second);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Action::insert:
|
|
||||||
func(item.first, false, nullptr, item.second.second);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Action::modify:
|
|
||||||
func(
|
|
||||||
item.first,
|
|
||||||
false,
|
|
||||||
to.read(keylet::unchecked(item.first)),
|
|
||||||
item.second.second);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TxMeta>
|
|
||||||
ApplyStateTable::apply(
|
|
||||||
OpenView& to,
|
|
||||||
STTx const& tx,
|
|
||||||
TER ter,
|
|
||||||
std::optional<STAmount> const& deliver,
|
|
||||||
std::optional<uint256 const> const& parentBatchId,
|
|
||||||
bool isDryRun,
|
|
||||||
beast::Journal j)
|
|
||||||
{
|
|
||||||
// Build metadata and insert
|
|
||||||
auto const sTx = std::make_shared<Serializer>();
|
|
||||||
tx.add(*sTx);
|
|
||||||
std::shared_ptr<Serializer> sMeta;
|
|
||||||
std::optional<TxMeta> metadata;
|
|
||||||
if (!to.open() || isDryRun)
|
|
||||||
{
|
|
||||||
TxMeta meta(tx.getTransactionID(), to.seq(), parentBatchId);
|
|
||||||
|
|
||||||
if (deliver)
|
|
||||||
meta.setDeliveredAmount(*deliver);
|
|
||||||
|
|
||||||
Mods newMod;
|
|
||||||
for (auto& item : items_)
|
|
||||||
{
|
|
||||||
SField const* type;
|
|
||||||
switch (item.second.first)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case Action::cache:
|
|
||||||
continue;
|
|
||||||
case Action::erase:
|
|
||||||
type = &sfDeletedNode;
|
|
||||||
break;
|
|
||||||
case Action::insert:
|
|
||||||
type = &sfCreatedNode;
|
|
||||||
break;
|
|
||||||
case Action::modify:
|
|
||||||
type = &sfModifiedNode;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto const origNode = to.read(keylet::unchecked(item.first));
|
|
||||||
auto curNode = item.second.second;
|
|
||||||
if ((type == &sfModifiedNode) && (*curNode == *origNode))
|
|
||||||
continue;
|
|
||||||
std::uint16_t nodeType = curNode
|
|
||||||
? curNode->getFieldU16(sfLedgerEntryType)
|
|
||||||
: origNode->getFieldU16(sfLedgerEntryType);
|
|
||||||
meta.setAffectedNode(item.first, *type, nodeType);
|
|
||||||
if (type == &sfDeletedNode)
|
|
||||||
{
|
|
||||||
XRPL_ASSERT(
|
|
||||||
origNode && curNode,
|
|
||||||
"ripple::detail::ApplyStateTable::apply : valid nodes for "
|
|
||||||
"deletion");
|
|
||||||
threadOwners(to, meta, origNode, newMod, j);
|
|
||||||
|
|
||||||
STObject prevs(sfPreviousFields);
|
|
||||||
for (auto const& obj : *origNode)
|
|
||||||
{
|
|
||||||
// go through the original node for
|
|
||||||
// modified fields saved on modification
|
|
||||||
if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) &&
|
|
||||||
!curNode->hasMatchingEntry(obj))
|
|
||||||
prevs.emplace_back(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prevs.empty())
|
|
||||||
meta.getAffectedNode(item.first)
|
|
||||||
.emplace_back(std::move(prevs));
|
|
||||||
|
|
||||||
STObject finals(sfFinalFields);
|
|
||||||
for (auto const& obj : *curNode)
|
|
||||||
{
|
|
||||||
// go through the final node for final fields
|
|
||||||
if (obj.getFName().shouldMeta(
|
|
||||||
SField::sMD_Always | SField::sMD_DeleteFinal))
|
|
||||||
finals.emplace_back(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!finals.empty())
|
|
||||||
meta.getAffectedNode(item.first)
|
|
||||||
.emplace_back(std::move(finals));
|
|
||||||
}
|
|
||||||
else if (type == &sfModifiedNode)
|
|
||||||
{
|
|
||||||
XRPL_ASSERT(
|
|
||||||
curNode && origNode,
|
|
||||||
"ripple::detail::ApplyStateTable::apply : valid nodes for "
|
|
||||||
"modification");
|
|
||||||
|
|
||||||
if (curNode->isThreadedType(
|
|
||||||
to.rules())) // thread transaction to node
|
|
||||||
// item modified
|
|
||||||
threadItem(meta, curNode);
|
|
||||||
|
|
||||||
STObject prevs(sfPreviousFields);
|
|
||||||
for (auto const& obj : *origNode)
|
|
||||||
{
|
|
||||||
// search the original node for values saved on modify
|
|
||||||
if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) &&
|
|
||||||
!curNode->hasMatchingEntry(obj))
|
|
||||||
prevs.emplace_back(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prevs.empty())
|
|
||||||
meta.getAffectedNode(item.first)
|
|
||||||
.emplace_back(std::move(prevs));
|
|
||||||
|
|
||||||
STObject finals(sfFinalFields);
|
|
||||||
for (auto const& obj : *curNode)
|
|
||||||
{
|
|
||||||
// search the final node for values saved always
|
|
||||||
if (obj.getFName().shouldMeta(
|
|
||||||
SField::sMD_Always | SField::sMD_ChangeNew))
|
|
||||||
finals.emplace_back(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!finals.empty())
|
|
||||||
meta.getAffectedNode(item.first)
|
|
||||||
.emplace_back(std::move(finals));
|
|
||||||
}
|
|
||||||
else if (type == &sfCreatedNode) // if created, thread to owner(s)
|
|
||||||
{
|
|
||||||
XRPL_ASSERT(
|
|
||||||
curNode && !origNode,
|
|
||||||
"ripple::detail::ApplyStateTable::apply : valid nodes for "
|
|
||||||
"creation");
|
|
||||||
threadOwners(to, meta, curNode, newMod, j);
|
|
||||||
|
|
||||||
if (curNode->isThreadedType(
|
|
||||||
to.rules())) // always thread to self
|
|
||||||
threadItem(meta, curNode);
|
|
||||||
|
|
||||||
STObject news(sfNewFields);
|
|
||||||
for (auto const& obj : *curNode)
|
|
||||||
{
|
|
||||||
// save non-default values
|
|
||||||
if (!obj.isDefault() &&
|
|
||||||
obj.getFName().shouldMeta(
|
|
||||||
SField::sMD_Create | SField::sMD_Always))
|
|
||||||
news.emplace_back(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!news.empty())
|
|
||||||
meta.getAffectedNode(item.first)
|
|
||||||
.emplace_back(std::move(news));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UNREACHABLE(
|
|
||||||
"ripple::detail::ApplyStateTable::apply : unsupported "
|
|
||||||
"operation type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDryRun)
|
|
||||||
{
|
|
||||||
// add any new modified nodes to the modification set
|
|
||||||
for (auto const& mod : newMod)
|
|
||||||
to.rawReplace(mod.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
sMeta = std::make_shared<Serializer>();
|
|
||||||
meta.addRaw(*sMeta, ter, to.txCount());
|
|
||||||
|
|
||||||
// VFALCO For diagnostics do we want to show
|
|
||||||
// metadata even when the base view is open?
|
|
||||||
JLOG(j.trace()) << "metadata " << meta.getJson(JsonOptions::none);
|
|
||||||
|
|
||||||
metadata = meta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDryRun)
|
void
|
||||||
|
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||||
{
|
{
|
||||||
to.rawTxInsert(tx.getTransactionID(), sTx, sMeta);
|
gasUsed_ = gasUsed;
|
||||||
apply(to);
|
|
||||||
}
|
}
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---
|
void
|
||||||
|
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||||
bool
|
|
||||||
ApplyStateTable::exists(ReadView const& base, Keylet const& k) const
|
|
||||||
{
|
|
||||||
auto const iter = items_.find(k.key);
|
|
||||||
if (iter == items_.end())
|
|
||||||
return base.exists(k);
|
|
||||||
auto const& item = iter->second;
|
|
||||||
auto const& sle = item.second;
|
|
||||||
switch (item.first)
|
|
||||||
{
|
{
|
||||||
case Action::erase:
|
wasmReturnCode_ = wasmReturnCode;
|
||||||
return false;
|
|
||||||
case Action::cache:
|
|
||||||
case Action::insert:
|
|
||||||
case Action::modify:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (!k.check(*sle))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto
|
/** Get the number of modified entries
|
||||||
ApplyStateTable::succ(
|
*/
|
||||||
ReadView const& base,
|
std::size_t
|
||||||
key_type const& key,
|
size();
|
||||||
std::optional<key_type> const& last) const -> std::optional<key_type>
|
|
||||||
{
|
|
||||||
std::optional<key_type> next = key;
|
|
||||||
items_t::const_iterator iter;
|
|
||||||
// Find base successor that is
|
|
||||||
// not also deleted in our list
|
|
||||||
do
|
|
||||||
{
|
|
||||||
next = base.succ(*next, last);
|
|
||||||
if (!next)
|
|
||||||
break;
|
|
||||||
iter = items_.find(*next);
|
|
||||||
} while (iter != items_.end() && iter->second.first == Action::erase);
|
|
||||||
// Find non-deleted successor in our list
|
|
||||||
for (iter = items_.upper_bound(key); iter != items_.end(); ++iter)
|
|
||||||
{
|
|
||||||
if (iter->second.first != Action::erase)
|
|
||||||
{
|
|
||||||
// Found both, return the lower key
|
|
||||||
if (!next || next > iter->first)
|
|
||||||
next = iter->first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Nothing in our list, return
|
|
||||||
// what we got from the parent.
|
|
||||||
if (last && next >= last)
|
|
||||||
return std::nullopt;
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SLE const>
|
/** Visit modified entries
|
||||||
ApplyStateTable::read(ReadView const& base, Keylet const& k) const
|
*/
|
||||||
{
|
void
|
||||||
auto const iter = items_.find(k.key);
|
visit(
|
||||||
if (iter == items_.end())
|
OpenView& target,
|
||||||
return base.read(k);
|
std::function<void(
|
||||||
auto const& item = iter->second;
|
uint256 const& key,
|
||||||
auto const& sle = item.second;
|
bool isDelete,
|
||||||
switch (item.first)
|
std::shared_ptr<SLE const> const& before,
|
||||||
{
|
std::shared_ptr<SLE const> const& after)> const& func);
|
||||||
case Action::erase:
|
|
||||||
return nullptr;
|
|
||||||
case Action::cache:
|
|
||||||
case Action::insert:
|
|
||||||
case Action::modify:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
if (!k.check(*sle))
|
|
||||||
return nullptr;
|
|
||||||
return sle;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SLE>
|
private:
|
||||||
ApplyStateTable::peek(ReadView const& base, Keylet const& k)
|
std::optional<STAmount> deliver_;
|
||||||
{
|
std::optional<std::uint32_t> gasUsed_;
|
||||||
auto iter = items_.lower_bound(k.key);
|
std::optional<std::int32_t> wasmReturnCode_;
|
||||||
if (iter == items_.end() || iter->first != k.key)
|
};
|
||||||
{
|
|
||||||
auto const sle = base.read(k);
|
|
||||||
if (!sle)
|
|
||||||
return nullptr;
|
|
||||||
// Make our own copy
|
|
||||||
using namespace std;
|
|
||||||
iter = items_.emplace_hint(
|
|
||||||
iter,
|
|
||||||
piecewise_construct,
|
|
||||||
forward_as_tuple(sle->key()),
|
|
||||||
forward_as_tuple(Action::cache, make_shared<SLE>(*sle)));
|
|
||||||
return iter->second.second;
|
|
||||||
}
|
|
||||||
auto const& item = iter->second;
|
|
||||||
auto const& sle = item.second;
|
|
||||||
switch (item.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
return nullptr;
|
|
||||||
case Action::cache:
|
|
||||||
case Action::insert:
|
|
||||||
case Action::modify:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
if (!k.check(*sle))
|
|
||||||
return nullptr;
|
|
||||||
return sle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::erase(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
|
||||||
{
|
|
||||||
auto const iter = items_.find(sle->key());
|
|
||||||
if (iter == items_.end())
|
|
||||||
LogicError("ApplyStateTable::erase: missing key");
|
|
||||||
auto& item = iter->second;
|
|
||||||
if (item.second != sle)
|
|
||||||
LogicError("ApplyStateTable::erase: unknown SLE");
|
|
||||||
switch (item.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
LogicError("ApplyStateTable::erase: double erase");
|
|
||||||
break;
|
|
||||||
case Action::insert:
|
|
||||||
items_.erase(iter);
|
|
||||||
break;
|
|
||||||
case Action::cache:
|
|
||||||
case Action::modify:
|
|
||||||
item.first = Action::erase;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
auto const result = items_.emplace(
|
|
||||||
piecewise_construct,
|
|
||||||
forward_as_tuple(sle->key()),
|
|
||||||
forward_as_tuple(Action::erase, sle));
|
|
||||||
if (result.second)
|
|
||||||
return;
|
|
||||||
auto& item = result.first->second;
|
|
||||||
switch (item.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
LogicError("ApplyStateTable::rawErase: double erase");
|
|
||||||
break;
|
|
||||||
case Action::insert:
|
|
||||||
items_.erase(result.first);
|
|
||||||
break;
|
|
||||||
case Action::cache:
|
|
||||||
case Action::modify:
|
|
||||||
item.first = Action::erase;
|
|
||||||
item.second = sle;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::insert(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
|
||||||
{
|
|
||||||
auto const iter = items_.lower_bound(sle->key());
|
|
||||||
if (iter == items_.end() || iter->first != sle->key())
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
items_.emplace_hint(
|
|
||||||
iter,
|
|
||||||
piecewise_construct,
|
|
||||||
forward_as_tuple(sle->key()),
|
|
||||||
forward_as_tuple(Action::insert, sle));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto& item = iter->second;
|
|
||||||
switch (item.first)
|
|
||||||
{
|
|
||||||
case Action::cache:
|
|
||||||
LogicError("ApplyStateTable::insert: already cached");
|
|
||||||
case Action::insert:
|
|
||||||
LogicError("ApplyStateTable::insert: already inserted");
|
|
||||||
case Action::modify:
|
|
||||||
LogicError("ApplyStateTable::insert: already modified");
|
|
||||||
case Action::erase:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
item.first = Action::modify;
|
|
||||||
item.second = sle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::replace(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
|
||||||
{
|
|
||||||
auto const iter = items_.lower_bound(sle->key());
|
|
||||||
if (iter == items_.end() || iter->first != sle->key())
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
items_.emplace_hint(
|
|
||||||
iter,
|
|
||||||
piecewise_construct,
|
|
||||||
forward_as_tuple(sle->key()),
|
|
||||||
forward_as_tuple(Action::modify, sle));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto& item = iter->second;
|
|
||||||
switch (item.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
LogicError("ApplyStateTable::replace: already erased");
|
|
||||||
case Action::cache:
|
|
||||||
item.first = Action::modify;
|
|
||||||
break;
|
|
||||||
case Action::insert:
|
|
||||||
case Action::modify:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
item.second = sle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::update(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
|
||||||
{
|
|
||||||
auto const iter = items_.find(sle->key());
|
|
||||||
if (iter == items_.end())
|
|
||||||
LogicError("ApplyStateTable::update: missing key");
|
|
||||||
auto& item = iter->second;
|
|
||||||
if (item.second != sle)
|
|
||||||
LogicError("ApplyStateTable::update: unknown SLE");
|
|
||||||
switch (item.first)
|
|
||||||
{
|
|
||||||
case Action::erase:
|
|
||||||
LogicError("ApplyStateTable::update: erased");
|
|
||||||
break;
|
|
||||||
case Action::cache:
|
|
||||||
item.first = Action::modify;
|
|
||||||
break;
|
|
||||||
case Action::insert:
|
|
||||||
case Action::modify:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::destroyXRP(XRPAmount const& fee)
|
|
||||||
{
|
|
||||||
dropsDestroyed_ += fee;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Insert this transaction to the SLE's threading list
|
|
||||||
void
|
|
||||||
ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr<SLE> const& sle)
|
|
||||||
{
|
|
||||||
key_type prevTxID;
|
|
||||||
LedgerIndex prevLgrID;
|
|
||||||
|
|
||||||
if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!prevTxID.isZero())
|
|
||||||
{
|
|
||||||
auto& node = meta.getAffectedNode(sle, sfModifiedNode);
|
|
||||||
|
|
||||||
if (node.getFieldIndex(sfPreviousTxnID) == -1)
|
|
||||||
{
|
|
||||||
XRPL_ASSERT(
|
|
||||||
node.getFieldIndex(sfPreviousTxnLgrSeq) == -1,
|
|
||||||
"ripple::ApplyStateTable::threadItem : previous ledger is not "
|
|
||||||
"set");
|
|
||||||
node.setFieldH256(sfPreviousTxnID, prevTxID);
|
|
||||||
node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID);
|
|
||||||
}
|
|
||||||
|
|
||||||
XRPL_ASSERT(
|
|
||||||
node.getFieldH256(sfPreviousTxnID) == prevTxID,
|
|
||||||
"ripple::ApplyStateTable::threadItem : previous transaction is a "
|
|
||||||
"match");
|
|
||||||
XRPL_ASSERT(
|
|
||||||
node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID,
|
|
||||||
"ripple::ApplyStateTable::threadItem : previous ledger is a match");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SLE>
|
|
||||||
ApplyStateTable::getForMod(
|
|
||||||
ReadView const& base,
|
|
||||||
key_type const& key,
|
|
||||||
Mods& mods,
|
|
||||||
beast::Journal j)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
auto miter = mods.find(key);
|
|
||||||
if (miter != mods.end())
|
|
||||||
{
|
|
||||||
XRPL_ASSERT(
|
|
||||||
miter->second,
|
|
||||||
"ripple::ApplyStateTable::getForMod : non-null result");
|
|
||||||
return miter->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto iter = items_.find(key);
|
|
||||||
if (iter != items_.end())
|
|
||||||
{
|
|
||||||
auto const& item = iter->second;
|
|
||||||
if (item.first == Action::erase)
|
|
||||||
{
|
|
||||||
// The Destination of an Escrow or a PayChannel may have been
|
|
||||||
// deleted. In that case the account we're threading to will
|
|
||||||
// not be found and it is appropriate to return a nullptr.
|
|
||||||
JLOG(j.warn()) << "Trying to thread to deleted node";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (item.first != Action::cache)
|
|
||||||
return item.second;
|
|
||||||
|
|
||||||
// If it's only cached, then the node is being modified only by
|
|
||||||
// metadata; fall through and track it in the mods table.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto c = base.read(keylet::unchecked(key));
|
|
||||||
if (!c)
|
|
||||||
{
|
|
||||||
// The Destination of an Escrow or a PayChannel may have been
|
|
||||||
// deleted. In that case the account we're threading to will
|
|
||||||
// not be found and it is appropriate to return a nullptr.
|
|
||||||
JLOG(j.warn()) << "ApplyStateTable::getForMod: key not found";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto sle = std::make_shared<SLE>(*c);
|
|
||||||
mods.emplace(key, sle);
|
|
||||||
return sle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::threadTx(
|
|
||||||
ReadView const& base,
|
|
||||||
TxMeta& meta,
|
|
||||||
AccountID const& to,
|
|
||||||
Mods& mods,
|
|
||||||
beast::Journal j)
|
|
||||||
{
|
|
||||||
auto const sle = getForMod(base, keylet::account(to).key, mods, j);
|
|
||||||
if (!sle)
|
|
||||||
{
|
|
||||||
// The Destination of an Escrow or PayChannel may have been deleted.
|
|
||||||
// In that case the account we are threading to will not be found.
|
|
||||||
// So this logging is just a warning.
|
|
||||||
JLOG(j.warn()) << "Threading to non-existent account: " << toBase58(to);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// threadItem only applied to AccountRoot
|
|
||||||
XRPL_ASSERT(
|
|
||||||
sle->isThreadedType(base.rules()),
|
|
||||||
"ripple::ApplyStateTable::threadTx : SLE is threaded");
|
|
||||||
threadItem(meta, sle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ApplyStateTable::threadOwners(
|
|
||||||
ReadView const& base,
|
|
||||||
TxMeta& meta,
|
|
||||||
std::shared_ptr<SLE const> const& sle,
|
|
||||||
Mods& mods,
|
|
||||||
beast::Journal j)
|
|
||||||
{
|
|
||||||
LedgerEntryType const ledgerType{sle->getType()};
|
|
||||||
switch (ledgerType)
|
|
||||||
{
|
|
||||||
case ltACCOUNT_ROOT: {
|
|
||||||
// Nothing to do
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ltRIPPLE_STATE: {
|
|
||||||
threadTx(base, meta, (*sle)[sfLowLimit].getIssuer(), mods, j);
|
|
||||||
threadTx(base, meta, (*sle)[sfHighLimit].getIssuer(), mods, j);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// If sfAccount is present, thread to that account
|
|
||||||
if (auto const optSleAcct{(*sle)[~sfAccount]})
|
|
||||||
threadTx(base, meta, *optSleAcct, mods, j);
|
|
||||||
|
|
||||||
// Don't thread a check's sfDestination unless the amendment is
|
|
||||||
// enabled
|
|
||||||
if (ledgerType == ltCHECK &&
|
|
||||||
!base.rules().enabled(fixCheckThreading))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// If sfDestination is present, thread to that account
|
|
||||||
if (auto const optSleDest{(*sle)[~sfDestination]})
|
|
||||||
threadTx(base, meta, *optSleDest, mods, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -17,43 +17,98 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
|
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||||
|
|
||||||
|
#include <xrpld/ledger/OpenView.h>
|
||||||
|
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||||
|
|
||||||
|
#include <xrpl/protocol/STAmount.h>
|
||||||
|
#include <xrpl/protocol/TER.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
ApplyViewImpl::ApplyViewImpl(ReadView const* base, ApplyFlags flags)
|
/** Editable, discardable view that can build metadata for one tx.
|
||||||
: ApplyViewBase(base, flags)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TxMeta>
|
Iteration of the tx map is delegated to the base.
|
||||||
ApplyViewImpl::apply(
|
|
||||||
OpenView& to,
|
|
||||||
STTx const& tx,
|
|
||||||
TER ter,
|
|
||||||
std::optional<uint256> parentBatchId,
|
|
||||||
bool isDryRun,
|
|
||||||
beast::Journal j)
|
|
||||||
{
|
|
||||||
return items_.apply(to, tx, ter, deliver_, parentBatchId, isDryRun, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t
|
@note Presented as ApplyView to clients.
|
||||||
ApplyViewImpl::size()
|
*/
|
||||||
|
class ApplyViewImpl final : public detail::ApplyViewBase
|
||||||
{
|
{
|
||||||
return items_.size();
|
public:
|
||||||
}
|
ApplyViewImpl() = delete;
|
||||||
|
ApplyViewImpl(ApplyViewImpl const&) = delete;
|
||||||
|
ApplyViewImpl&
|
||||||
|
operator=(ApplyViewImpl&&) = delete;
|
||||||
|
ApplyViewImpl&
|
||||||
|
operator=(ApplyViewImpl const&) = delete;
|
||||||
|
|
||||||
void
|
ApplyViewImpl(ApplyViewImpl&&) = default;
|
||||||
ApplyViewImpl::visit(
|
ApplyViewImpl(ReadView const* base, ApplyFlags flags);
|
||||||
OpenView& to,
|
|
||||||
std::function<void(
|
/** Apply the transaction.
|
||||||
uint256 const& key,
|
|
||||||
bool isDelete,
|
After a call to `apply`, the only valid
|
||||||
std::shared_ptr<SLE const> const& before,
|
operation on this object is to call the
|
||||||
std::shared_ptr<SLE const> const& after)> const& func)
|
destructor.
|
||||||
{
|
*/
|
||||||
items_.visit(to, func);
|
std::optional<TxMeta>
|
||||||
}
|
apply(
|
||||||
|
OpenView& to,
|
||||||
|
STTx const& tx,
|
||||||
|
TER ter,
|
||||||
|
std::optional<uint256> parentBatchId,
|
||||||
|
bool isDryRun,
|
||||||
|
beast::Journal j);
|
||||||
|
|
||||||
|
/** Set the amount of currency delivered.
|
||||||
|
|
||||||
|
This value is used when generating metadata
|
||||||
|
for payments, to set the DeliveredAmount field.
|
||||||
|
If the amount is not specified, the field is
|
||||||
|
excluded from the resulting metadata.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
deliver(STAmount const& amount)
|
||||||
|
{
|
||||||
|
deliver_ = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||||
|
{
|
||||||
|
gasUsed_ = gasUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||||
|
{
|
||||||
|
wasmReturnCode_ = wasmReturnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the number of modified entries
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
size();
|
||||||
|
|
||||||
|
/** Visit modified entries
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
visit(
|
||||||
|
OpenView& target,
|
||||||
|
std::function<void(
|
||||||
|
uint256 const& key,
|
||||||
|
bool isDelete,
|
||||||
|
std::shared_ptr<SLE const> const& before,
|
||||||
|
std::shared_ptr<SLE const> const& after)> const& func);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<STAmount> deliver_;
|
||||||
|
std::optional<std::uint32_t> gasUsed_;
|
||||||
|
std::optional<std::int32_t> wasmReturnCode_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ transResults()
|
|||||||
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
|
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
|
||||||
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
|
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
|
||||||
MAKE_ERROR(tecNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."),
|
MAKE_ERROR(tecNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."),
|
||||||
|
MAKE_ERROR(tecWASM_REJECTED, "The custom WASM code that was run rejected your transaction."),
|
||||||
|
|
||||||
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
||||||
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||||
@@ -151,6 +152,8 @@ transResults()
|
|||||||
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
|
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
|
||||||
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
|
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
|
||||||
MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."),
|
MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."),
|
||||||
|
MAKE_ERROR(tefNO_WASM, "There is no WASM code to run, but a WASM-specific field was included."),
|
||||||
|
MAKE_ERROR(tefWASM_FIELD_NOT_INCLUDED, "WASM code requires a field to be included that was not included."),
|
||||||
|
|
||||||
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
|
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
|
||||||
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
|
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
|
||||||
|
|||||||
@@ -56,9 +56,12 @@ TxMeta::TxMeta(
|
|||||||
|
|
||||||
if (obj.isFieldPresent(sfDeliveredAmount))
|
if (obj.isFieldPresent(sfDeliveredAmount))
|
||||||
setDeliveredAmount(obj.getFieldAmount(sfDeliveredAmount));
|
setDeliveredAmount(obj.getFieldAmount(sfDeliveredAmount));
|
||||||
|
|
||||||
if (obj.isFieldPresent(sfParentBatchID))
|
if (obj.isFieldPresent(sfParentBatchID))
|
||||||
setParentBatchId(obj.getFieldH256(sfParentBatchID));
|
setParentBatchId(obj.getFieldH256(sfParentBatchID));
|
||||||
|
if (obj.isFieldPresent(sfGasUsed))
|
||||||
|
setGasUsed(obj.getFieldU32(sfGasUsed));
|
||||||
|
if (obj.isFieldPresent(sfWasmReturnCode))
|
||||||
|
setWasmReturnCode(obj.getFieldI32(sfWasmReturnCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
|
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
|
||||||
@@ -82,6 +85,12 @@ TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
|
|||||||
|
|
||||||
if (obj.isFieldPresent(sfParentBatchID))
|
if (obj.isFieldPresent(sfParentBatchID))
|
||||||
setParentBatchId(obj.getFieldH256(sfParentBatchID));
|
setParentBatchId(obj.getFieldH256(sfParentBatchID));
|
||||||
|
|
||||||
|
if (obj.isFieldPresent(sfGasUsed))
|
||||||
|
setGasUsed(obj.getFieldU32(sfGasUsed));
|
||||||
|
|
||||||
|
if (obj.isFieldPresent(sfWasmReturnCode))
|
||||||
|
setWasmReturnCode(obj.getFieldI32(sfWasmReturnCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, Blob const& vec)
|
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, Blob const& vec)
|
||||||
@@ -97,15 +106,11 @@ TxMeta::TxMeta(
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TxMeta::TxMeta(
|
TxMeta::TxMeta(uint256 const& transactionID, std::uint32_t ledger)
|
||||||
uint256 const& transactionID,
|
|
||||||
std::uint32_t ledger,
|
|
||||||
std::optional<uint256> parentBatchId)
|
|
||||||
: mTransactionID(transactionID)
|
: mTransactionID(transactionID)
|
||||||
, mLedger(ledger)
|
, mLedger(ledger)
|
||||||
, mIndex(static_cast<std::uint32_t>(-1))
|
, mIndex(static_cast<std::uint32_t>(-1))
|
||||||
, mResult(255)
|
, mResult(255)
|
||||||
, mParentBatchId(parentBatchId)
|
|
||||||
, mNodes(sfAffectedNodes)
|
, mNodes(sfAffectedNodes)
|
||||||
{
|
{
|
||||||
mNodes.reserve(32);
|
mNodes.reserve(32);
|
||||||
@@ -253,9 +258,12 @@ TxMeta::getAsObject() const
|
|||||||
metaData.emplace_back(mNodes);
|
metaData.emplace_back(mNodes);
|
||||||
if (hasDeliveredAmount())
|
if (hasDeliveredAmount())
|
||||||
metaData.setFieldAmount(sfDeliveredAmount, getDeliveredAmount());
|
metaData.setFieldAmount(sfDeliveredAmount, getDeliveredAmount());
|
||||||
|
|
||||||
if (hasParentBatchId())
|
if (hasParentBatchId())
|
||||||
metaData.setFieldH256(sfParentBatchID, getParentBatchId());
|
metaData.setFieldH256(sfParentBatchID, getParentBatchId());
|
||||||
|
if (hasGasUsed())
|
||||||
|
metaData.setFieldU32(sfGasUsed, getGasUsed());
|
||||||
|
if (hasWasmReturnCode())
|
||||||
|
metaData.setFieldI32(sfWasmReturnCode, getWasmReturnCode());
|
||||||
|
|
||||||
return metaData;
|
return metaData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <test/app/wasm_fixtures/fixtures.h>
|
||||||
#include <test/jtx.h>
|
#include <test/jtx.h>
|
||||||
|
|
||||||
#include <xrpld/app/tx/applySteps.h>
|
#include <xrpld/app/tx/applySteps.h>
|
||||||
|
#include <xrpld/app/wasm/WasmVM.h>
|
||||||
|
#include <xrpld/ledger/Dir.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/Dir.h>
|
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
@@ -253,14 +255,6 @@ struct Escrow_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(sle);
|
BEAST_EXPECT(sle);
|
||||||
BEAST_EXPECT((*sle)[sfSourceTag] == 1);
|
BEAST_EXPECT((*sle)[sfSourceTag] == 1);
|
||||||
BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
|
BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
|
||||||
if (features[fixIncludeKeyletFields])
|
|
||||||
{
|
|
||||||
BEAST_EXPECT((*sle)[sfSequence] == seq);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(!sle->isFieldPresent(sfSequence));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -302,7 +296,7 @@ struct Escrow_test : public beast::unit_test::suite
|
|||||||
{
|
{
|
||||||
testcase("Implied Finish Time (without fix1571)");
|
testcase("Implied Finish Time (without fix1571)");
|
||||||
|
|
||||||
Env env(*this, testable_amendments() - fix1571);
|
Env env(*this, features - fix1571);
|
||||||
auto const baseFee = env.current()->fees().base;
|
auto const baseFee = env.current()->fees().base;
|
||||||
env.fund(XRP(5000), "alice", "bob", "carol");
|
env.fund(XRP(5000), "alice", "bob", "carol");
|
||||||
env.close();
|
env.close();
|
||||||
@@ -1559,7 +1553,7 @@ struct Escrow_test : public beast::unit_test::suite
|
|||||||
Account const alice{"alice"};
|
Account const alice{"alice"};
|
||||||
Account const bob{"bob"};
|
Account const bob{"bob"};
|
||||||
Account const carol{"carol"};
|
Account const carol{"carol"};
|
||||||
Account const dillon{"dillon "};
|
Account const dillon{"dillon"};
|
||||||
Account const zelda{"zelda"};
|
Account const zelda{"zelda"};
|
||||||
|
|
||||||
char const credType[] = "abcde";
|
char const credType[] = "abcde";
|
||||||
@@ -1701,6 +1695,763 @@ struct Escrow_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testCreateFinishFunctionPreflight(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("Test preflight checks involving FinishFunction");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const carol{"carol"};
|
||||||
|
|
||||||
|
// Tests whether the ledger index is >= 5
|
||||||
|
// getLedgerSqn() >= 5}
|
||||||
|
static auto wasmHex = ledgerSqnWasmHex;
|
||||||
|
|
||||||
|
{
|
||||||
|
// featureSmartEscrow disabled
|
||||||
|
Env env(*this, features - featureSmartEscrow);
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
XRPAmount const txnFees = env.current()->fees().base + 1000;
|
||||||
|
auto escrowCreate = escrow::create(alice, carol, XRP(1000));
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temDISABLED));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FinishFunction > max length
|
||||||
|
Env env(
|
||||||
|
*this,
|
||||||
|
envconfig([](std::unique_ptr<Config> cfg) {
|
||||||
|
cfg->FEES.extension_size_limit = 10; // 10 bytes
|
||||||
|
return cfg;
|
||||||
|
}),
|
||||||
|
features);
|
||||||
|
XRPAmount const txnFees = env.current()->fees().base + 1000;
|
||||||
|
// create escrow
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
|
||||||
|
auto escrowCreate = escrow::create(alice, carol, XRP(500));
|
||||||
|
|
||||||
|
// 11-byte string
|
||||||
|
std::string longWasmHex = "00112233445566778899AA";
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(longWasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temMALFORMED));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Env env(
|
||||||
|
*this,
|
||||||
|
envconfig([](std::unique_ptr<Config> cfg) {
|
||||||
|
cfg->START_UP = Config::FRESH;
|
||||||
|
return cfg;
|
||||||
|
}),
|
||||||
|
features);
|
||||||
|
XRPAmount const txnFees =
|
||||||
|
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
|
||||||
|
// create escrow
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
|
||||||
|
auto escrowCreate = escrow::create(alice, carol, XRP(500));
|
||||||
|
|
||||||
|
// Success situations
|
||||||
|
{
|
||||||
|
// FinishFunction + CancelAfter
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 20s),
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction + Condition + CancelAfter
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 30s),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction + FinishAfter + CancelAfter
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 40s),
|
||||||
|
escrow::finish_time(env.now() + 2s),
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction + FinishAfter + Condition + CancelAfter
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 50s),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
escrow::finish_time(env.now() + 2s),
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failure situations (i.e. all other combinations)
|
||||||
|
{
|
||||||
|
// only FinishFunction
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temBAD_EXPIRATION));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction + FinishAfter
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::finish_time(env.now() + 2s),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temBAD_EXPIRATION));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction + Condition
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temBAD_EXPIRATION));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction + FinishAfter + Condition
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
escrow::finish_time(env.now() + 2s),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temBAD_EXPIRATION));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// FinishFunction 0 length
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(""),
|
||||||
|
escrow::cancel_time(env.now() + 60s),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temMALFORMED));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Not enough fees
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 70s),
|
||||||
|
fee(txnFees - 1),
|
||||||
|
ter(telINSUF_FEE_P));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FinishFunction nonexistent host function
|
||||||
|
// pub fn finish() -> bool {
|
||||||
|
// unsafe { host_lib::bad() >= 5 }
|
||||||
|
// }
|
||||||
|
auto const badWasmHex =
|
||||||
|
"0061736d010000000105016000017f02100108686f73745f6c696203626164"
|
||||||
|
"00000302010005030100100611027f00418080c0000b7f00418080c0000b07"
|
||||||
|
"2e04066d656d6f727902000666696e69736800010a5f5f646174615f656e64"
|
||||||
|
"03000b5f5f686561705f6261736503010a09010700100041044a0b004d0970"
|
||||||
|
"726f64756365727302086c616e6775616765010452757374000c70726f6365"
|
||||||
|
"737365642d6279010572757374631d312e38352e3120283465623136313235"
|
||||||
|
"3020323032352d30332d31352900490f7461726765745f6665617475726573"
|
||||||
|
"042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f72"
|
||||||
|
"65666572656e63652d74797065732b0a6d756c746976616c7565";
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(badWasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(txnFees),
|
||||||
|
ter(temBAD_WASM));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testFinishWasmFailures(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("EscrowFinish Smart Escrow failures");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const carol{"carol"};
|
||||||
|
|
||||||
|
// Tests whether the ledger index is >= 5
|
||||||
|
// getLedgerSqn() >= 5}
|
||||||
|
static auto wasmHex = ledgerSqnWasmHex;
|
||||||
|
|
||||||
|
{
|
||||||
|
// featureSmartEscrow disabled
|
||||||
|
Env env(*this, features - featureSmartEscrow);
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
XRPAmount const txnFees =
|
||||||
|
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
|
||||||
|
env(escrow::finish(carol, alice, 1),
|
||||||
|
fee(txnFees),
|
||||||
|
escrow::comp_allowance(4),
|
||||||
|
ter(temDISABLED));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// ComputationAllowance > max compute limit
|
||||||
|
Env env(
|
||||||
|
*this,
|
||||||
|
envconfig([](std::unique_ptr<Config> cfg) {
|
||||||
|
cfg->FEES.extension_compute_limit = 1'000; // in gas
|
||||||
|
return cfg;
|
||||||
|
}),
|
||||||
|
features);
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
// Run past the flag ledger so that a Fee change vote occurs and
|
||||||
|
// updates FeeSettings. (It also activates all supported
|
||||||
|
// amendments.)
|
||||||
|
for (auto i = env.current()->seq(); i <= 257; ++i)
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto const allowance = 1'001;
|
||||||
|
env(escrow::finish(carol, alice, 1),
|
||||||
|
fee(env.current()->fees().base + allowance),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
ter(temBAD_LIMIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
Env env(*this, features);
|
||||||
|
|
||||||
|
// Run past the flag ledger so that a Fee change vote occurs and
|
||||||
|
// updates FeeSettings. (It also activates all supported
|
||||||
|
// amendments.)
|
||||||
|
for (auto i = env.current()->seq(); i <= 257; ++i)
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
XRPAmount const txnFees =
|
||||||
|
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
|
||||||
|
// create escrow
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
env(escrow::create(alice, carol, XRP(500)),
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
// no ComputationAllowance field
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
ter(tefWASM_FIELD_NOT_INCLUDED));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// ComputationAllowance value of 0
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(0),
|
||||||
|
ter(temBAD_LIMIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// not enough fees
|
||||||
|
// This function takes 4 gas
|
||||||
|
// In testing, 1 gas costs 1 drop
|
||||||
|
auto const finishFee = env.current()->fees().base + 3;
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
fee(finishFee),
|
||||||
|
escrow::comp_allowance(4),
|
||||||
|
ter(telINSUF_FEE_P));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// not enough gas
|
||||||
|
// This function takes 4 gas
|
||||||
|
// In testing, 1 gas costs 1 drop
|
||||||
|
auto const finishFee = env.current()->fees().base + 4;
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
fee(finishFee),
|
||||||
|
escrow::comp_allowance(2),
|
||||||
|
ter(tecFAILED_PROCESSING));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// ComputationAllowance field included w/no FinishFunction on
|
||||||
|
// escrow
|
||||||
|
auto const seq2 = env.seq(alice);
|
||||||
|
env(escrow::create(alice, carol, XRP(500)),
|
||||||
|
escrow::finish_time(env.now() + 10s),
|
||||||
|
escrow::cancel_time(env.now() + 100s));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto const allowance = 100;
|
||||||
|
env(escrow::finish(carol, alice, seq2),
|
||||||
|
fee(env.current()->fees().base +
|
||||||
|
(allowance * env.current()->fees().gasPrice) /
|
||||||
|
MICRO_DROPS_PER_DROP +
|
||||||
|
1),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
ter(tefNO_WASM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testFinishFunction(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("Example escrow function");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const carol{"carol"};
|
||||||
|
|
||||||
|
// Tests whether the ledger index is >= 5
|
||||||
|
// getLedgerSqn() >= 5}
|
||||||
|
auto const& wasmHex = ledgerSqnWasmHex;
|
||||||
|
std::uint32_t const allowance = 66;
|
||||||
|
auto escrowCreate = escrow::create(alice, carol, XRP(1000));
|
||||||
|
auto [createFee, finishFee] = [&]() {
|
||||||
|
Env env(*this, features);
|
||||||
|
auto createFee =
|
||||||
|
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
|
||||||
|
auto finishFee = env.current()->fees().base +
|
||||||
|
(allowance * env.current()->fees().gasPrice) /
|
||||||
|
MICRO_DROPS_PER_DROP +
|
||||||
|
1;
|
||||||
|
return std::make_pair(createFee, finishFee);
|
||||||
|
}();
|
||||||
|
|
||||||
|
{
|
||||||
|
// basic FinishFunction situation
|
||||||
|
Env env(*this, features);
|
||||||
|
// create escrow
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(createFee));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(env.ownerCount(alice) == 2))
|
||||||
|
{
|
||||||
|
env.require(balance(alice, XRP(4000) - createFee));
|
||||||
|
env.require(balance(carol, XRP(5000)));
|
||||||
|
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
env.meta()->getFieldU32(sfGasUsed) == allowance,
|
||||||
|
std::to_string(env.meta()->getFieldU32(sfGasUsed)));
|
||||||
|
}
|
||||||
|
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
fee(finishFee),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
txMeta->getFieldU32(sfGasUsed) == allowance,
|
||||||
|
std::to_string(txMeta->getFieldU32(sfGasUsed)));
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
txMeta->getFieldI32(sfWasmReturnCode) == 5,
|
||||||
|
std::to_string(txMeta->getFieldI32(sfWasmReturnCode)));
|
||||||
|
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FinishFunction + Condition
|
||||||
|
Env env(*this, features);
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
// create escrow
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(createFee));
|
||||||
|
env.close();
|
||||||
|
auto const conditionFinishFee = finishFee +
|
||||||
|
env.current()->fees().base * (32 + (escrow::fb1.size() / 16));
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(env.ownerCount(alice) == 2))
|
||||||
|
{
|
||||||
|
env.require(balance(alice, XRP(4000) - createFee));
|
||||||
|
env.require(balance(carol, XRP(5000)));
|
||||||
|
|
||||||
|
// no fulfillment provided, function fails
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecCRYPTOCONDITION_ERROR));
|
||||||
|
// fulfillment provided, function fails
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
escrow::fulfillment(escrow::fb1),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(conditionFinishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
if (BEAST_EXPECT(env.meta()->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
env.meta()->getFieldU32(sfGasUsed) == allowance,
|
||||||
|
std::to_string(env.meta()->getFieldU32(sfGasUsed)));
|
||||||
|
env.close();
|
||||||
|
// no fulfillment provided, function succeeds
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(conditionFinishFee),
|
||||||
|
ter(tecCRYPTOCONDITION_ERROR));
|
||||||
|
// wrong fulfillment provided, function succeeds
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
escrow::fulfillment(escrow::fb2),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(conditionFinishFee),
|
||||||
|
ter(tecCRYPTOCONDITION_ERROR));
|
||||||
|
// fulfillment provided, function succeeds, tx succeeds
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
escrow::condition(escrow::cb1),
|
||||||
|
escrow::fulfillment(escrow::fb1),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(conditionFinishFee),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
txMeta->getFieldI32(sfWasmReturnCode) == 6,
|
||||||
|
std::to_string(txMeta->getFieldI32(sfWasmReturnCode)));
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FinishFunction + FinishAfter
|
||||||
|
Env env(*this, features);
|
||||||
|
// create escrow
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
auto const ts = env.now() + 97s;
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::finish_time(ts),
|
||||||
|
escrow::cancel_time(env.now() + 1000s),
|
||||||
|
fee(createFee));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(env.ownerCount(alice) == 2))
|
||||||
|
{
|
||||||
|
env.require(balance(alice, XRP(4000) - createFee));
|
||||||
|
env.require(balance(carol, XRP(5000)));
|
||||||
|
|
||||||
|
// finish time hasn't passed, function fails
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee + 1),
|
||||||
|
ter(tecNO_PERMISSION));
|
||||||
|
env.close();
|
||||||
|
// finish time hasn't passed, function succeeds
|
||||||
|
for (; env.now() < ts; env.close())
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee + 2),
|
||||||
|
ter(tecNO_PERMISSION));
|
||||||
|
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee + 1),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
txMeta->getFieldI32(sfWasmReturnCode) == 13,
|
||||||
|
std::to_string(txMeta->getFieldI32(sfWasmReturnCode)));
|
||||||
|
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FinishFunction + FinishAfter #2
|
||||||
|
Env env(*this, features);
|
||||||
|
// create escrow
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::finish_time(env.now() + 2s),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(createFee));
|
||||||
|
// Don't close the ledger here
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(env.ownerCount(alice) == 2))
|
||||||
|
{
|
||||||
|
env.require(balance(alice, XRP(4000) - createFee));
|
||||||
|
env.require(balance(carol, XRP(5000)));
|
||||||
|
|
||||||
|
// finish time hasn't passed, function fails
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecNO_PERMISSION));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// finish time has passed, function fails
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecWASM_REJECTED));
|
||||||
|
if (BEAST_EXPECT(env.meta()->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
env.meta()->getFieldU32(sfGasUsed) == allowance,
|
||||||
|
std::to_string(env.meta()->getFieldU32(sfGasUsed)));
|
||||||
|
env.close();
|
||||||
|
// finish time has passed, function succeeds, tx succeeds
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
txMeta->getFieldI32(sfWasmReturnCode) == 6,
|
||||||
|
std::to_string(txMeta->getFieldI32(sfWasmReturnCode)));
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testAllHostFunctions(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("Test all host functions");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// TODO: create wasm module for all host functions
|
||||||
|
static auto wasmHex = allHostFunctionsWasmHex;
|
||||||
|
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const carol{"carol"};
|
||||||
|
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
// create escrow
|
||||||
|
env.fund(XRP(5000), alice, carol);
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
auto escrowCreate = escrow::create(alice, carol, XRP(1000));
|
||||||
|
XRPAmount txnFees =
|
||||||
|
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
|
||||||
|
env(escrowCreate,
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::finish_time(env.now() + 11s),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
escrow::data("1000000000"), // 1000 XRP in drops
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(
|
||||||
|
env.ownerCount(alice) == (1 + wasmHex.size() / 2 / 500)))
|
||||||
|
{
|
||||||
|
env.require(balance(alice, XRP(4000) - txnFees));
|
||||||
|
env.require(balance(carol, XRP(5000)));
|
||||||
|
|
||||||
|
auto const allowance = 1'000'000;
|
||||||
|
XRPAmount const finishFee = env.current()->fees().base +
|
||||||
|
(allowance * env.current()->fees().gasPrice) /
|
||||||
|
MICRO_DROPS_PER_DROP +
|
||||||
|
1;
|
||||||
|
|
||||||
|
// FinishAfter time hasn't passed
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tecNO_PERMISSION));
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// reduce the destination balance
|
||||||
|
env(pay(carol, alice, XRP(4500)));
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(escrow::finish(alice, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
txMeta->getFieldU32(sfGasUsed) == 38'571,
|
||||||
|
std::to_string(txMeta->getFieldU32(sfGasUsed)));
|
||||||
|
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||||
|
BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1);
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testKeyletHostFunctions(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("Test all keylet host functions");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// TODO: create wasm module for all host functions
|
||||||
|
static auto wasmHex = allKeyletsWasmHex;
|
||||||
|
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const carol{"carol"};
|
||||||
|
|
||||||
|
{
|
||||||
|
Env env{*this};
|
||||||
|
env.fund(XRP(10000), alice, carol);
|
||||||
|
|
||||||
|
BEAST_EXPECT(env.seq(alice) == 4);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 0);
|
||||||
|
|
||||||
|
// base objects that need to be created first
|
||||||
|
auto const tokenId =
|
||||||
|
token::getNextID(env, alice, 0, tfTransferable);
|
||||||
|
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||||
|
env(trust(alice, carol["USD"](1'000'000)));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == 6);
|
||||||
|
BEAST_EXPECT(env.ownerCount(alice) == 2);
|
||||||
|
|
||||||
|
// set up a bunch of objects to check their keylets
|
||||||
|
AMM amm(env, carol, XRP(10), carol["USD"](1000));
|
||||||
|
env(check::create(alice, carol, XRP(100)));
|
||||||
|
env(credentials::create(alice, alice, "termsandconditions"));
|
||||||
|
env(delegate::set(alice, carol, {"TrustSet"}));
|
||||||
|
env(deposit::auth(alice, carol));
|
||||||
|
env(did::set(alice), did::data("alice_did"));
|
||||||
|
env(escrow::create(alice, carol, XRP(100)),
|
||||||
|
escrow::finish_time(env.now() + 100s));
|
||||||
|
MPTTester mptTester{env, alice, {.fund = false}};
|
||||||
|
mptTester.create();
|
||||||
|
mptTester.authorize({.account = carol});
|
||||||
|
env(token::createOffer(carol, tokenId, XRP(100)),
|
||||||
|
token::owner(alice));
|
||||||
|
env(offer(alice, carol["GBP"](0.1), XRP(100)));
|
||||||
|
env(create(alice, carol, XRP(1000), 100s, alice.pk()));
|
||||||
|
pdomain::Credentials credentials{{alice, "first credential"}};
|
||||||
|
env(pdomain::setTx(alice, credentials));
|
||||||
|
env(signers(alice, 1, {{carol, 1}}));
|
||||||
|
env(ticket::create(alice, 1));
|
||||||
|
Vault vault{env};
|
||||||
|
auto [tx, _keylet] =
|
||||||
|
vault.create({.owner = alice, .asset = xrpIssue()});
|
||||||
|
env(tx);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
env.ownerCount(alice) == 16,
|
||||||
|
std::to_string(env.ownerCount(alice)));
|
||||||
|
if (BEAST_EXPECTS(
|
||||||
|
env.seq(alice) == 20, std::to_string(env.seq(alice))))
|
||||||
|
{
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
XRPAmount txnFees =
|
||||||
|
env.current()->fees().base * 10 + wasmHex.size() / 2 * 5;
|
||||||
|
env(escrow::create(alice, carol, XRP(1000)),
|
||||||
|
escrow::finish_function(wasmHex),
|
||||||
|
escrow::finish_time(env.now() + 2s),
|
||||||
|
escrow::cancel_time(env.now() + 100s),
|
||||||
|
fee(txnFees));
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto const allowance = 137'596;
|
||||||
|
auto const finishFee = env.current()->fees().base +
|
||||||
|
(allowance * env.current()->fees().gasPrice) /
|
||||||
|
MICRO_DROPS_PER_DROP +
|
||||||
|
1;
|
||||||
|
env(escrow::finish(carol, alice, seq),
|
||||||
|
escrow::comp_allowance(allowance),
|
||||||
|
fee(finishFee));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto const txMeta = env.meta();
|
||||||
|
if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed)))
|
||||||
|
{
|
||||||
|
auto const gasUsed = txMeta->getFieldU32(sfGasUsed);
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
gasUsed == allowance, std::to_string(gasUsed));
|
||||||
|
}
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
env.ownerCount(alice) == 16,
|
||||||
|
std::to_string(env.ownerCount(alice)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testWithFeats(FeatureBitset features)
|
testWithFeats(FeatureBitset features)
|
||||||
{
|
{
|
||||||
@@ -1716,6 +2467,13 @@ struct Escrow_test : public beast::unit_test::suite
|
|||||||
testConsequences(features);
|
testConsequences(features);
|
||||||
testEscrowWithTickets(features);
|
testEscrowWithTickets(features);
|
||||||
testCredentials(features);
|
testCredentials(features);
|
||||||
|
testCreateFinishFunctionPreflight(features);
|
||||||
|
testFinishWasmFailures(features);
|
||||||
|
testFinishFunction(features);
|
||||||
|
|
||||||
|
// TODO: Update module with new host functions
|
||||||
|
testAllHostFunctions(features);
|
||||||
|
testKeyletHostFunctions(features);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -1726,8 +2484,7 @@ public:
|
|||||||
FeatureBitset const all{testable_amendments()};
|
FeatureBitset const all{testable_amendments()};
|
||||||
testWithFeats(all);
|
testWithFeats(all);
|
||||||
testWithFeats(all - featureTokenEscrow);
|
testWithFeats(all - featureTokenEscrow);
|
||||||
testTags(all - fixIncludeKeyletFields);
|
};
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(Escrow, app, ripple);
|
BEAST_DEFINE_TESTSUITE(Escrow, app, ripple);
|
||||||
|
|||||||
@@ -24,8 +24,7 @@
|
|||||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||||
#include <xrpld/app/wasm/HostFunc.h>
|
#include <xrpld/app/wasm/HostFunc.h>
|
||||||
#include <xrpld/app/wasm/WasmVM.h>
|
#include <xrpld/app/wasm/WasmVM.h>
|
||||||
|
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||||
#include <xrpl/ledger/detail/ApplyViewBase.h>
|
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
|||||||
@@ -649,7 +649,7 @@ struct Wasm_test : public beast::unit_test::suite
|
|||||||
Bytes const wasm(wasmStr.begin(), wasmStr.end());
|
Bytes const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
TestHostFunctions hfs(env, 0);
|
TestHostFunctions hfs(env, 0);
|
||||||
|
|
||||||
auto const allowance = 148'406;
|
auto const allowance = 121'895;
|
||||||
auto re = runEscrowWasm(
|
auto re = runEscrowWasm(
|
||||||
wasm, ESCROW_FUNCTION_NAME, {}, &hfs, allowance, env.journal);
|
wasm, ESCROW_FUNCTION_NAME, {}, &hfs, allowance, env.journal);
|
||||||
|
|
||||||
@@ -709,7 +709,7 @@ struct Wasm_test : public beast::unit_test::suite
|
|||||||
testWasmSha();
|
testWasmSha();
|
||||||
testWasmB58();
|
testWasmB58();
|
||||||
|
|
||||||
// running too long
|
// runing too long
|
||||||
// testWasmSP1Verifier();
|
// testWasmSP1Verifier();
|
||||||
testWasmBG16Verifier();
|
testWasmBG16Verifier();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <test/jtx/Account.h>
|
#include <test/jtx/Account.h>
|
||||||
#include <test/jtx/Env.h>
|
#include <test/jtx/Env.h>
|
||||||
#include <test/jtx/TestHelpers.h>
|
|
||||||
#include <test/jtx/owners.h>
|
#include <test/jtx/owners.h>
|
||||||
#include <test/jtx/rate.h>
|
#include <test/jtx/rate.h>
|
||||||
|
|
||||||
@@ -95,14 +94,156 @@ std::array<std::uint8_t, 39> const cb3 = {
|
|||||||
0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4, 0x81, 0x01, 0x04}};
|
0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4, 0x81, 0x01, 0x04}};
|
||||||
|
|
||||||
/** Set the "FinishAfter" time tag on a JTx */
|
/** Set the "FinishAfter" time tag on a JTx */
|
||||||
auto const finish_time = JTxFieldWrapper<timePointField>(sfFinishAfter);
|
struct finish_time
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
NetClock::time_point value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit finish_time(NetClock::time_point const& value) : value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfFinishAfter.jsonName] = value_.time_since_epoch().count();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** Set the "CancelAfter" time tag on a JTx */
|
/** Set the "CancelAfter" time tag on a JTx */
|
||||||
auto const cancel_time = JTxFieldWrapper<timePointField>(sfCancelAfter);
|
struct cancel_time
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
NetClock::time_point value_;
|
||||||
|
|
||||||
auto const condition = JTxFieldWrapper<blobField>(sfCondition);
|
public:
|
||||||
|
explicit cancel_time(NetClock::time_point const& value) : value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
auto const fulfillment = JTxFieldWrapper<blobField>(sfFulfillment);
|
void
|
||||||
|
operator()(jtx::Env&, jtx::JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfCancelAfter.jsonName] = value_.time_since_epoch().count();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct condition
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit condition(Slice const& cond) : value_(strHex(cond))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
explicit condition(std::array<std::uint8_t, N> const& c)
|
||||||
|
: condition(makeSlice(c))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfCondition.jsonName] = value_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fulfillment
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fulfillment(Slice condition) : value_(strHex(condition))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
explicit fulfillment(std::array<std::uint8_t, N> f)
|
||||||
|
: fulfillment(makeSlice(f))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfFulfillment.jsonName] = value_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct finish_function
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit finish_function(std::string func) : value_(func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit finish_function(Slice const& func) : value_(strHex(func))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
explicit finish_function(std::array<std::uint8_t, N> const& f)
|
||||||
|
: finish_function(makeSlice(f))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfFinishFunction.jsonName] = value_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit data(std::string func) : value_(func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit data(Slice const& func) : value_(strHex(func))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
explicit data(std::array<std::uint8_t, N> const& f) : data(makeSlice(f))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfData.jsonName] = value_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct comp_allowance
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::uint32_t value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit comp_allowance(std::uint32_t const& value) : value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv[sfComputationAllowance.jsonName] = value_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace escrow
|
} // namespace escrow
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,14 @@ setupConfigForUnitTests(Config& cfg)
|
|||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
// Default fees to old values, so tests don't have to worry about changes in
|
// Default fees to old values, so tests don't have to worry about changes in
|
||||||
// Config.h
|
// Config.h
|
||||||
|
// NOTE: For new `FEES` fields, you need to wait for the first flag ledger
|
||||||
|
// to close for the values to be activated.
|
||||||
cfg.FEES.reference_fee = UNIT_TEST_REFERENCE_FEE;
|
cfg.FEES.reference_fee = UNIT_TEST_REFERENCE_FEE;
|
||||||
cfg.FEES.account_reserve = XRP(200).value().xrp().drops();
|
cfg.FEES.account_reserve = XRP(200).value().xrp().drops();
|
||||||
cfg.FEES.owner_reserve = XRP(50).value().xrp().drops();
|
cfg.FEES.owner_reserve = XRP(50).value().xrp().drops();
|
||||||
|
cfg.FEES.extension_compute_limit = 1'000'000;
|
||||||
|
cfg.FEES.extension_size_limit = 100'000;
|
||||||
|
cfg.FEES.gas_price = 1'000'000; // 1 drop = 1,000,000 micro-drops
|
||||||
|
|
||||||
// The Beta API (currently v2) is always available to tests
|
// The Beta API (currently v2) is always available to tests
|
||||||
cfg.BETA_RPC_API = true;
|
cfg.BETA_RPC_API = true;
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ ApplyContext::discard()
|
|||||||
std::optional<TxMeta>
|
std::optional<TxMeta>
|
||||||
ApplyContext::apply(TER ter)
|
ApplyContext::apply(TER ter)
|
||||||
{
|
{
|
||||||
|
if (wasmReturnCode_.has_value())
|
||||||
|
{
|
||||||
|
view_->setWasmReturnCode(*wasmReturnCode_);
|
||||||
|
}
|
||||||
|
view_->setGasUsed(gasUsed_);
|
||||||
return view_->apply(
|
return view_->apply(
|
||||||
base_, tx, ter, parentBatchId_, flags_ & tapDRY_RUN, journal);
|
base_, tx, ter, parentBatchId_, flags_ & tapDRY_RUN, journal);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
#include <xrpld/app/main/Application.h>
|
#include <xrpld/app/main/Application.h>
|
||||||
#include <xrpld/core/Config.h>
|
#include <xrpld/core/Config.h>
|
||||||
|
#include <xrpld/ledger/ApplyViewImpl.h>
|
||||||
|
|
||||||
#include <xrpl/beast/utility/Journal.h>
|
#include <xrpl/beast/utility/Journal.h>
|
||||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
|
||||||
#include <xrpl/protocol/STTx.h>
|
#include <xrpl/protocol/STTx.h>
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
|
|
||||||
@@ -106,6 +106,20 @@ public:
|
|||||||
view_->deliver(amount);
|
view_->deliver(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets the gas used in the metadata */
|
||||||
|
void
|
||||||
|
setGasUsed(std::uint32_t const gasUsed)
|
||||||
|
{
|
||||||
|
gasUsed_ = gasUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the gas used in the metadata */
|
||||||
|
void
|
||||||
|
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||||
|
{
|
||||||
|
wasmReturnCode_ = wasmReturnCode;
|
||||||
|
}
|
||||||
|
|
||||||
/** Discard changes and start fresh. */
|
/** Discard changes and start fresh. */
|
||||||
void
|
void
|
||||||
discard();
|
discard();
|
||||||
@@ -157,6 +171,8 @@ private:
|
|||||||
|
|
||||||
// The ID of the batch transaction we are executing under, if seated.
|
// The ID of the batch transaction we are executing under, if seated.
|
||||||
std::optional<uint256 const> parentBatchId_;
|
std::optional<uint256 const> parentBatchId_;
|
||||||
|
std::optional<std::uint32_t> gasUsed_;
|
||||||
|
std::optional<std::int32_t> wasmReturnCode_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -17,23 +17,26 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||||
#include <xrpld/app/misc/HashRouter.h>
|
#include <xrpld/app/misc/HashRouter.h>
|
||||||
#include <xrpld/app/tx/detail/Escrow.h>
|
#include <xrpld/app/tx/detail/Escrow.h>
|
||||||
#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
||||||
|
#include <xrpld/app/wasm/HostFuncImpl.h>
|
||||||
|
#include <xrpld/app/wasm/WasmVM.h>
|
||||||
#include <xrpld/conditions/Condition.h>
|
#include <xrpld/conditions/Condition.h>
|
||||||
#include <xrpld/conditions/Fulfillment.h>
|
#include <xrpld/conditions/Fulfillment.h>
|
||||||
|
#include <xrpld/ledger/ApplyView.h>
|
||||||
|
#include <xrpld/ledger/View.h>
|
||||||
|
|
||||||
#include <xrpl/basics/Log.h>
|
#include <xrpl/basics/Log.h>
|
||||||
#include <xrpl/basics/chrono.h>
|
#include <xrpl/basics/chrono.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
|
||||||
#include <xrpl/ledger/CredentialHelpers.h>
|
|
||||||
#include <xrpl/ledger/View.h>
|
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/MPTAmount.h>
|
#include <xrpl/protocol/MPTAmount.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
// During an EscrowFinish, the transaction must specify both
|
// During an EscrowFinish, the transaction must specify both
|
||||||
@@ -81,8 +84,8 @@ constexpr HashRouterFlags SF_CF_VALID = HashRouterFlags::PRIVATE6;
|
|||||||
TxConsequences
|
TxConsequences
|
||||||
EscrowCreate::makeTxConsequences(PreflightContext const& ctx)
|
EscrowCreate::makeTxConsequences(PreflightContext const& ctx)
|
||||||
{
|
{
|
||||||
auto const amount = ctx.tx[sfAmount];
|
return TxConsequences{
|
||||||
return TxConsequences{ctx.tx, isXRP(amount) ? amount.xrp() : beast::zero};
|
ctx.tx, isXRP(ctx.tx[sfAmount]) ? ctx.tx[sfAmount].xrp() : beast::zero};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <ValidIssueType T>
|
template <ValidIssueType T>
|
||||||
@@ -118,9 +121,29 @@ escrowCreatePreflightHelper<MPTIssue>(PreflightContext const& ctx)
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XRPAmount
|
||||||
|
EscrowCreate::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||||
|
{
|
||||||
|
XRPAmount txnFees{Transactor::calculateBaseFee(view, tx)};
|
||||||
|
if (tx.isFieldPresent(sfFinishFunction))
|
||||||
|
{
|
||||||
|
// 10 base fees for the transaction (1 is in
|
||||||
|
// `Transactor::calculateBaseFee`), plus 5 drops per byte
|
||||||
|
txnFees += 9 * view.fees().base + 5 * tx[sfFinishFunction].size();
|
||||||
|
}
|
||||||
|
return txnFees;
|
||||||
|
}
|
||||||
|
|
||||||
NotTEC
|
NotTEC
|
||||||
EscrowCreate::preflight(PreflightContext const& ctx)
|
EscrowCreate::preflight(PreflightContext const& ctx)
|
||||||
{
|
{
|
||||||
|
if (ctx.tx.isFieldPresent(sfFinishFunction) &&
|
||||||
|
!ctx.rules.enabled(featureSmartEscrow))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "SmartEscrow not enabled";
|
||||||
|
return temDISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx.rules.enabled(fix1543) && ctx.tx.getFlags() & tfUniversalMask)
|
if (ctx.rules.enabled(fix1543) && ctx.tx.getFlags() & tfUniversalMask)
|
||||||
return temINVALID_FLAG;
|
return temINVALID_FLAG;
|
||||||
|
|
||||||
@@ -157,14 +180,23 @@ EscrowCreate::preflight(PreflightContext const& ctx)
|
|||||||
ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter])
|
ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter])
|
||||||
return temBAD_EXPIRATION;
|
return temBAD_EXPIRATION;
|
||||||
|
|
||||||
|
if (ctx.tx.isFieldPresent(sfFinishFunction) &&
|
||||||
|
!ctx.tx.isFieldPresent(sfCancelAfter))
|
||||||
|
return temBAD_EXPIRATION;
|
||||||
|
|
||||||
if (ctx.rules.enabled(fix1571))
|
if (ctx.rules.enabled(fix1571))
|
||||||
{
|
{
|
||||||
// In the absence of a FinishAfter, the escrow can be finished
|
// In the absence of a FinishAfter, the escrow can be finished
|
||||||
// immediately, which can be confusing. When creating an escrow,
|
// immediately, which can be confusing. When creating an escrow,
|
||||||
// we want to ensure that either a FinishAfter time is explicitly
|
// we want to ensure that either a FinishAfter time is explicitly
|
||||||
// specified or a completion condition is attached.
|
// specified or a completion condition is attached.
|
||||||
if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition])
|
if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition] &&
|
||||||
|
!ctx.tx[~sfFinishFunction])
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "Must have at least one of FinishAfter, "
|
||||||
|
"Condition, or FinishFunction.";
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const cb = ctx.tx[~sfCondition])
|
if (auto const cb = ctx.tx[~sfCondition])
|
||||||
@@ -189,6 +221,27 @@ EscrowCreate::preflight(PreflightContext const& ctx)
|
|||||||
return temDISABLED;
|
return temDISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.tx.isFieldPresent(sfFinishFunction))
|
||||||
|
{
|
||||||
|
auto const code = ctx.tx.getFieldVL(sfFinishFunction);
|
||||||
|
if (code.size() == 0 ||
|
||||||
|
code.size() > ctx.app.config().FEES.extension_size_limit)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug())
|
||||||
|
<< "EscrowCreate.FinishFunction bad size " << code.size();
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostFunctions mock;
|
||||||
|
auto const re =
|
||||||
|
preflightEscrowWasm(code, ESCROW_FUNCTION_NAME, {}, &mock, ctx.j);
|
||||||
|
if (!isTesSuccess(re))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "EscrowCreate.FinishFunction bad WASM";
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return preflight2(ctx);
|
return preflight2(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,6 +504,17 @@ escrowLockApplyHelper<MPTIssue>(
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static uint32_t
|
||||||
|
calculateAdditionalReserve(T const& finishFunction)
|
||||||
|
{
|
||||||
|
if (!finishFunction)
|
||||||
|
return 1;
|
||||||
|
// First 500 bytes included in the normal reserve
|
||||||
|
// Each additional 500 bytes requires an additional reserve
|
||||||
|
return 1 + (finishFunction->size() / 500);
|
||||||
|
}
|
||||||
|
|
||||||
TER
|
TER
|
||||||
EscrowCreate::doApply()
|
EscrowCreate::doApply()
|
||||||
{
|
{
|
||||||
@@ -494,9 +558,11 @@ EscrowCreate::doApply()
|
|||||||
|
|
||||||
// Check reserve and funds availability
|
// Check reserve and funds availability
|
||||||
STAmount const amount{ctx_.tx[sfAmount]};
|
STAmount const amount{ctx_.tx[sfAmount]};
|
||||||
|
auto const reserveToAdd =
|
||||||
|
calculateAdditionalReserve(ctx_.tx[~sfFinishFunction]);
|
||||||
|
|
||||||
auto const reserve =
|
auto const reserve =
|
||||||
ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);
|
ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + reserveToAdd);
|
||||||
|
|
||||||
if (mSourceBalance < reserve)
|
if (mSourceBalance < reserve)
|
||||||
return tecINSUFFICIENT_RESERVE;
|
return tecINSUFFICIENT_RESERVE;
|
||||||
@@ -537,11 +603,8 @@ EscrowCreate::doApply()
|
|||||||
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
||||||
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
|
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
|
||||||
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
||||||
|
(*slep)[~sfFinishFunction] = ctx_.tx[~sfFinishFunction];
|
||||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
(*slep)[~sfData] = ctx_.tx[~sfData];
|
||||||
{
|
|
||||||
(*slep)[sfSequence] = ctx_.tx.getSeqValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
|
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
|
||||||
{
|
{
|
||||||
@@ -604,7 +667,8 @@ EscrowCreate::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// increment owner count
|
// increment owner count
|
||||||
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
|
// TODO: determine actual reserve based on FinishFunction size
|
||||||
|
adjustOwnerCount(ctx_.view(), sle, reserveToAdd, ctx_.journal);
|
||||||
ctx_.view().update(sle);
|
ctx_.view().update(sle);
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
@@ -639,6 +703,13 @@ EscrowFinish::preflight(PreflightContext const& ctx)
|
|||||||
!ctx.rules.enabled(featureCredentials))
|
!ctx.rules.enabled(featureCredentials))
|
||||||
return temDISABLED;
|
return temDISABLED;
|
||||||
|
|
||||||
|
if (ctx.tx.isFieldPresent(sfComputationAllowance) &&
|
||||||
|
!ctx.rules.enabled(featureSmartEscrow))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "SmartEscrow not enabled";
|
||||||
|
return temDISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -648,7 +719,10 @@ EscrowFinish::preflight(PreflightContext const& ctx)
|
|||||||
// If you specify a condition, then you must also specify
|
// If you specify a condition, then you must also specify
|
||||||
// a fulfillment.
|
// a fulfillment.
|
||||||
if (static_cast<bool>(cb) != static_cast<bool>(fb))
|
if (static_cast<bool>(cb) != static_cast<bool>(fb))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "Condition != Fulfillment";
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify the transaction signature. If it doesn't work
|
// Verify the transaction signature. If it doesn't work
|
||||||
// then don't do any more work.
|
// then don't do any more work.
|
||||||
@@ -677,6 +751,20 @@ EscrowFinish::preflight(PreflightContext const& ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto const allowance = ctx.tx[~sfComputationAllowance]; allowance)
|
||||||
|
{
|
||||||
|
if (*allowance == 0)
|
||||||
|
{
|
||||||
|
return temBAD_LIMIT;
|
||||||
|
}
|
||||||
|
if (*allowance > ctx.app.config().FEES.extension_compute_limit)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug())
|
||||||
|
<< "ComputationAllowance too large: " << *allowance;
|
||||||
|
return temBAD_LIMIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
|
if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
|
||||||
!isTesSuccess(err))
|
!isTesSuccess(err))
|
||||||
return err;
|
return err;
|
||||||
@@ -693,7 +781,14 @@ EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
{
|
{
|
||||||
extraFee += view.fees().base * (32 + (fb->size() / 16));
|
extraFee += view.fees().base * (32 + (fb->size() / 16));
|
||||||
}
|
}
|
||||||
|
if (auto const allowance = tx[~sfComputationAllowance]; allowance)
|
||||||
|
{
|
||||||
|
// The extra fee is the allowance in drops, rounded up to the nearest
|
||||||
|
// whole drop.
|
||||||
|
// Integer math rounds down by default, so we add 1 to round up.
|
||||||
|
extraFee +=
|
||||||
|
((*allowance) * view.fees().gasPrice) / MICRO_DROPS_PER_DROP + 1;
|
||||||
|
}
|
||||||
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,25 +868,52 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
if (ctx.view.rules().enabled(featureTokenEscrow) ||
|
||||||
|
ctx.view.rules().enabled(featureSmartEscrow))
|
||||||
{
|
{
|
||||||
|
// this check is done in doApply before this amendment is enabled
|
||||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||||
auto const slep = ctx.view.read(k);
|
auto const slep = ctx.view.read(k);
|
||||||
if (!slep)
|
if (!slep)
|
||||||
return tecNO_TARGET;
|
return tecNO_TARGET;
|
||||||
|
|
||||||
AccountID const dest = (*slep)[sfDestination];
|
if (ctx.view.rules().enabled(featureSmartEscrow))
|
||||||
STAmount const amount = (*slep)[sfAmount];
|
|
||||||
|
|
||||||
if (!isXRP(amount))
|
|
||||||
{
|
{
|
||||||
if (auto const ret = std::visit(
|
if (slep->isFieldPresent(sfFinishFunction))
|
||||||
[&]<typename T>(T const&) {
|
{
|
||||||
return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
|
if (!ctx.tx.isFieldPresent(sfComputationAllowance))
|
||||||
},
|
{
|
||||||
amount.asset().value());
|
JLOG(ctx.j.debug())
|
||||||
!isTesSuccess(ret))
|
<< "FinishFunction requires ComputationAllowance";
|
||||||
return ret;
|
return tefWASM_FIELD_NOT_INCLUDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ctx.tx.isFieldPresent(sfComputationAllowance))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "FinishFunction not present, "
|
||||||
|
"ComputationAllowance present";
|
||||||
|
return tefNO_WASM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||||
|
{
|
||||||
|
AccountID const dest = (*slep)[sfDestination];
|
||||||
|
STAmount const amount = (*slep)[sfAmount];
|
||||||
|
|
||||||
|
if (!isXRP(amount))
|
||||||
|
{
|
||||||
|
if (auto const ret = std::visit(
|
||||||
|
[&]<typename T>(T const&) {
|
||||||
|
return escrowFinishPreclaimHelper<T>(
|
||||||
|
ctx, dest, amount);
|
||||||
|
},
|
||||||
|
amount.asset().value());
|
||||||
|
!isTesSuccess(ret))
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
@@ -1012,13 +1134,8 @@ escrowUnlockApplyHelper<MPTIssue>(
|
|||||||
// compute balance to transfer
|
// compute balance to transfer
|
||||||
finalAmt = amount.value() - xferFee;
|
finalAmt = amount.value() - xferFee;
|
||||||
}
|
}
|
||||||
return rippleUnlockEscrowMPT(
|
|
||||||
view,
|
return rippleUnlockEscrowMPT(view, sender, receiver, finalAmt, journal);
|
||||||
sender,
|
|
||||||
receiver,
|
|
||||||
finalAmt,
|
|
||||||
view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
|
|
||||||
journal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TER
|
TER
|
||||||
@@ -1028,7 +1145,8 @@ EscrowFinish::doApply()
|
|||||||
auto const slep = ctx_.view().peek(k);
|
auto const slep = ctx_.view().peek(k);
|
||||||
if (!slep)
|
if (!slep)
|
||||||
{
|
{
|
||||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
if (ctx_.view().rules().enabled(featureTokenEscrow) ||
|
||||||
|
ctx_.view().rules().enabled(featureSmartEscrow))
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
return tecNO_TARGET;
|
return tecNO_TARGET;
|
||||||
@@ -1043,11 +1161,17 @@ EscrowFinish::doApply()
|
|||||||
|
|
||||||
// Too soon: can't execute before the finish time
|
// Too soon: can't execute before the finish time
|
||||||
if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
|
if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
|
||||||
|
{
|
||||||
|
JLOG(j_.debug()) << "Too soon";
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
|
|
||||||
// Too late: can't execute after the cancel time
|
// Too late: can't execute after the cancel time
|
||||||
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
|
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
|
||||||
|
{
|
||||||
|
JLOG(j_.debug()) << "Too late";
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1055,13 +1179,36 @@ EscrowFinish::doApply()
|
|||||||
if ((*slep)[~sfFinishAfter] &&
|
if ((*slep)[~sfFinishAfter] &&
|
||||||
ctx_.view().info().parentCloseTime.time_since_epoch().count() <=
|
ctx_.view().info().parentCloseTime.time_since_epoch().count() <=
|
||||||
(*slep)[sfFinishAfter])
|
(*slep)[sfFinishAfter])
|
||||||
|
{
|
||||||
|
JLOG(j_.debug()) << "Too soon?";
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
|
|
||||||
// Too late?
|
// Too late?
|
||||||
if ((*slep)[~sfCancelAfter] &&
|
if ((*slep)[~sfCancelAfter] &&
|
||||||
ctx_.view().info().parentCloseTime.time_since_epoch().count() <=
|
ctx_.view().info().parentCloseTime.time_since_epoch().count() <=
|
||||||
(*slep)[sfCancelAfter])
|
(*slep)[sfCancelAfter])
|
||||||
|
{
|
||||||
|
JLOG(j_.debug()) << "Too late?";
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountID const destID = (*slep)[sfDestination];
|
||||||
|
auto const sled = ctx_.view().peek(keylet::account(destID));
|
||||||
|
if (ctx_.view().rules().enabled(featureSmartEscrow))
|
||||||
|
{
|
||||||
|
// NOTE: Escrow payments cannot be used to fund accounts.
|
||||||
|
if (!sled)
|
||||||
|
return tecNO_DST;
|
||||||
|
|
||||||
|
if (ctx_.view().rules().enabled(featureDepositAuth))
|
||||||
|
{
|
||||||
|
if (auto err = verifyDepositPreauth(
|
||||||
|
ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
|
||||||
|
!isTesSuccess(err))
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cryptocondition fulfillment
|
// Check cryptocondition fulfillment
|
||||||
@@ -1111,18 +1258,65 @@ EscrowFinish::doApply()
|
|||||||
return tecCRYPTOCONDITION_ERROR;
|
return tecCRYPTOCONDITION_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Escrow payments cannot be used to fund accounts.
|
if (!ctx_.view().rules().enabled(featureSmartEscrow))
|
||||||
AccountID const destID = (*slep)[sfDestination];
|
|
||||||
auto const sled = ctx_.view().peek(keylet::account(destID));
|
|
||||||
if (!sled)
|
|
||||||
return tecNO_DST;
|
|
||||||
|
|
||||||
if (ctx_.view().rules().enabled(featureDepositAuth))
|
|
||||||
{
|
{
|
||||||
if (auto err = verifyDepositPreauth(
|
// NOTE: Escrow payments cannot be used to fund accounts.
|
||||||
ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
|
if (!sled)
|
||||||
!isTesSuccess(err))
|
return tecNO_DST;
|
||||||
return err;
|
|
||||||
|
if (ctx_.view().rules().enabled(featureDepositAuth))
|
||||||
|
{
|
||||||
|
if (auto err = verifyDepositPreauth(
|
||||||
|
ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
|
||||||
|
!isTesSuccess(err))
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute custom release function
|
||||||
|
if ((*slep)[~sfFinishFunction])
|
||||||
|
{
|
||||||
|
JLOG(j_.trace())
|
||||||
|
<< "The escrow has a finish function, running WASM code...";
|
||||||
|
// WASM execution
|
||||||
|
auto const wasmStr = slep->getFieldVL(sfFinishFunction);
|
||||||
|
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
WasmHostFunctionsImpl ledgerDataProvider(ctx_, k);
|
||||||
|
|
||||||
|
if (!ctx_.tx.isFieldPresent(sfComputationAllowance))
|
||||||
|
{
|
||||||
|
// already checked above, this check is just in case
|
||||||
|
return tecINTERNAL;
|
||||||
|
}
|
||||||
|
std::uint32_t allowance = ctx_.tx[sfComputationAllowance];
|
||||||
|
auto re = runEscrowWasm(
|
||||||
|
wasm, ESCROW_FUNCTION_NAME, {}, &ledgerDataProvider, allowance);
|
||||||
|
JLOG(j_.trace()) << "Escrow WASM ran";
|
||||||
|
|
||||||
|
if (auto const& data = ledgerDataProvider.getData(); data.has_value())
|
||||||
|
{
|
||||||
|
slep->setFieldVL(sfData, makeSlice(*data));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (re.has_value())
|
||||||
|
{
|
||||||
|
auto reValue = re.value().result;
|
||||||
|
ctx_.setWasmReturnCode(reValue);
|
||||||
|
// TODO: better error handling for this conversion
|
||||||
|
ctx_.setGasUsed(static_cast<uint32_t>(re.value().cost));
|
||||||
|
JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue)
|
||||||
|
<< ", cost: " << re.value().cost;
|
||||||
|
if (reValue <= 0)
|
||||||
|
{
|
||||||
|
return tecWASM_REJECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JLOG(j_.debug()) << "WASM Failure: " + transHuman(re.error());
|
||||||
|
return re.error();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountID const account = (*slep)[sfAccount];
|
AccountID const account = (*slep)[sfAccount];
|
||||||
@@ -1195,9 +1389,12 @@ EscrowFinish::doApply()
|
|||||||
|
|
||||||
ctx_.view().update(sled);
|
ctx_.view().update(sled);
|
||||||
|
|
||||||
|
auto const reserveToSubtract =
|
||||||
|
calculateAdditionalReserve((*slep)[~sfFinishFunction]);
|
||||||
|
|
||||||
// Adjust source owner count
|
// Adjust source owner count
|
||||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||||
adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
|
adjustOwnerCount(ctx_.view(), sle, -1 * reserveToSubtract, ctx_.journal);
|
||||||
ctx_.view().update(sle);
|
ctx_.view().update(sle);
|
||||||
|
|
||||||
// Remove escrow from ledger
|
// Remove escrow from ledger
|
||||||
@@ -1408,7 +1605,9 @@ EscrowCancel::doApply()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
|
auto const reserveToSubtract =
|
||||||
|
calculateAdditionalReserve((*slep)[~sfFinishFunction]);
|
||||||
|
adjustOwnerCount(ctx_.view(), sle, -1 * reserveToSubtract, ctx_.journal);
|
||||||
ctx_.view().update(sle);
|
ctx_.view().update(sle);
|
||||||
|
|
||||||
// Remove escrow from ledger
|
// Remove escrow from ledger
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ public:
|
|||||||
static TxConsequences
|
static TxConsequences
|
||||||
makeTxConsequences(PreflightContext const& ctx);
|
makeTxConsequences(PreflightContext const& ctx);
|
||||||
|
|
||||||
|
static XRPAmount
|
||||||
|
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||||
|
|
||||||
static NotTEC
|
static NotTEC
|
||||||
preflight(PreflightContext const& ctx);
|
preflight(PreflightContext const& ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <xrpld/app/main/Application.h>
|
#include <xrpld/app/main/Application.h>
|
||||||
|
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||||
#include <xrpld/app/misc/DelegateUtils.h>
|
#include <xrpld/app/misc/DelegateUtils.h>
|
||||||
#include <xrpld/app/misc/LoadFeeTrack.h>
|
#include <xrpld/app/misc/LoadFeeTrack.h>
|
||||||
#include <xrpld/app/tx/apply.h>
|
#include <xrpld/app/tx/apply.h>
|
||||||
@@ -25,12 +26,11 @@
|
|||||||
#include <xrpld/app/tx/detail/SignerEntries.h>
|
#include <xrpld/app/tx/detail/SignerEntries.h>
|
||||||
#include <xrpld/app/tx/detail/Transactor.h>
|
#include <xrpld/app/tx/detail/Transactor.h>
|
||||||
#include <xrpld/core/Config.h>
|
#include <xrpld/core/Config.h>
|
||||||
|
#include <xrpld/ledger/View.h>
|
||||||
|
|
||||||
#include <xrpl/basics/Log.h>
|
#include <xrpl/basics/Log.h>
|
||||||
#include <xrpl/basics/contract.h>
|
#include <xrpl/basics/contract.h>
|
||||||
#include <xrpl/json/to_string.h>
|
#include <xrpl/json/to_string.h>
|
||||||
#include <xrpl/ledger/CredentialHelpers.h>
|
|
||||||
#include <xrpl/ledger/View.h>
|
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/Protocol.h>
|
#include <xrpl/protocol/Protocol.h>
|
||||||
@@ -206,10 +206,7 @@ preflight2(PreflightContext const& ctx)
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
Transactor::Transactor(ApplyContext& ctx)
|
Transactor::Transactor(ApplyContext& ctx)
|
||||||
: ctx_(ctx)
|
: ctx_(ctx), j_(ctx.journal), account_(ctx.tx.getAccountID(sfAccount))
|
||||||
, sink_(ctx.journal, to_short_string(ctx.tx.getTransactionID()) + " ")
|
|
||||||
, j_(sink_)
|
|
||||||
, account_(ctx.tx.getAccountID(sfAccount))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -967,6 +964,22 @@ removeExpiredCredentials(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modifyWasmDataFields(
|
||||||
|
ApplyView& view,
|
||||||
|
std::vector<std::pair<uint256, Blob>> const& wasmObjects,
|
||||||
|
beast::Journal viewJ)
|
||||||
|
{
|
||||||
|
for (auto const& [index, data] : wasmObjects)
|
||||||
|
{
|
||||||
|
if (auto const sle = view.peek(keylet::escrow(index)))
|
||||||
|
{
|
||||||
|
sle->setFieldVL(sfData, data);
|
||||||
|
view.update(sle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
removeDeletedTrustLines(
|
removeDeletedTrustLines(
|
||||||
ApplyView& view,
|
ApplyView& view,
|
||||||
@@ -1124,6 +1137,7 @@ Transactor::operator()()
|
|||||||
else if (
|
else if (
|
||||||
(result == tecOVERSIZE) || (result == tecKILLED) ||
|
(result == tecOVERSIZE) || (result == tecKILLED) ||
|
||||||
(result == tecINCOMPLETE) || (result == tecEXPIRED) ||
|
(result == tecINCOMPLETE) || (result == tecEXPIRED) ||
|
||||||
|
(result == tecWASM_REJECTED) ||
|
||||||
(isTecClaimHardFail(result, view().flags())))
|
(isTecClaimHardFail(result, view().flags())))
|
||||||
{
|
{
|
||||||
JLOG(j_.trace()) << "reapplying because of " << transToken(result);
|
JLOG(j_.trace()) << "reapplying because of " << transToken(result);
|
||||||
@@ -1136,13 +1150,16 @@ Transactor::operator()()
|
|||||||
std::vector<uint256> removedTrustLines;
|
std::vector<uint256> removedTrustLines;
|
||||||
std::vector<uint256> expiredNFTokenOffers;
|
std::vector<uint256> expiredNFTokenOffers;
|
||||||
std::vector<uint256> expiredCredentials;
|
std::vector<uint256> expiredCredentials;
|
||||||
|
std::vector<std::pair<uint256, Blob>> modifiedWasmObjects;
|
||||||
|
|
||||||
bool const doOffers =
|
bool const doOffers =
|
||||||
((result == tecOVERSIZE) || (result == tecKILLED));
|
((result == tecOVERSIZE) || (result == tecKILLED));
|
||||||
bool const doLines = (result == tecINCOMPLETE);
|
bool const doLines = (result == tecINCOMPLETE);
|
||||||
bool const doNFTokenOffers = (result == tecEXPIRED);
|
bool const doNFTokenOffers = (result == tecEXPIRED);
|
||||||
bool const doCredentials = (result == tecEXPIRED);
|
bool const doCredentials = (result == tecEXPIRED);
|
||||||
if (doOffers || doLines || doNFTokenOffers || doCredentials)
|
bool const doWasmData = (result == tecWASM_REJECTED);
|
||||||
|
if (doOffers || doLines || doNFTokenOffers || doCredentials ||
|
||||||
|
doWasmData)
|
||||||
{
|
{
|
||||||
ctx_.visit([doOffers,
|
ctx_.visit([doOffers,
|
||||||
&removedOffers,
|
&removedOffers,
|
||||||
@@ -1151,7 +1168,9 @@ Transactor::operator()()
|
|||||||
doNFTokenOffers,
|
doNFTokenOffers,
|
||||||
&expiredNFTokenOffers,
|
&expiredNFTokenOffers,
|
||||||
doCredentials,
|
doCredentials,
|
||||||
&expiredCredentials](
|
&expiredCredentials,
|
||||||
|
doWasmData,
|
||||||
|
&modifiedWasmObjects](
|
||||||
uint256 const& index,
|
uint256 const& index,
|
||||||
bool isDelete,
|
bool isDelete,
|
||||||
std::shared_ptr<SLE const> const& before,
|
std::shared_ptr<SLE const> const& before,
|
||||||
@@ -1186,6 +1205,13 @@ Transactor::operator()()
|
|||||||
(before->getType() == ltCREDENTIAL))
|
(before->getType() == ltCREDENTIAL))
|
||||||
expiredCredentials.push_back(index);
|
expiredCredentials.push_back(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (doWasmData && before && after &&
|
||||||
|
(before->getType() == ltESCROW))
|
||||||
|
{
|
||||||
|
modifiedWasmObjects.push_back(
|
||||||
|
std::make_pair(index, after->getFieldVL(sfData)));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1215,6 +1241,10 @@ Transactor::operator()()
|
|||||||
removeExpiredCredentials(
|
removeExpiredCredentials(
|
||||||
view(), expiredCredentials, ctx_.app.journal("View"));
|
view(), expiredCredentials, ctx_.app.journal("View"));
|
||||||
|
|
||||||
|
if (result == tecWASM_REJECTED)
|
||||||
|
modifyWasmDataFields(
|
||||||
|
view(), modifiedWasmObjects, ctx_.app.journal("View"));
|
||||||
|
|
||||||
applied = isTecClaim(result);
|
applied = isTecClaim(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user