Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Builds/CMake/RippledCore.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/apply.cpp
src/ripple/app/tx/impl/applySteps.cpp
src/ripple/app/hook/impl/applyHook.cpp
src/ripple/app/hook/impl/GasValidator.cpp
src/ripple/app/tx/impl/details/NFTokenUtils.cpp
#[===============================[
main sources:
Expand Down Expand Up @@ -909,6 +910,7 @@ if (tests)
src/test/jtx/impl/fee.cpp
src/test/jtx/impl/flags.cpp
src/test/jtx/impl/genesis.cpp
src/test/jtx/impl/hookgas.cpp
src/test/jtx/impl/import.cpp
src/test/jtx/impl/invoice_id.cpp
src/test/jtx/impl/invoke.cpp
Expand Down
4 changes: 4 additions & 0 deletions hook/sfcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
#define sfEmitGeneration ((2U << 16U) + 46U)
#define sfLockCount ((2U << 16U) + 49U)
#define sfFirstNFTokenSequence ((2U << 16U) + 50U)
#define sfHookCallbackGas ((2U << 16U) + 89U)
#define sfHookWeakGas ((2U << 16U) + 90U)
#define sfHookInstructionCost ((2U << 16U) + 91U)
#define sfHookGas ((2U << 16U) + 92U)
#define sfStartTime ((2U << 16U) + 93U)
#define sfRepeatCount ((2U << 16U) + 94U)
#define sfDelaySeconds ((2U << 16U) + 95U)
Expand Down
1 change: 1 addition & 0 deletions src/ripple/app/hook/Enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ enum ExitType : uint8_t {
WASM_ERROR = 1,
ROLLBACK = 2,
ACCEPT = 3,
GAS_INSUFFICIENT = 4,
};

const uint16_t max_state_modifications = 256;
Expand Down
61 changes: 61 additions & 0 deletions src/ripple/app/hook/GasValidator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2024 XRPL-Labs

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#ifndef RIPPLE_APP_HOOK_GASVALIDATOR_H_INCLUDED
#define RIPPLE_APP_HOOK_GASVALIDATOR_H_INCLUDED

#include <ripple/basics/Expected.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/protocol/Rules.h>
#include <functional>
#include <optional>
#include <ostream>
#include <string>
#include <vector>

// Forward declaration
using GuardLog =
std::optional<std::reference_wrapper<std::basic_ostream<char>>>;

namespace hook {

/**
* @brief Validate WASM host functions for Gas-type hooks
*
* Validates that a WASM binary only imports allowed host functions
* and does not import the _g (guard) function, which is only for
* Guard-type hooks.
*
* @param wasm The WASM binary to validate
* @param guardLog Logging function for validation errors
* @param guardLogAccStr Account string for logging
* @return bool if validation succeeds,
* @return true if contains cbak function
* @return false if otherwise
* @return error message if validation fails
*/
ripple::Expected<bool, std::string>
validateWasmHostFunctionsForGas(
std::vector<uint8_t> const& wasm,
ripple::Rules const& rules,
beast::Journal const& j);

} // namespace hook

#endif // RIPPLE_APP_HOOK_GASVALIDATOR_H_INCLUDED
62 changes: 50 additions & 12 deletions src/ripple/app/hook/applyHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <ripple/protocol/SField.h>
#include <ripple/protocol/TER.h>
#include <ripple/protocol/digest.h>
#include <any>
#include <memory>
#include <optional>
#include <queue>
Expand Down Expand Up @@ -462,7 +461,9 @@ apply(
uint32_t wasmParam,
uint8_t hookChainPosition,
// result of apply() if this is weak exec
std::shared_ptr<STObject const> const& provisionalMeta);
std::shared_ptr<STObject const> const& provisionalMeta,
uint16_t hookApiVersion,
uint32_t hookGas);

struct HookContext;

Expand Down Expand Up @@ -501,6 +502,7 @@ struct HookResult
std::string exitReason{""};
int64_t exitCode{-1};
uint64_t instructionCount{0};
uint64_t instructionCost{0};
bool hasCallback = false; // true iff this hook wasm has a cbak function
bool isCallback =
false; // true iff this hook execution is a callback in action
Expand All @@ -513,6 +515,8 @@ struct HookResult
false; // hook_again allows strong pre-apply to nominate
// additional weak post-apply execution
std::shared_ptr<STObject const> provisionalMeta;
uint16_t hookApiVersion = 0; // 0 = Guard-type, 1 = Gas-type
uint32_t hookGas; // Gas limit for Gas-type hooks
};

