mirror of
https://github.com/Xahau/xahaud.git
synced 2026-01-24 16:45:19 +00:00
Compare commits
4 Commits
gas-hook
...
fix-ips-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4367eae0c | ||
|
|
e04464df38 | ||
|
|
f3c69ae34e | ||
|
|
aee8087042 |
3
.github/workflows/xahau-ga-macos.yml
vendored
3
.github/workflows/xahau-ga-macos.yml
vendored
@@ -43,9 +43,8 @@ jobs:
|
||||
# To isolate environments for each Runner, instead of installing globally with brew,
|
||||
# use mise to isolate environments for each Runner directory.
|
||||
- name: Setup toolchain (mise)
|
||||
uses: jdx/mise-action@v3.6.1
|
||||
uses: jdx/mise-action@v2
|
||||
with:
|
||||
cache: false
|
||||
install: true
|
||||
|
||||
- name: Install tools via mise
|
||||
|
||||
@@ -488,7 +488,6 @@ 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:
|
||||
@@ -910,7 +909,6 @@ 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
|
||||
|
||||
@@ -63,8 +63,6 @@
|
||||
#define sfEmitGeneration ((2U << 16U) + 46U)
|
||||
#define sfLockCount ((2U << 16U) + 49U)
|
||||
#define sfFirstNFTokenSequence ((2U << 16U) + 50U)
|
||||
#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)
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/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 std::nullopt if validation succeeds, error message otherwise
|
||||
*/
|
||||
std::optional<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
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <any>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
@@ -461,9 +462,7 @@ apply(
|
||||
uint32_t wasmParam,
|
||||
uint8_t hookChainPosition,
|
||||
// result of apply() if this is weak exec
|
||||
std::shared_ptr<STObject const> const& provisionalMeta,
|
||||
uint16_t hookApiVersion,
|
||||
uint32_t hookGas);
|
||||
std::shared_ptr<STObject const> const& provisionalMeta);
|
||||
|
||||
struct HookContext;
|
||||
|
||||
@@ -502,7 +501,6 @@ 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
|
||||
@@ -515,8 +513,6 @@ 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;
|
||||
@@ -656,18 +652,12 @@ public:
|
||||
WasmEdge_ConfigureContext* conf = NULL;
|
||||
WasmEdge_VMContext* ctx = NULL;
|
||||
|
||||
WasmEdgeVM(uint16_t hookApiVersion)
|
||||
WasmEdgeVM()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -702,9 +692,9 @@ public:
|
||||
* Validate that a web assembly blob can be loaded by wasmedge
|
||||
*/
|
||||
static std::optional<std::string>
|
||||
validateWasm(const void* wasm, size_t len, uint16_t hookApiVersion)
|
||||
validateWasm(const void* wasm, size_t len)
|
||||
{
|
||||
WasmEdgeVM vm{hookApiVersion};
|
||||
WasmEdgeVM vm;
|
||||
|
||||
if (!vm.sane())
|
||||
return "Could not create WASMEDGE instance";
|
||||
@@ -747,7 +737,7 @@ public:
|
||||
|
||||
WasmEdge_LogOff();
|
||||
|
||||
WasmEdgeVM vm{hookCtx.result.hookApiVersion};
|
||||
WasmEdgeVM vm;
|
||||
|
||||
if (!vm.sane())
|
||||
{
|
||||
@@ -768,22 +758,6 @@ public:
|
||||
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];
|
||||
|
||||
@@ -797,29 +771,17 @@ public:
|
||||
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.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;
|
||||
}
|
||||
|
||||
JLOG(j.warn()) << "HookError[" << HC_ACC() << "]: " << *err;
|
||||
hookCtx.result.exitType = hook_api::ExitType::WASM_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
auto* statsCtx = WasmEdge_VMGetStatisticsContext(vm.ctx);
|
||||
hookCtx.result.instructionCount =
|
||||
WasmEdge_StatisticsGetInstrCount(statsCtx);
|
||||
|
||||
// RH NOTE: stack unwind will clean up WasmEdgeVM
|
||||
}
|
||||
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/hook/GasValidator.h>
|
||||
#include <ripple/app/hook/Guard.h>
|
||||
#include <ripple/app/hook/Macro.h>
|
||||
#include <ripple/basics/Expected.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <wasmedge/wasmedge.h>
|
||||
|
||||
namespace hook {
|
||||
|
||||
Expected<void, std::string>
|
||||
validateExportSection(
|
||||
WasmEdge_ASTModuleContext* astModule,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
// Get export count
|
||||
uint32_t exportCount = WasmEdge_ASTModuleListExportsLength(astModule);
|
||||
if (exportCount == 0)
|
||||
{
|
||||
return Unexpected("WASM must export at least hook API functions");
|
||||
}
|
||||
|
||||
// Get exports
|
||||
const WasmEdge_ExportTypeContext* exports[256];
|
||||
uint32_t actualExportCount = std::min(exportCount, 256u);
|
||||
actualExportCount =
|
||||
WasmEdge_ASTModuleListExports(astModule, exports, actualExportCount);
|
||||
|
||||
// Track if we found required hook() function
|
||||
bool foundHook = false;
|
||||
|
||||
// Check each export
|
||||
for (uint32_t i = 0; i < actualExportCount; i++)
|
||||
{
|
||||
// Only check function exports
|
||||
WasmEdge_ExternalType const type =
|
||||
WasmEdge_ExportTypeGetExternalType(exports[i]);
|
||||
if (type != WasmEdge_ExternalType_Function)
|
||||
continue;
|
||||
|
||||
WasmEdge_String const name =
|
||||
WasmEdge_ExportTypeGetExternalName(exports[i]);
|
||||
std::string nameStr(name.Buf, name.Length);
|
||||
|
||||
if (nameStr.starts_with("__"))
|
||||
{
|
||||
// skip runtime support functions
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only allow hook() and cbak() exports
|
||||
if (nameStr != "hook" && nameStr != "cbak")
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::EXPORT_MISSING
|
||||
<< "): Unauthorized export function '" << nameStr
|
||||
<< "'. Only 'hook' and 'cbak' are allowed";
|
||||
return Unexpected(
|
||||
"Unauthorized export function '" + nameStr +
|
||||
"'. Only 'hook' and 'cbak' are allowed");
|
||||
}
|
||||
|
||||
if (nameStr == "hook")
|
||||
foundHook = true;
|
||||
|
||||
// Get function type to validate signature
|
||||
WasmEdge_FunctionTypeContext const* functionType =
|
||||
WasmEdge_ExportTypeGetFunctionType(astModule, exports[i]);
|
||||
|
||||
// Validate parameter count (must be exactly 1)
|
||||
uint32_t paramCount =
|
||||
WasmEdge_FunctionTypeGetParametersLength(functionType);
|
||||
if (paramCount != 1)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet("
|
||||
<< (nameStr == "hook" ? hook::log::EXPORT_HOOK_FUNC
|
||||
: hook::log::EXPORT_CBAK_FUNC)
|
||||
<< "): Function '" << nameStr
|
||||
<< "' must have exactly 1 parameter, found " << paramCount;
|
||||
return Unexpected(
|
||||
"Function '" + nameStr +
|
||||
"' must have exactly 1 parameter of type uint32_t");
|
||||
}
|
||||
|
||||
// Validate parameter type (must be i32 / uint32_t)
|
||||
WasmEdge_ValType parameters[1];
|
||||
WasmEdge_FunctionTypeGetParameters(functionType, parameters, 1);
|
||||
if (parameters[0] != WasmEdge_ValType_I32)
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet("
|
||||
<< (nameStr == "hook" ? hook::log::EXPORT_HOOK_FUNC
|
||||
: hook::log::EXPORT_CBAK_FUNC)
|
||||
<< "): Function '" << nameStr
|
||||
<< "' parameter must be uint32_t (i32), found type "
|
||||
<< parameters[0];
|
||||
return Unexpected(
|
||||
"Function '" + nameStr + "' parameter must be uint32_t (i32)");
|
||||
}
|
||||
|
||||
// Validate return type (must be i64 / uint64_t)
|
||||
uint32_t returnCount =
|
||||
WasmEdge_FunctionTypeGetReturnsLength(functionType);
|
||||
if (returnCount != 1)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet("
|
||||
<< (nameStr == "hook" ? hook::log::EXPORT_HOOK_FUNC
|
||||
: hook::log::EXPORT_CBAK_FUNC)
|
||||
<< "): Function '" << nameStr
|
||||
<< "' must return exactly 1 value, found " << returnCount;
|
||||
return Unexpected(
|
||||
"Function '" + nameStr +
|
||||
"' must return exactly 1 value of type uint64_t");
|
||||
}
|
||||
|
||||
WasmEdge_ValType returns[1];
|
||||
WasmEdge_FunctionTypeGetReturns(functionType, returns, 1);
|
||||
if (returns[0] != WasmEdge_ValType_I64)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet("
|
||||
<< (nameStr == "hook" ? hook::log::EXPORT_HOOK_FUNC
|
||||
: hook::log::EXPORT_CBAK_FUNC)
|
||||
<< "): Function '" << nameStr
|
||||
<< "' return type must be uint64_t (i64), found type "
|
||||
<< returns[0];
|
||||
return Unexpected(
|
||||
"Function '" + nameStr +
|
||||
"' return type must be uint64_t (i64)");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure hook() function was exported (required)
|
||||
if (!foundHook)
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::EXPORT_MISSING
|
||||
<< "): Required function 'hook' not found in exports";
|
||||
return Unexpected("Required function 'hook' not found in exports");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Expected<void, std::string>
|
||||
validateImportSection(
|
||||
WasmEdge_ASTModuleContext* astModule,
|
||||
Rules const& rules,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
// Get import count
|
||||
uint32_t importCount = WasmEdge_ASTModuleListImportsLength(astModule);
|
||||
|
||||
if (importCount == 0)
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::IMPORTS_MISSING
|
||||
<< "): WASM must import at least hook API functions";
|
||||
return Unexpected("WASM must import at least hook API functions");
|
||||
}
|
||||
|
||||
// Get imports (max 256)
|
||||
const WasmEdge_ImportTypeContext* imports[256];
|
||||
uint32_t actualImportCount = std::min(importCount, 256u);
|
||||
actualImportCount =
|
||||
WasmEdge_ASTModuleListImports(astModule, imports, actualImportCount);
|
||||
|
||||
std::optional<std::string> error;
|
||||
|
||||
// Check each import
|
||||
for (uint32_t i = 0; i < actualImportCount; i++)
|
||||
{
|
||||
WasmEdge_String moduleName =
|
||||
WasmEdge_ImportTypeGetModuleName(imports[i]);
|
||||
WasmEdge_String externalName =
|
||||
WasmEdge_ImportTypeGetExternalName(imports[i]);
|
||||
WasmEdge_ExternalType extType =
|
||||
WasmEdge_ImportTypeGetExternalType(imports[i]);
|
||||
|
||||
// Only check function imports
|
||||
if (extType != WasmEdge_ExternalType_Function)
|
||||
continue;
|
||||
|
||||
// Convert WasmEdge_String to std::string for comparison
|
||||
std::string modName(moduleName.Buf, moduleName.Length);
|
||||
std::string extName(externalName.Buf, externalName.Length);
|
||||
|
||||
// Check module name is "env"
|
||||
if (modName != "env")
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet(" << hook::log::IMPORT_MODULE_ENV
|
||||
<< "): Import module must be 'env', found: " << modName;
|
||||
return Unexpected("Import module must be 'env', found: " + modName);
|
||||
}
|
||||
|
||||
// Check for forbidden _g function (guard function)
|
||||
if (extName == "_g")
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet(" << hook::log::IMPORT_ILLEGAL
|
||||
<< "): Gas-type hooks cannot import _g (guard) function";
|
||||
return Unexpected(
|
||||
"Gas-type hooks cannot import _g (guard) function");
|
||||
}
|
||||
|
||||
// Determine which whitelist contains the function and get expected
|
||||
// signature
|
||||
std::vector<uint8_t> const* expectedSig = nullptr;
|
||||
auto baseIt = hook_api::import_whitelist.find(extName);
|
||||
if (baseIt != hook_api::import_whitelist.end())
|
||||
{
|
||||
expectedSig = &baseIt->second;
|
||||
}
|
||||
else if (rules.enabled(featureHooksUpdate1))
|
||||
{
|
||||
auto extIt = hook_api::import_whitelist_1.find(extName);
|
||||
if (extIt != hook_api::import_whitelist_1.end())
|
||||
{
|
||||
expectedSig = &extIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Function not in any whitelist
|
||||
if (!expectedSig)
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::IMPORT_ILLEGAL
|
||||
<< "): Import not in whitelist: " << extName;
|
||||
return Unexpected("Import not in whitelist: " + extName);
|
||||
}
|
||||
|
||||
// Get function type for signature validation
|
||||
WasmEdge_FunctionTypeContext const* functionType =
|
||||
WasmEdge_ImportTypeGetFunctionType(astModule, imports[i]);
|
||||
|
||||
if (!functionType)
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::FUNC_TYPELESS
|
||||
<< "): Import function '" << extName
|
||||
<< "' has no function type definition";
|
||||
return Unexpected(
|
||||
"Import function '" + extName +
|
||||
"' has no function type definition");
|
||||
}
|
||||
|
||||
// Validate return type
|
||||
// expectedSig[0] is the return type
|
||||
uint32_t returnCount =
|
||||
WasmEdge_FunctionTypeGetReturnsLength(functionType);
|
||||
|
||||
if (returnCount != 1)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet(" << hook::log::FUNC_RETURN_COUNT
|
||||
<< "): Import function '" << extName
|
||||
<< "' must return exactly 1 value, found " << returnCount;
|
||||
return Unexpected(
|
||||
"Import function '" + extName +
|
||||
"' must return exactly 1 value");
|
||||
}
|
||||
|
||||
WasmEdge_ValType actualReturnType;
|
||||
WasmEdge_FunctionTypeGetReturns(functionType, &actualReturnType, 1);
|
||||
|
||||
if (actualReturnType != (*expectedSig)[0])
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::FUNC_RETURN_INVALID
|
||||
<< "): Import function '" << extName
|
||||
<< "' has incorrect return type. Expected "
|
||||
<< static_cast<int>((*expectedSig)[0]) << ", found "
|
||||
<< static_cast<int>(actualReturnType);
|
||||
return Unexpected(
|
||||
"Import function '" + extName + "' has incorrect return type");
|
||||
}
|
||||
|
||||
// Validate parameter count and types
|
||||
// expectedSig[1..N] are the parameter types
|
||||
uint32_t expectedParamCount =
|
||||
expectedSig->size() > 0 ? expectedSig->size() - 1 : 0;
|
||||
uint32_t actualParamCount =
|
||||
WasmEdge_FunctionTypeGetParametersLength(functionType);
|
||||
|
||||
if (actualParamCount != expectedParamCount)
|
||||
{
|
||||
JLOG(j.trace()) << "HookSet(" << hook::log::FUNC_PARAM_INVALID
|
||||
<< "): Import function '" << extName << "' has "
|
||||
<< actualParamCount << " parameters, expected "
|
||||
<< expectedParamCount;
|
||||
return Unexpected(
|
||||
"Import function '" + extName +
|
||||
"' has incorrect parameter count");
|
||||
}
|
||||
|
||||
// Validate each parameter type
|
||||
if (actualParamCount > 0)
|
||||
{
|
||||
std::vector<WasmEdge_ValType> actualParams(actualParamCount);
|
||||
WasmEdge_FunctionTypeGetParameters(
|
||||
functionType, actualParams.data(), actualParamCount);
|
||||
|
||||
for (uint32_t p = 0; p < actualParamCount; p++)
|
||||
{
|
||||
uint8_t expectedParamType = (*expectedSig)[1 + p];
|
||||
if (actualParams[p] != expectedParamType)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "HookSet(" << hook::log::FUNC_PARAM_INVALID
|
||||
<< "): Import function '" << extName << "' parameter "
|
||||
<< p << " has incorrect type. Expected "
|
||||
<< static_cast<int>(expectedParamType) << ", found "
|
||||
<< static_cast<int>(actualParams[p]);
|
||||
return Unexpected(
|
||||
"Import function '" + extName +
|
||||
"' has incorrect parameter types");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
validateWasmHostFunctionsForGas(
|
||||
std::vector<uint8_t> const& wasm,
|
||||
Rules const& rules,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
// Create WasmEdge Loader
|
||||
WasmEdge_LoaderContext* loader = WasmEdge_LoaderCreate(NULL);
|
||||
if (!loader)
|
||||
{
|
||||
return "Failed to create WasmEdge Loader";
|
||||
}
|
||||
|
||||
// Parse WASM binary
|
||||
WasmEdge_ASTModuleContext* astModule = NULL;
|
||||
WasmEdge_Result res = WasmEdge_LoaderParseFromBuffer(
|
||||
loader, &astModule, wasm.data(), wasm.size());
|
||||
|
||||
if (!WasmEdge_ResultOK(res))
|
||||
{
|
||||
WasmEdge_LoaderDelete(loader);
|
||||
const char* msg = WasmEdge_ResultGetMessage(res);
|
||||
return std::string("Failed to parse WASM: ") +
|
||||
(msg ? msg : "unknown error");
|
||||
}
|
||||
|
||||
//
|
||||
// check export section
|
||||
//
|
||||
if (auto result = validateExportSection(astModule, j); !result)
|
||||
{
|
||||
WasmEdge_ASTModuleDelete(astModule);
|
||||
WasmEdge_LoaderDelete(loader);
|
||||
return result.error();
|
||||
}
|
||||
|
||||
//
|
||||
// check import section
|
||||
//
|
||||
if (auto result = validateImportSection(astModule, rules, j); !result)
|
||||
{
|
||||
WasmEdge_ASTModuleDelete(astModule);
|
||||
WasmEdge_LoaderDelete(loader);
|
||||
return result.error();
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
WasmEdge_ASTModuleDelete(astModule);
|
||||
WasmEdge_LoaderDelete(loader);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace hook
|
||||
@@ -1232,9 +1232,7 @@ hook::apply(
|
||||
bool isStrong,
|
||||
uint32_t wasmParam,
|
||||
uint8_t hookChainPosition,
|
||||
std::shared_ptr<STObject const> const& provisionalMeta,
|
||||
uint16_t hookApiVersion,
|
||||
uint32_t hookGas)
|
||||
std::shared_ptr<STObject const> const& provisionalMeta)
|
||||
{
|
||||
HookContext hookCtx = {
|
||||
.applyCtx = applyCtx,
|
||||
@@ -1266,9 +1264,7 @@ hook::apply(
|
||||
.wasmParam = wasmParam,
|
||||
.hookChainPosition = hookChainPosition,
|
||||
.foreignStateSetDisabled = false,
|
||||
.provisionalMeta = provisionalMeta,
|
||||
.hookApiVersion = hookApiVersion,
|
||||
.hookGas = hookGas},
|
||||
.provisionalMeta = provisionalMeta},
|
||||
.emitFailure = isCallback && wasmParam & 1
|
||||
? std::optional<ripple::STObject>(
|
||||
(*(applyCtx.view().peek(keylet::emittedTxn(
|
||||
@@ -2053,9 +2049,6 @@ hook::finalizeHookResult(
|
||||
ripple::Slice{
|
||||
hookResult.exitReason.data(), hookResult.exitReason.size()});
|
||||
meta.setFieldU64(sfHookInstructionCount, hookResult.instructionCount);
|
||||
if (hookResult.hookApiVersion == 1)
|
||||
meta.setFieldU32(sfHookInstructionCost, hookResult.instructionCost);
|
||||
|
||||
meta.setFieldU16(
|
||||
sfHookEmitCount,
|
||||
emission_txnid.size()); // this will never wrap, hard limit
|
||||
|
||||
@@ -638,7 +638,7 @@ Change::activateXahauGenesis()
|
||||
|
||||
std::optional<std::string> result2 =
|
||||
hook::HookExecutor::validateWasm(
|
||||
wasmBytes.data(), (size_t)wasmBytes.size(), 0);
|
||||
wasmBytes.data(), (size_t)wasmBytes.size());
|
||||
|
||||
if (result2)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <ripple/app/tx/impl/SetHook.h>
|
||||
|
||||
#include <ripple/app/hook/Enum.h>
|
||||
#include <ripple/app/hook/GasValidator.h>
|
||||
#include <ripple/app/hook/Guard.h>
|
||||
#include <ripple/app/hook/applyHook.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
@@ -436,8 +435,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
}
|
||||
|
||||
auto version = hookSetObj.getFieldU16(sfHookApiVersion);
|
||||
|
||||
if (!ctx.rules.enabled(featureHookGas) && version != 0)
|
||||
if (version != 0)
|
||||
{
|
||||
// we currently only accept api version 0
|
||||
JLOG(ctx.j.trace())
|
||||
@@ -447,17 +445,6 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
return false;
|
||||
}
|
||||
|
||||
// allow only version=0 and version=1
|
||||
if (version != 0 && version != 1)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << ::hook::log::API_INVALID << ")["
|
||||
<< HS_ACC()
|
||||
<< "]: Malformed transaction: SetHook "
|
||||
"sfHook->sfHookApiVersion invalid. (Must be 0 or 1).";
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate sfHookOn
|
||||
if (!hookSetObj.isFieldPresent(sfHookOn))
|
||||
{
|
||||
@@ -482,7 +469,6 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
return {};
|
||||
|
||||
Blob hook = hookSetObj.getFieldVL(sfCreateCode);
|
||||
auto version = hookSetObj.getFieldU16(sfHookApiVersion);
|
||||
|
||||
// RH NOTE: validateGuards has a generic non-rippled specific
|
||||
// interface so it can be used in other projects (i.e. tooling).
|
||||
@@ -500,83 +486,46 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
hsacc = ss.str();
|
||||
}
|
||||
|
||||
uint64_t maxInstrCountHook = 0;
|
||||
uint64_t maxInstrCountCbak = 0;
|
||||
auto result = validateGuards(
|
||||
hook, // wasm to verify
|
||||
logger,
|
||||
hsacc,
|
||||
(ctx.rules.enabled(featureHooksUpdate1) ? 1 : 0) +
|
||||
(ctx.rules.enabled(fix20250131) ? 2 : 0));
|
||||
|
||||
if (version == 0) // Guard type
|
||||
if (ctx.j.trace())
|
||||
{
|
||||
auto result = validateGuards(
|
||||
hook, // wasm to verify
|
||||
logger,
|
||||
hsacc,
|
||||
(ctx.rules.enabled(featureHooksUpdate1) ? 1 : 0) +
|
||||
(ctx.rules.enabled(fix20250131) ? 2 : 0));
|
||||
// clunky but to get the stream to accept the output
|
||||
// correctly we will split on new line and feed each line
|
||||
// one by one into the trace stream beast::Journal should be
|
||||
// updated to inherit from basic_ostream<char> then this
|
||||
// wouldn't be necessary.
|
||||
|
||||
if (ctx.j.trace())
|
||||
// is this a needless copy or does the compiler do copy
|
||||
// elision here?
|
||||
std::string s = loggerStream.str();
|
||||
|
||||
char* data = s.data();
|
||||
size_t len = s.size();
|
||||
|
||||
char* last = data;
|
||||
size_t i = 0;
|
||||
for (; i < len; ++i)
|
||||
{
|
||||
// clunky but to get the stream to accept the output
|
||||
// correctly we will split on new line and feed each
|
||||
// line one by one into the trace stream beast::Journal
|
||||
// should be updated to inherit from basic_ostream<char>
|
||||
// then this wouldn't be necessary.
|
||||
|
||||
// is this a needless copy or does the compiler do copy
|
||||
// elision here?
|
||||
std::string s = loggerStream.str();
|
||||
|
||||
char* data = s.data();
|
||||
size_t len = s.size();
|
||||
|
||||
char* last = data;
|
||||
size_t i = 0;
|
||||
for (; i < len; ++i)
|
||||
if (data[i] == '\n')
|
||||
{
|
||||
if (data[i] == '\n')
|
||||
{
|
||||
data[i] = '\0';
|
||||
ctx.j.trace() << last;
|
||||
last = data + i;
|
||||
}
|
||||
}
|
||||
|
||||
if (last < data + i)
|
||||
data[i] = '\0';
|
||||
ctx.j.trace() << last;
|
||||
last = data + i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::WASM_BAD_MAGIC << ")["
|
||||
<< HS_ACC()
|
||||
<< "]: Malformed transaction: SetHook "
|
||||
"sfCreateCode failed validation.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tie(maxInstrCountHook, maxInstrCountCbak) = *result;
|
||||
if (last < data + i)
|
||||
ctx.j.trace() << last;
|
||||
}
|
||||
else if (version == 1) // Gas type
|
||||
{
|
||||
// validate with GasValidator
|
||||
auto error = hook::validateWasmHostFunctionsForGas(
|
||||
hook, ctx.rules, ctx.j);
|
||||
|
||||
if (error)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::IMPORT_ILLEGAL << ")["
|
||||
<< HS_ACC()
|
||||
<< "]: Malformed transaction: Gas-type Hook "
|
||||
"validation failed: "
|
||||
<< *error;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gas type: maxInstrCount is not pre-calculated (use Gas
|
||||
// limit at runtime)
|
||||
maxInstrCountHook = 0;
|
||||
maxInstrCountCbak = 0;
|
||||
}
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::WASM_SMOKE_TEST << ")["
|
||||
@@ -586,7 +535,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
|
||||
std::optional<std::string> result2 =
|
||||
hook::HookExecutor::validateWasm(
|
||||
hook.data(), (size_t)hook.size(), version);
|
||||
hook.data(), (size_t)hook.size());
|
||||
|
||||
if (result2)
|
||||
{
|
||||
@@ -598,7 +547,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::make_pair(maxInstrCountHook, maxInstrCountCbak);
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1731,11 +1680,10 @@ SetHook::setHook()
|
||||
newHookDef->setFieldH256(
|
||||
sfHookSetTxnID, ctx.tx.getTransactionID());
|
||||
newHookDef->setFieldU64(sfReferenceCount, 1);
|
||||
if (hookSetObj->get().getFieldU16(sfHookApiVersion) != 1)
|
||||
newHookDef->setFieldAmount(
|
||||
sfFee,
|
||||
XRPAmount{
|
||||
hook::computeExecutionFee(maxInstrCountHook)});
|
||||
newHookDef->setFieldAmount(
|
||||
sfFee,
|
||||
XRPAmount{
|
||||
hook::computeExecutionFee(maxInstrCountHook)});
|
||||
if (maxInstrCountCbak > 0)
|
||||
newHookDef->setFieldAmount(
|
||||
sfHookCallbackFee,
|
||||
|
||||
@@ -100,11 +100,6 @@ preflight1(PreflightContext const& ctx)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfHookGas) && !ctx.rules.enabled(featureHookGas))
|
||||
{
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const ret = preflight0(ctx);
|
||||
if (!isTesSuccess(ret))
|
||||
return ret;
|
||||
@@ -224,7 +219,6 @@ Transactor::calculateHookChainFee(
|
||||
return XRPAmount{0};
|
||||
|
||||
XRPAmount fee{0};
|
||||
uint32_t gasTypeHookCount = 0; // Gas type hook counter
|
||||
|
||||
auto const& hooks = hookSLE->getFieldArray(sfHooks);
|
||||
|
||||
@@ -261,43 +255,18 @@ Transactor::calculateHookChainFee(
|
||||
if (hook::canHook(tx.getTxnType(), hookOn) &&
|
||||
(!collectCallsOnly || (flags & hook::hsfCOLLECT)))
|
||||
{
|
||||
// get HookApiVersion
|
||||
uint16_t apiVersion = hookDef->getFieldU16(sfHookApiVersion);
|
||||
XRPAmount const toAdd{hookDef->getFieldAmount(sfFee).xrp().drops()};
|
||||
|
||||
if (apiVersion == 0) // Guard type
|
||||
{
|
||||
// existing logic: read HookDefinition's sfFee
|
||||
XRPAmount const toAdd{
|
||||
hookDef->getFieldAmount(sfFee).xrp().drops()};
|
||||
|
||||
// this overflow should never happen, if somehow it does
|
||||
// fee is set to the largest possible valid xrp value to force
|
||||
// fail the transaction
|
||||
if (fee + toAdd < fee)
|
||||
fee = XRPAmount{INITIAL_XRP.drops()};
|
||||
else
|
||||
fee += toAdd;
|
||||
}
|
||||
else if (apiVersion == 1) // Gas type
|
||||
{
|
||||
// Gas type: only count
|
||||
gasTypeHookCount++;
|
||||
}
|
||||
// this overflow should never happen, if somehow it does
|
||||
// fee is set to the largest possible valid xrp value to force
|
||||
// fail the transaction
|
||||
if (fee + toAdd < fee)
|
||||
fee = XRPAmount{INITIAL_XRP.drops()};
|
||||
else
|
||||
fee += toAdd;
|
||||
}
|
||||
}
|
||||
|
||||
// Additional cost for Gas type: 0.2 XAH/Hook = 200,000 drops/Hook
|
||||
if (gasTypeHookCount > 0)
|
||||
{
|
||||
// TODO:
|
||||
auto const baseGasFee = 200000;
|
||||
XRPAmount const gasTypeFee{gasTypeHookCount * baseGasFee};
|
||||
if (fee + gasTypeFee < fee)
|
||||
fee = XRPAmount{INITIAL_XRP.drops()}; // overflow
|
||||
else
|
||||
fee += gasTypeFee;
|
||||
}
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
@@ -377,10 +346,6 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
if (canRollback)
|
||||
hookExecutionFee +=
|
||||
calculateHookChainFee(view, tx, keylet::hook(tshAcc));
|
||||
|
||||
if (view.rules().enabled(featureHookGas) &&
|
||||
tx.isFieldPresent(sfHookGas))
|
||||
hookExecutionFee += XRPAmount{tx.getFieldU32(sfHookGas)};
|
||||
}
|
||||
|
||||
XRPAmount accumulator = baseFee;
|
||||
@@ -1229,15 +1194,6 @@ Transactor::executeHookChain(
|
||||
std::map<uint256, std::map<std::vector<uint8_t>, std::vector<uint8_t>>>
|
||||
hookParamOverrides{};
|
||||
|
||||
// Initialize Gas pool for Gas-type hooks
|
||||
uint32_t gasPool = 0;
|
||||
if (ctx_.tx.isFieldPresent(sfHookGas))
|
||||
{
|
||||
gasPool = ctx_.tx.getFieldU32(sfHookGas);
|
||||
JLOG(j_.trace()) << "HookChain: Initialized Gas pool with " << gasPool
|
||||
<< " instructions";
|
||||
}
|
||||
|
||||
auto const& hooks = hookSLE->getFieldArray(sfHooks);
|
||||
uint8_t hook_no = 0;
|
||||
|
||||
@@ -1306,19 +1262,6 @@ Transactor::executeHookChain(
|
||||
|
||||
bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee);
|
||||
|
||||
// Extract HookApiVersion for Gas-type hooks
|
||||
uint16_t hookApiVersion = hookDef->isFieldPresent(sfHookApiVersion)
|
||||
? hookDef->getFieldU16(sfHookApiVersion)
|
||||
: 0;
|
||||
|
||||
// Prepare Gas limit for this hook execution
|
||||
uint32_t hookGas = 0;
|
||||
if (hookApiVersion == 1)
|
||||
{
|
||||
// Pass remaining Gas pool to this hook
|
||||
hookGas = gasPool;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
results.push_back(hook::apply(
|
||||
@@ -1337,35 +1280,12 @@ Transactor::executeHookChain(
|
||||
strong,
|
||||
(strong ? 0 : 1UL), // 0 = strong, 1 = weak
|
||||
hook_no - 1,
|
||||
provisionalMeta,
|
||||
hookApiVersion,
|
||||
hookGas));
|
||||
provisionalMeta));
|
||||
|
||||
executedHookCount_++;
|
||||
|
||||
hook::HookResult& hookResult = results.back();
|
||||
|
||||
// Track Gas consumption for Gas-type hooks
|
||||
if (hookApiVersion == 1)
|
||||
{
|
||||
uint64_t consumed = hookResult.instructionCost;
|
||||
|
||||
JLOG(j_.trace()) << "HookChain: Hook consumed " << consumed
|
||||
<< " instructions. Pool before: " << gasPool;
|
||||
|
||||
if (consumed >= gasPool)
|
||||
{
|
||||
JLOG(j_.trace()) << "HookError: Gas pool exhausted. "
|
||||
<< "Hook tried to consume " << consumed
|
||||
<< " but only " << gasPool << " remained.";
|
||||
return tecHOOK_INSUFFICIENT_GAS;
|
||||
}
|
||||
|
||||
gasPool -= consumed;
|
||||
|
||||
JLOG(j_.trace()) << "HookChain: Pool after: " << gasPool;
|
||||
}
|
||||
|
||||
if (hookResult.exitType != hook_api::ExitType::ACCEPT)
|
||||
{
|
||||
if (results.back().exitType == hook_api::ExitType::WASM_ERROR)
|
||||
@@ -1502,17 +1422,6 @@ Transactor::doHookCallback(
|
||||
{
|
||||
hook::HookStateMap stateMap;
|
||||
|
||||
// Extract HookApiVersion for callback
|
||||
uint16_t hookApiVersion = hookDef->getFieldU16(sfHookApiVersion);
|
||||
|
||||
// Callbacks don't consume HookGas independently, but we pass it
|
||||
// for consistency
|
||||
uint32_t hookGas = 0;
|
||||
if (ctx_.tx.isFieldPresent(sfHookGas))
|
||||
{
|
||||
hookGas = ctx_.tx.getFieldU32(sfHookGas);
|
||||
}
|
||||
|
||||
hook::HookResult callbackResult = hook::apply(
|
||||
hookDef->getFieldH256(sfHookSetTxnID),
|
||||
callbackHookHash,
|
||||
@@ -1532,9 +1441,7 @@ Transactor::doHookCallback(
|
||||
? 1UL
|
||||
: 0UL,
|
||||
hook_no - 1,
|
||||
provisionalMeta,
|
||||
hookApiVersion,
|
||||
hookGas);
|
||||
provisionalMeta);
|
||||
|
||||
executedHookCount_++;
|
||||
|
||||
@@ -1810,14 +1717,6 @@ Transactor::doAgainAsWeak(
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract HookApiVersion for aaw execution
|
||||
uint16_t hookApiVersion = hookDef->getFieldU16(sfHookApiVersion);
|
||||
|
||||
// Extract HookGas for Gas-type hooks
|
||||
uint32_t hookGas = 0;
|
||||
if (hookApiVersion == 1 && ctx_.tx.isFieldPresent(sfHookGas))
|
||||
hookGas = ctx_.tx.getFieldU32(sfHookGas);
|
||||
|
||||
try
|
||||
{
|
||||
hook::HookResult aawResult = hook::apply(
|
||||
@@ -1836,9 +1735,7 @@ Transactor::doAgainAsWeak(
|
||||
false,
|
||||
2UL, // param 2 = aaw
|
||||
hook_no - 1,
|
||||
provisionalMeta,
|
||||
hookApiVersion,
|
||||
hookGas);
|
||||
provisionalMeta);
|
||||
|
||||
executedHookCount_++;
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace detail {
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||
// the actual number of amendments. A LogicError on startup will verify this.
|
||||
static constexpr std::size_t numFeatures = 91;
|
||||
static constexpr std::size_t numFeatures = 90;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -378,7 +378,6 @@ extern uint256 const fixInvalidTxFlags;
|
||||
extern uint256 const featureExtendedHookState;
|
||||
extern uint256 const fixCronStacking;
|
||||
extern uint256 const fixHookAPI20251128;
|
||||
extern uint256 const featureHookGas;
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -414,8 +414,6 @@ extern SF_UINT32 const sfXahauActivationLgrSeq;
|
||||
extern SF_UINT32 const sfDelaySeconds;
|
||||
extern SF_UINT32 const sfRepeatCount;
|
||||
extern SF_UINT32 const sfStartTime;
|
||||
extern SF_UINT32 const sfHookGas;
|
||||
extern SF_UINT32 const sfHookInstructionCost;
|
||||
|
||||
// 64-bit integers (common)
|
||||
extern SF_UINT64 const sfIndexNext;
|
||||
|
||||
@@ -344,7 +344,6 @@ enum TECcodes : TERUnderlyingType {
|
||||
tecIMMUTABLE = 188,
|
||||
tecTOO_MANY_REMARKS = 189,
|
||||
tecHAS_HOOK_STATE = 190,
|
||||
tecHOOK_INSUFFICIENT_GAS = 191,
|
||||
tecLAST_POSSIBLE_ENTRY = 255,
|
||||
};
|
||||
|
||||
|
||||
@@ -484,7 +484,6 @@ REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FIX (fixCronStacking, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FIX (fixHookAPI20251128, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(HookGas, Supported::yes, VoteBehavior::DefaultYes);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -73,8 +73,7 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{sfHookExecutionIndex, soeREQUIRED},
|
||||
{sfHookStateChangeCount, soeREQUIRED},
|
||||
{sfHookEmitCount, soeREQUIRED},
|
||||
{sfFlags, soeOPTIONAL},
|
||||
{sfHookInstructionCost, soeOPTIONAL}});
|
||||
{sfFlags, soeOPTIONAL}});
|
||||
|
||||
add(sfHookEmission.jsonName.c_str(),
|
||||
sfHookEmission.getCode(),
|
||||
@@ -92,7 +91,7 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{sfHookCanEmit, soeOPTIONAL},
|
||||
{sfHookApiVersion, soeREQUIRED},
|
||||
{sfFlags, soeREQUIRED},
|
||||
{sfFee, soeOPTIONAL}});
|
||||
{sfFee, soeREQUIRED}});
|
||||
|
||||
add(sfHook.jsonName.c_str(),
|
||||
sfHook.getCode(),
|
||||
|
||||
@@ -158,9 +158,6 @@ CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32,
|
||||
|
||||
CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50);
|
||||
|
||||
// 32-bit integers (hook)
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookInstructionCost, "HookInstructionCost", UINT32, 91);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookGas, "HookGas", UINT32, 92);
|
||||
CONSTRUCT_TYPED_SFIELD(sfStartTime, "StartTime", UINT32, 93);
|
||||
CONSTRUCT_TYPED_SFIELD(sfRepeatCount, "RepeatCount", UINT32, 94);
|
||||
CONSTRUCT_TYPED_SFIELD(sfDelaySeconds, "DelaySeconds", UINT32, 95);
|
||||
|
||||
@@ -95,7 +95,6 @@ transResults()
|
||||
MAKE_ERROR(tecIMMUTABLE, "The remark is marked immutable on the object, and therefore cannot be updated."),
|
||||
MAKE_ERROR(tecTOO_MANY_REMARKS, "The number of remarks on the object would exceed the limit of 32."),
|
||||
MAKE_ERROR(tecHAS_HOOK_STATE, "Delete all hook state before reducing scale"),
|
||||
MAKE_ERROR(tecHOOK_INSUFFICIENT_GAS, "Insufficient hook gas to complete the transaction."),
|
||||
|
||||
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
||||
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||
|
||||
@@ -44,7 +44,6 @@ TxFormats::TxFormats()
|
||||
{sfNetworkID, soeOPTIONAL},
|
||||
{sfHookParameters, soeOPTIONAL},
|
||||
{sfOperationLimit, soeOPTIONAL},
|
||||
{sfHookGas, soeOPTIONAL},
|
||||
};
|
||||
|
||||
add(jss::AccountSet,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -41,28 +41,17 @@ echo '
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
std::map<std::string, std::vector<uint8_t>> wasm = {' > $OUTPUT_FILE
|
||||
# Counter file for sharing between subshells
|
||||
COUNTER_FILE=$(mktemp)
|
||||
echo "0" > $COUNTER_FILE
|
||||
trap "rm -f $COUNTER_FILE" EXIT
|
||||
|
||||
# Process both [test.hook] and [test.hook.gas] blocks
|
||||
process_block() {
|
||||
local tag_pattern="$1" # regex pattern: "hook" or "hook\.gas"
|
||||
local tag_output="$2" # output string: "hook" or "hook.gas"
|
||||
local skip_cleaner="$3" # "0" for no skip, "1" for skip
|
||||
|
||||
cat $INPUT_FILE | tr '\n' '\f' |
|
||||
grep -Po "R\"\[test\.${tag_pattern}\](.*?)\[test\.${tag_pattern}\]\"" |
|
||||
sed -E "s/R\"\[test\.${tag_pattern}\]\(//g" |
|
||||
sed -E "s/\)\[test\.${tag_pattern}\]\"[\f \t]*/\/*end*\//g" |
|
||||
COUNTER="0"
|
||||
cat $INPUT_FILE | tr '\n' '\f' |
|
||||
grep -Po 'R"\[test\.hook\](.*?)\[test\.hook\]"' |
|
||||
sed -E 's/R"\[test\.hook\]\(//g' |
|
||||
sed -E 's/\)\[test\.hook\]"[\f \t]*/\/*end*\//g' |
|
||||
while read -r line
|
||||
do
|
||||
COUNTER=$(cat $COUNTER_FILE)
|
||||
echo "/* ==== WASM: $COUNTER ==== */" >> $OUTPUT_FILE
|
||||
echo -n "{ R\"[test.${tag_output}](" >> $OUTPUT_FILE
|
||||
echo -n '{ R"[test.hook](' >> $OUTPUT_FILE
|
||||
cat <<< "$line" | sed -E 's/.{7}$//g' | tr -d '\n' | tr '\f' '\n' >> $OUTPUT_FILE
|
||||
echo ")[test.${tag_output}]\"," >> $OUTPUT_FILE
|
||||
echo ')[test.hook]",' >> $OUTPUT_FILE
|
||||
echo "{" >> $OUTPUT_FILE
|
||||
WAT=`grep -Eo '\(module' <<< $line | wc -l`
|
||||
if [ "$WAT" -eq "0" ]
|
||||
@@ -80,19 +69,10 @@ process_block() {
|
||||
echo "$line"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$skip_cleaner" -eq "1" ]
|
||||
then
|
||||
# Skip hook-cleaner for [test.hook.gas]
|
||||
wasmcc -x c /dev/stdin -o /dev/stdout -O2 -Wl,--allow-undefined -Wno-int-conversion -Wno-pointer-sign -Wno-return-type <<< "`tr '\f' '\n' <<< $line`" |
|
||||
xxd -p -u -c 10 |
|
||||
sed -E 's/../0x&U,/g' | sed -E 's/^/ /g' >> $OUTPUT_FILE
|
||||
else
|
||||
# Run hook-cleaner for [test.hook]
|
||||
wasmcc -x c /dev/stdin -o /dev/stdout -O2 -Wl,--allow-undefined -Wno-int-conversion -Wno-pointer-sign -Wno-return-type <<< "`tr '\f' '\n' <<< $line`" |
|
||||
hook-cleaner - - 2>/dev/null |
|
||||
xxd -p -u -c 10 |
|
||||
sed -E 's/../0x&U,/g' | sed -E 's/^/ /g' >> $OUTPUT_FILE
|
||||
fi
|
||||
wasmcc -x c /dev/stdin -o /dev/stdout -O2 -Wl,--allow-undefined <<< "`tr '\f' '\n' <<< $line`" |
|
||||
hook-cleaner - - 2>/dev/null |
|
||||
xxd -p -u -c 10 |
|
||||
sed -E 's/../0x&U,/g' | sed -E 's/^/ /g' >> $OUTPUT_FILE
|
||||
else
|
||||
wat2wasm - -o /dev/stdout <<< "`tr '\f' '\n' <<< $(sed -E 's/.{7}$//g' <<< $line)`" |
|
||||
xxd -p -u -c 10 |
|
||||
@@ -105,15 +85,8 @@ process_block() {
|
||||
fi
|
||||
echo '}},' >> $OUTPUT_FILE
|
||||
echo >> $OUTPUT_FILE
|
||||
echo $((COUNTER + 1)) > $COUNTER_FILE
|
||||
COUNTER=`echo $COUNTER + 1 | bc`
|
||||
done
|
||||
}
|
||||
|
||||
# Process [test.hook] blocks (with hook-cleaner)
|
||||
process_block "hook" "hook" "0"
|
||||
|
||||
# Process [test.hook.gas] blocks (without hook-cleaner)
|
||||
process_block "hook\.gas" "hook.gas" "1"
|
||||
echo '};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2025 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_TEST_JTX_HOOKGAS_H_INCLUDED
|
||||
#define RIPPLE_TEST_JTX_HOOKGAS_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/tags.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
/** Set the HookGas on a JTx. */
|
||||
class hookgas
|
||||
{
|
||||
private:
|
||||
std::uint32_t gas_;
|
||||
|
||||
public:
|
||||
hookgas(std::uint32_t gas) : gas_{gas}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env&, JTx& jt) const;
|
||||
};
|
||||
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2025 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx/hookgas.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
void
|
||||
hookgas::operator()(Env&, JTx& jt) const
|
||||
{
|
||||
jt[sfHookGas.jsonName] = gas_;
|
||||
}
|
||||
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
Reference in New Issue
Block a user