mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-28 17:40:25 +00:00
host function error path refactor (#7639)
This commit is contained in:
@@ -360,6 +360,7 @@ enum TECcodes : TERUnderlyingType {
|
||||
tecLIMIT_EXCEEDED = 195,
|
||||
tecPSEUDO_ACCOUNT = 196,
|
||||
tecPRECISION_LOSS = 197,
|
||||
tecOUT_OF_GAS = 200,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -113,398 +113,398 @@ public:
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<uint32_t, HostFunctionError>
|
||||
getBaseFee() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(uint256 const& amendmentId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(std::string_view const& amendmentName) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(FieldLocator const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(FieldLocator const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, FieldLocator const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(FieldLocator const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(FieldLocator const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, FieldLocator const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data)
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
ammKeylet(Asset const& issue1, Asset const& issue2) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
checkKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
|
||||
const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
didKeylet(AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
delegateKeylet(AccountID const& account, AccountID const& authorize) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
mptokenKeylet(MPTID const& mptid, AccountID const& holder) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
nftOfferKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
offerKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t docId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
signersKeylet(AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
ticketKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
vaultKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFTIssuer(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTTaxon(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getNFTFlags(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getNFTTransferFee(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTSerial(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceAccount(std::string_view const& msg, AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceFloat(std::string_view const& msg, Slice const& data) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceAmount(std::string_view const& msg, STAmount const& amount) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromInt(int64_t x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromUint(uint64_t x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromSTAmount(STAmount const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromSTNumber(STNumber const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int64_t, HostFunctionError>
|
||||
floatToInt(Slice const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<FloatPair, HostFunctionError>
|
||||
floatToMantExp(Slice const& x) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
floatCompare(Slice const& x, Slice const& y) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatAdd(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatSubtract(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatMultiply(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatDivide(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatRoot(Slice const& x, int32_t n, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatPower(Slice const& x, int32_t n, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
virtual ~HostFunctions() = default;
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -14,8 +16,20 @@ using Bytes = std::vector<std::uint8_t>;
|
||||
using Hash = xrpl::uint256;
|
||||
using FloatPair = std::pair<int64_t, int32_t>;
|
||||
|
||||
// Error signals that cross the wasm boundary as trap messages (the C API has no
|
||||
// trap code). WasmiEngine::call maps them to TER: hfErrInternal -> tecINTERNAL,
|
||||
// hfErrOutOfGas / wasmi's OutOfFuel -> tecOUT_OF_GAS, anything else ->
|
||||
// tecFAILED_PROCESSING.
|
||||
//
|
||||
// Matched as substrings, not by equality: the C API returns the Rust Debug form
|
||||
// of the error, e.g. `Error { kind: Message("HfInternal") }` or
|
||||
// `Error { kind: TrapCode(OutOfFuel) }`.
|
||||
std::string_view inline constexpr hfErrInternal = "HfInternal";
|
||||
std::string_view inline constexpr hfErrOutOfGas = "HfOutOfGas";
|
||||
std::string_view inline constexpr wasmiTrapOutOfFuel = "OutOfFuel";
|
||||
|
||||
enum class HostFunctionError : int32_t {
|
||||
Internal = -1,
|
||||
Unimplemented = -1,
|
||||
FieldNotFound = -2,
|
||||
BufferTooSmall = -3,
|
||||
NoArray = -4,
|
||||
@@ -25,7 +39,7 @@ enum class HostFunctionError : int32_t {
|
||||
SlotsFull = -8,
|
||||
EmptySlot = -9,
|
||||
LedgerObjNotFound = -10,
|
||||
Decoding = -11,
|
||||
OutOfTransferLimit = -11,
|
||||
DataFieldTooLarge = -12,
|
||||
PointerOutOfBounds = -13,
|
||||
NoMemExported = -14,
|
||||
@@ -35,9 +49,6 @@ enum class HostFunctionError : int32_t {
|
||||
IndexOutOfBounds = -18,
|
||||
FloatInputMalformed = -19,
|
||||
FloatComputationError = -20,
|
||||
NoRuntime = -21,
|
||||
OutOfGas = -22,
|
||||
OutOfTransferLimit = -23,
|
||||
};
|
||||
|
||||
enum class WasmTypes { WtI32, WtI64 };
|
||||
@@ -61,6 +72,15 @@ struct WasmResult
|
||||
};
|
||||
using EscrowResult = WasmResult<int32_t>;
|
||||
|
||||
// Engine error when wasm does not run to completion. `cost` is the gas consumed
|
||||
// when meaningful (tecOUT_OF_GAS / tecFAILED_PROCESSING; caller writes it to tx
|
||||
// metadata); std::nullopt for tecINTERNAL and malformed input (no gas reported).
|
||||
struct WasmTER
|
||||
{
|
||||
TER ter;
|
||||
std::optional<int64_t> cost;
|
||||
};
|
||||
|
||||
class FieldLocator
|
||||
{
|
||||
int32_t const* ptr_ = nullptr;
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
static WasmEngine&
|
||||
instance();
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
Expected<WasmResult<int32_t>, WasmTER>
|
||||
run(Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
int64_t gasLimit,
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
ImportVec
|
||||
createWasmImport(HostFunctions& hfs);
|
||||
|
||||
Expected<EscrowResult, TER>
|
||||
Expected<EscrowResult, WasmTER>
|
||||
runEscrowWasm(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <wasm.h>
|
||||
#include <wasmi.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class T, void (*Create)(T*, size_t), void (*Destroy)(T*)>
|
||||
@@ -120,7 +122,10 @@ using WasmImporttypeVec = WasmVec<
|
||||
struct WasmiResult
|
||||
{
|
||||
WasmValVec r;
|
||||
bool f{false}; // failure flag
|
||||
// Set iff the call trapped. Holds the TER the trap was classified into
|
||||
// (tecINTERNAL / tecOUT_OF_GAS / tecFAILED_PROCESSING); see
|
||||
// WasmiEngine::call. std::nullopt means the call returned normally.
|
||||
std::optional<TER> ter;
|
||||
|
||||
WasmiResult(unsigned n = 0) : r(n)
|
||||
{
|
||||
@@ -304,7 +309,7 @@ public:
|
||||
static EnginePtr
|
||||
init();
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
Expected<WasmResult<int32_t>, WasmTER>
|
||||
run(Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
int64_t gas,
|
||||
@@ -355,7 +360,7 @@ private:
|
||||
return moduleWrap_ ? moduleWrap_->getMem() : Wmem();
|
||||
}
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
Expected<WasmResult<int32_t>, WasmTER>
|
||||
runHlp(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
|
||||
@@ -106,6 +106,7 @@ transResults()
|
||||
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
|
||||
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(tecOUT_OF_GAS, "The WASM code ran out of gas during execution."),
|
||||
|
||||
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
||||
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -13,6 +14,8 @@
|
||||
#include <xrpl/tx/wasm/WasmCommon.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
@@ -121,7 +124,9 @@ getAnyFieldData(FieldValue const& variantObj)
|
||||
if (uint256 const* const* u = std::get_if<uint256 const*>(&variantObj))
|
||||
return Bytes((*u)->begin(), (*u)->end());
|
||||
|
||||
return Unexpected(HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
// Unreachable: the variant only holds the two alternatives above. If not,
|
||||
// it's an xrpld bug -> tecINTERNAL (thrown, caught by HostFuncMain_wrap).
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -31,6 +32,8 @@
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -43,7 +46,10 @@ using SFieldCRef = std::reference_wrapper<SField const>;
|
||||
|
||||
constexpr int64_t unalignedGas = 50;
|
||||
|
||||
static inline Expected<std::int64_t, HostFunctionError>
|
||||
// Charge `delta` gas; returns the remaining gas. Out-of-gas throws hfErrOutOfGas
|
||||
// (-> tecOUT_OF_GAS); a failed setGas is an xrpld bug, throws hfErrInternal
|
||||
// (-> tecINTERNAL). HostFuncMain_wrap turns both into traps.
|
||||
static inline std::int64_t
|
||||
checkGas(WasmRuntimeWrapper& rt, int64_t delta)
|
||||
{
|
||||
int64_t const gas = rt.getGas();
|
||||
@@ -53,36 +59,16 @@ checkGas(WasmRuntimeWrapper& rt, int64_t delta)
|
||||
int64_t const x = gas >= delta ? gas - delta : 0;
|
||||
|
||||
if (rt.setGas(x) < 0)
|
||||
return Unexpected(HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
if (gas < delta)
|
||||
return Unexpected(HostFunctionError::OutOfGas);
|
||||
Throw<std::runtime_error>(std::string(hfErrOutOfGas));
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline Expected<std::int64_t, wasm_trap_t*>
|
||||
checkGas(WasmRuntimeWrapper& rt, WasmImportFunc const& impFunc)
|
||||
{
|
||||
auto g = checkGas(rt, impFunc.gas);
|
||||
|
||||
if (!g)
|
||||
{
|
||||
if (g.error() == HostFunctionError::OutOfGas)
|
||||
{
|
||||
wasm_trap_t* const trap = // NOLINT
|
||||
reinterpret_cast<wasm_trap_t*>(WasmEngine::instance().newTrap("hf out of gas"));
|
||||
return Unexpected(trap);
|
||||
}
|
||||
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap("can't set gas")); // LCOV_EXCL_LINE
|
||||
return Unexpected(trap); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return *g;
|
||||
}
|
||||
|
||||
// Transfer limit is a separate soft budget: exceeding it is a normal guest-facing
|
||||
// return code, not a trap. Only a failed setTransferLimit (an xrpld bug) throws.
|
||||
static inline Expected<std::int64_t, HostFunctionError>
|
||||
checkTransfer(WasmRuntimeWrapper& rt, int64_t delta)
|
||||
{
|
||||
@@ -90,7 +76,7 @@ checkTransfer(WasmRuntimeWrapper& rt, int64_t delta)
|
||||
int64_t const x = transLimit >= delta ? transLimit - delta : 0;
|
||||
|
||||
if (rt.setTransferLimit(x) < 0)
|
||||
return Unexpected(HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
if (transLimit < delta)
|
||||
return Unexpected(HostFunctionError::OutOfTransferLimit);
|
||||
@@ -98,37 +84,27 @@ checkTransfer(WasmRuntimeWrapper& rt, int64_t delta)
|
||||
return x;
|
||||
}
|
||||
|
||||
static Expected<std::tuple<HostFunctions&, WasmImportFunc const&>, wasm_trap_t*>
|
||||
// On any failure here a C++ exception is thrown; HostFuncMain_wrap's catch-all
|
||||
// turns it into tecINTERNAL. These conditions are all xrpld-side invariants.
|
||||
static std::tuple<HostFunctions&, WasmImportFunc const&>
|
||||
mainCheck(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
|
||||
{
|
||||
if (env == nullptr)
|
||||
{
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap("no environment")); // LCOV_EXCL_LINE
|
||||
return Unexpected(trap); // LCOV_EXCL_LINE
|
||||
}
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
if (params == nullptr)
|
||||
{
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap("no params")); // LCOV_EXCL_LINE
|
||||
return Unexpected(trap); // LCOV_EXCL_LINE
|
||||
}
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
if (results == nullptr)
|
||||
{
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap("no results")); // LCOV_EXCL_LINE
|
||||
return Unexpected(trap); // LCOV_EXCL_LINE
|
||||
}
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
WasmUserData const* udata = reinterpret_cast<WasmUserData*>(env);
|
||||
HostFunctions& hf = udata->first;
|
||||
WasmRuntimeWrapper& rt = hf.getRT();
|
||||
WasmImportFunc const& impFunc = udata->second;
|
||||
|
||||
if (auto g = checkGas(rt, impFunc); !g)
|
||||
return Unexpected(g.error()); // LCOV_EXCL_LINE
|
||||
// Charge the per-call gas. Throws (and terminates) if out of gas.
|
||||
checkGas(rt, impFunc.gas);
|
||||
|
||||
return std::tie(hf, impFunc);
|
||||
}
|
||||
@@ -387,9 +363,10 @@ getDataLocator(WasmRuntimeWrapper& runtime, wasm_val_vec_t const* params, int32_
|
||||
if ((p & (alignof(int32_t) - 1)) != 0u)
|
||||
{ // unaligned
|
||||
|
||||
// Use gas and transfer limit for copying
|
||||
if (auto g = checkGas(runtime, unalignedGas); !g)
|
||||
return Unexpected(g.error());
|
||||
// Use gas and transfer limit for copying. checkGas throws (and
|
||||
// terminates execution) if out of gas; checkTransfer keeps returning a
|
||||
// guest-facing code when the transfer limit is exceeded.
|
||||
checkGas(runtime, unalignedGas);
|
||||
if (auto t = checkTransfer(runtime, slice->size()); !t)
|
||||
return Unexpected(t.error());
|
||||
|
||||
@@ -435,7 +412,7 @@ returnResult(
|
||||
if constexpr (std::is_same_v<T, Bytes>)
|
||||
{
|
||||
if (index < 0 || index + 1 >= params->size)
|
||||
return hfResult(results, HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
auto const dataResult = setData(
|
||||
runtime,
|
||||
@@ -448,7 +425,7 @@ returnResult(
|
||||
else if constexpr (std::is_same_v<T, Hash>)
|
||||
{
|
||||
if (index < 0 || index + 1 >= params->size)
|
||||
return hfResult(results, HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
auto const dataResult = setData(
|
||||
runtime,
|
||||
@@ -465,7 +442,7 @@ returnResult(
|
||||
else if constexpr (std::is_same_v<T, std::uint32_t>)
|
||||
{
|
||||
if (index < 0 || index + 1 >= params->size)
|
||||
return hfResult(results, HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
auto const resultValue = adjustWasmEndianess(res.value());
|
||||
auto const dataResult = setData(
|
||||
@@ -479,7 +456,7 @@ returnResult(
|
||||
else if constexpr (std::is_same_v<T, int64_t>)
|
||||
{
|
||||
if (index < 0 || index + 1 >= params->size)
|
||||
return hfResult(results, HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
auto const resultValue = adjustWasmEndianess(res.value());
|
||||
auto const dataResult = setData(
|
||||
@@ -493,7 +470,7 @@ returnResult(
|
||||
else if constexpr (std::is_same_v<T, FloatPair>)
|
||||
{
|
||||
if (index < 0 || index + 3 >= params->size)
|
||||
return hfResult(results, HostFunctionError::Internal); // LCOV_EXCL_LINE
|
||||
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
|
||||
|
||||
auto const mantissa = adjustWasmEndianess(res->first);
|
||||
auto const r1 = setData(
|
||||
@@ -533,11 +510,7 @@ HostFuncMain_wrap(WASM_CB_PARAMS_LIST)
|
||||
|
||||
try
|
||||
{
|
||||
auto const mc = mainCheck(env, params, results);
|
||||
if (!mc)
|
||||
return mc.error();
|
||||
|
||||
auto& [hf, impFunc] = *mc;
|
||||
auto [hf, impFunc] = mainCheck(env, params, results);
|
||||
hfName = impFunc.name;
|
||||
auto* fWrap = reinterpret_cast<wasmSecondaryCbFuncType*>(impFunc.wrap);
|
||||
return fWrap(hf, params, results);
|
||||
@@ -547,8 +520,11 @@ HostFuncMain_wrap(WASM_CB_PARAMS_LIST)
|
||||
#ifdef DEBUG_OUTPUT
|
||||
std::cerr << "Hostfunction " << hfName << " exception: " << e.what() << std::endl;
|
||||
#endif
|
||||
// Normalize to the two boundary signals: explicit out-of-gas, else any
|
||||
// exception (including stray ones from helpers) is an internal fault.
|
||||
bool const oog = std::string_view(e.what()) == hfErrOutOfGas;
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap(e.what())); // LCOV_EXCL_LINE
|
||||
WasmEngine::instance().newTrap(std::string(oog ? hfErrOutOfGas : hfErrInternal)));
|
||||
return trap;
|
||||
}
|
||||
catch (...)
|
||||
@@ -556,12 +532,12 @@ HostFuncMain_wrap(WASM_CB_PARAMS_LIST)
|
||||
#ifdef DEBUG_OUTPUT
|
||||
std::cerr << "Hostfunction " << hfName << " unknown exception." << std::endl;
|
||||
#endif
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap("Unknown exception")); // LCOV_EXCL_LINE
|
||||
wasm_trap_t* trap = reinterpret_cast<wasm_trap_t*>( // NOLINT
|
||||
WasmEngine::instance().newTrap(std::string(hfErrInternal))); // LCOV_EXCL_LINE
|
||||
return trap;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -115,7 +115,7 @@ createWasmImport(HostFunctions& hfs)
|
||||
return i;
|
||||
}
|
||||
|
||||
Expected<EscrowResult, TER>
|
||||
Expected<EscrowResult, WasmTER>
|
||||
runEscrowWasm(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
@@ -133,9 +133,12 @@ runEscrowWasm(
|
||||
if (!ret)
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
std::cout << ", error: " << ret.error() << std::endl;
|
||||
std::cout << ", error: " << ret.error().ter << std::endl;
|
||||
#endif
|
||||
return Unexpected<TER>(ret.error());
|
||||
// Carries the TER (tecOUT_OF_GAS / tecFAILED_PROCESSING / tecINTERNAL /
|
||||
// temBAD_AMOUNT) and, when meaningful, the gas consumed. The caller is
|
||||
// responsible for writing that gas to tx metadata.
|
||||
return Unexpected(ret.error());
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
@@ -174,7 +177,7 @@ WasmEngine::instance()
|
||||
return e;
|
||||
}
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
Expected<WasmResult<int32_t>, WasmTER>
|
||||
WasmEngine::run(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -78,6 +79,26 @@ printWasmError(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog)
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
// Extract a trap's message into a std::string (the only signal the C API gives
|
||||
// for classification; see the trap-signal constants in WasmCommon.h). Does not
|
||||
// take ownership of `trap`.
|
||||
std::string
|
||||
trapMessage(wasm_trap_t* trap)
|
||||
{
|
||||
if (trap == nullptr)
|
||||
return {}; // LCOV_EXCL_LINE
|
||||
wasm_byte_vec_t msg WASM_EMPTY_VEC;
|
||||
wasm_trap_message(trap, &msg);
|
||||
std::string out;
|
||||
if (msg.size != 0u)
|
||||
{
|
||||
// wasm_trap_message NUL-terminates, so drop the trailing NUL.
|
||||
out.assign(msg.data, msg.size - 1);
|
||||
wasm_byte_vec_delete(&msg);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class WasmiRuntimeWrapper : public WasmRuntimeWrapper
|
||||
@@ -679,7 +700,24 @@ WasmiEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
|
||||
|
||||
if (trap)
|
||||
{
|
||||
ret.f = true;
|
||||
// Classify the trap into a TER by matching tokens as substrings of the
|
||||
// message (see the trap-signal constants in WasmCommon.h for why).
|
||||
std::string const msg = trapMessage(trap);
|
||||
auto const has = [&msg](std::string_view token) {
|
||||
return msg.find(token) != std::string::npos;
|
||||
};
|
||||
if (has(hfErrInternal))
|
||||
{
|
||||
ret.ter = tecINTERNAL;
|
||||
}
|
||||
else if (has(hfErrOutOfGas) || has(wasmiTrapOutOfFuel))
|
||||
{
|
||||
ret.ter = tecOUT_OF_GAS;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.ter = tecFAILED_PROCESSING;
|
||||
}
|
||||
printWasmError("failure to call func", trap, j_);
|
||||
}
|
||||
|
||||
@@ -719,7 +757,7 @@ checkImports(ImportVec const& imports, HostFunctions* hfs)
|
||||
}
|
||||
}
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
Expected<WasmResult<int32_t>, WasmTER>
|
||||
WasmiEngine::run(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
@@ -730,7 +768,7 @@ WasmiEngine::run(
|
||||
beast::Journal j)
|
||||
{
|
||||
if (gas <= 0)
|
||||
return Unexpected<TER>(temBAD_AMOUNT);
|
||||
return Unexpected(WasmTER{.ter = temBAD_AMOUNT, .cost = std::nullopt});
|
||||
|
||||
try
|
||||
{
|
||||
@@ -747,10 +785,12 @@ WasmiEngine::run(
|
||||
printWasmError(std::string("exception: unknown"), nullptr, j);
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
return Unexpected<TER>(tecFAILED_PROCESSING);
|
||||
// An exception escaping the engine is an xrpld-side fault -> tecINTERNAL,
|
||||
// no gas. Genuine wasm faults don't throw; they surface as traps in runHlp.
|
||||
return Unexpected(WasmTER{.ter = tecINTERNAL, .cost = std::nullopt});
|
||||
}
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
Expected<WasmResult<int32_t>, WasmTER>
|
||||
WasmiEngine::runHlp(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
@@ -793,8 +833,24 @@ WasmiEngine::runHlp(
|
||||
|
||||
auto const res = call<1>(f, p);
|
||||
|
||||
if (res.f)
|
||||
Throw<std::runtime_error>("<" + std::string(funcName) + "> failure");
|
||||
if (gas == -1)
|
||||
gas = std::numeric_limits<decltype(gas)>::max();
|
||||
|
||||
if (res.ter.has_value())
|
||||
{
|
||||
// call() already classified the trap (see WasmiEngine::call).
|
||||
// tecINTERNAL is an xrpld-side bug: report no gas.
|
||||
if (*res.ter == tecINTERNAL)
|
||||
return Unexpected(WasmTER{.ter = tecINTERNAL, .cost = std::nullopt});
|
||||
|
||||
// Out-of-gas / wasm faults report gas (caller writes it to metadata).
|
||||
// Force fuel to 0 on out-of-gas so cost is the full limit (wasmi leaves
|
||||
// nonzero leftover fuel on its own out-of-fuel trap).
|
||||
if (*res.ter == tecOUT_OF_GAS)
|
||||
iw.setGas(0);
|
||||
|
||||
return Unexpected(WasmTER{.ter = *res.ter, .cost = gas - moduleWrap_->getGas()});
|
||||
}
|
||||
|
||||
if (res.r.empty())
|
||||
{
|
||||
@@ -809,8 +865,6 @@ WasmiEngine::runHlp(
|
||||
"> return type mismatch, ret: " + std::to_string(static_cast<int>(res.r[0].kind)));
|
||||
}
|
||||
|
||||
if (gas == -1)
|
||||
gas = std::numeric_limits<decltype(gas)>::max();
|
||||
WasmResult<int32_t> const ret{.result = res.r[0].of.i32, .cost = gas - moduleWrap_->getGas()};
|
||||
|
||||
// #ifdef DEBUG_OUTPUT
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
return Bytes{s.begin(), s.end()};
|
||||
}
|
||||
|
||||
return Unexpected(HostFunctionError::Internal);
|
||||
return Unexpected(HostFunctionError::Unimplemented);
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
|
||||
@@ -53,13 +53,13 @@ struct Wasm_test : public beast::unit_test::Suite
|
||||
{
|
||||
void
|
||||
checkResult(
|
||||
Expected<WasmResult<int32_t>, TER> re,
|
||||
Expected<WasmResult<int32_t>, WasmTER> re,
|
||||
int32_t expectedResult,
|
||||
int64_t expectedCost,
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
auto const lineStr = " (" + std::to_string(location.line()) + ")";
|
||||
if (BEAST_EXPECTS(re.has_value(), transToken(re.error()) + lineStr))
|
||||
if (BEAST_EXPECTS(re.has_value(), transToken(re.error().ter) + lineStr))
|
||||
{
|
||||
BEAST_EXPECTS(re->result == expectedResult, std::to_string(re->result) + lineStr);
|
||||
BEAST_EXPECTS(re->cost == expectedCost, std::to_string(re->cost) + lineStr);
|
||||
@@ -258,8 +258,10 @@ struct Wasm_test : public beast::unit_test::Suite
|
||||
|
||||
if (BEAST_EXPECT(!re))
|
||||
{
|
||||
// Running out of gas now terminates with tecOUT_OF_GAS (was
|
||||
// previously collapsed into tecFAILED_PROCESSING).
|
||||
BEAST_EXPECTS(
|
||||
re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error())));
|
||||
re.error().ter == tecOUT_OF_GAS, std::to_string(TERtoInt(re.error().ter)));
|
||||
}
|
||||
|
||||
env.close();
|
||||
@@ -286,7 +288,7 @@ struct Wasm_test : public beast::unit_test::Suite
|
||||
TestHostFunctions hfs(env);
|
||||
auto re = runEscrowWasm(allHFWasm, hfs, -1, escrowFunctionName, {});
|
||||
BEAST_EXPECT(!re.has_value());
|
||||
BEAST_EXPECT(re.error() == temBAD_AMOUNT);
|
||||
BEAST_EXPECT(re.error().ter == temBAD_AMOUNT);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -294,7 +296,7 @@ struct Wasm_test : public beast::unit_test::Suite
|
||||
TestHostFunctions hfs(env);
|
||||
auto re = runEscrowWasm(allHFWasm, hfs, 0, escrowFunctionName, {});
|
||||
BEAST_EXPECT(!re.has_value());
|
||||
BEAST_EXPECT(re.error() == temBAD_AMOUNT);
|
||||
BEAST_EXPECT(re.error().ter == temBAD_AMOUNT);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -379,7 +381,7 @@ struct Wasm_test : public beast::unit_test::Suite
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(badAlignWasm, hfs, 1'000'000, "test", {}, imports, env.journal);
|
||||
if (BEAST_EXPECTS(re, transToken(re.error())))
|
||||
if (BEAST_EXPECTS(re, transToken(re.error().ter)))
|
||||
{
|
||||
BEAST_EXPECTS(re->result == 0x47308594, std::to_string(re->result));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user