class HookExecutor;
Expand Down Expand Up @@ -652,12 +656,18 @@ class HookExecutor
WasmEdge_ConfigureContext* conf = NULL;
WasmEdge_VMContext* ctx = NULL;

WasmEdgeVM()
WasmEdgeVM(uint16_t hookApiVersion)
{
conf = WasmEdge_ConfigureCreate();
if (!conf)
return;
WasmEdge_ConfigureStatisticsSetInstructionCounting(conf, true);
if (hookApiVersion == 1)
{
WasmEdge_ConfigureStatisticsSetCostMeasuring(conf, true);
uint32_t maxMemoryPage = 8;
WasmEdge_ConfigureSetMaxMemoryPage(conf, maxMemoryPage);
}
ctx = WasmEdge_VMCreate(conf, NULL);
}

Expand Down Expand Up @@ -692,9 +702,9 @@ class HookExecutor
* Validate that a web assembly blob can be loaded by wasmedge
*/
static std::optional<std::string>
validateWasm(const void* wasm, size_t len)
validateWasm(const void* wasm, size_t len, uint16_t hookApiVersion)
{
WasmEdgeVM vm;
WasmEdgeVM vm{hookApiVersion};

if (!vm.sane())
return "Could not create WASMEDGE instance";
Expand Down Expand Up @@ -737,7 +747,7 @@ class HookExecutor

WasmEdge_LogOff();

WasmEdgeVM vm;
WasmEdgeVM vm{hookCtx.result.hookApiVersion};

if (!vm.sane())
{
Expand All @@ -758,6 +768,22 @@ class HookExecutor
return;
}

// Set Gas limit for Gas-type hooks (HookApiVersion == 1)
if (hookCtx.result.hookApiVersion == 1)
{
auto* statsCtx = WasmEdge_VMGetStatisticsContext(vm.ctx);
if (statsCtx)
{
// Convert HookGas to cost limit count (1 Gas = 1 cost)
uint32_t gasLimit = hookCtx.result.hookGas;
WasmEdge_StatisticsSetCostLimit(statsCtx, gasLimit);

JLOG(j.trace())
<< "HookInfo[" << HC_ACC() << "]: Set Gas limit to "
<< gasLimit << " cost limit for Gas-type Hook";
}
}

WasmEdge_Value params[1] = {WasmEdge_ValueGenI32((int64_t)wasmParam)};
WasmEdge_Value returns[1];

Expand All @@ -771,17 +797,29 @@ class HookExecutor
returns,
1);

auto* statsCtx = WasmEdge_VMGetStatisticsContext(vm.ctx);
hookCtx.result.instructionCount =
WasmEdge_StatisticsGetInstrCount(statsCtx);
hookCtx.result.instructionCost =
WasmEdge_StatisticsGetTotalCost(statsCtx);

if (auto err = getWasmError("WASM VM error", res); err)
{
JLOG(j.warn()) << "HookError[" << HC_ACC() << "]: " << *err;
hookCtx.result.exitType = hook_api::ExitType::WASM_ERROR;
JLOG(j.trace()) << "HookError[" << HC_ACC() << "]: " << *err;

// Check if error is due to Gas limit exceeded for Gas-type hooks
if (hookCtx.result.hookApiVersion == 1 &&
err->find("cost limit exceeded") != std::string::npos)
{
JLOG(j.trace()) << "HookError[" << HC_ACC()
<< "]: Gas limit exceeded. Limit was "
<< hookCtx.result.hookGas;
}

hookCtx.result.exitType = hook_api::ExitType::GAS_INSUFFICIENT;
return;
}

auto* statsCtx = WasmEdge_VMGetStatisticsContext(vm.ctx);
hookCtx.result.instructionCount =
WasmEdge_StatisticsGetInstrCount(statsCtx);

// RH NOTE: stack unwind will clean up WasmEdgeVM
}

Expand Down
Loading
Loading