PoC Smart Escrows (#5340)

* wasmedge in unittest

* add WashVM.h and cpp

* accountID comparison (vector<u8>) working

* json decode tx and ledger object with two buffers working

* wasm return a buffer working

* add a failure test case to P2P3

* host function return ledger sqn

* instruction gas and host function gas

* basics

* add scaffold

* add amendment check

* working PoC

* get test working

* fix clang-format

* prototype #2

* p2p3

* [WIP] P4

* P5

* add calculateBaseFee

* add FinishFunction preflight checks (+ tests)

* additional reserve for sfFinishFunction

* higher fees for EscrowFinish

* rename amendment to SmartEscrow

* make fee voting changes, add basic tests

* clean up

* clean up

* clean up

* more cleanup

* add subscribe tests

* add more tests

* undo formatting

* undo formatting

* remove bad comment

* more debugging statements

* fix clang-format

* fix rebase issues

* fix more rebase issues

* more rebase fixes

* add source code for wasm

* respond to comments

* add const

---------

Co-authored-by: Peng Wang <pwang200@gmail.com>
This commit is contained in:
Mayukha Vadari
2025-03-20 14:08:06 -04:00
committed by GitHub
parent 5bf3a308d5
commit b6a95f9970
33 changed files with 6339 additions and 40 deletions

View File

@@ -0,0 +1,592 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
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 <xrpld/app/misc/WasmVM.h>
// WasmVM::WasmVM(beast::Journal j)
// : j_(j)
//{
// }
namespace ripple {
Expected<bool, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
int32_t input)
{
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL);
WasmEdge_Value Params[1] = {WasmEdge_ValueGenI32(input)};
WasmEdge_Value Returns[1];
WasmEdge_String FuncName = WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Result Res = WasmEdge_VMRunWasmFromBuffer(
VMCxt,
wasmCode.data(),
wasmCode.size(),
FuncName,
Params,
1,
Returns,
1);
bool ok = WasmEdge_ResultOK(Res);
bool re = false;
if (ok)
{
auto result = WasmEdge_ValueGetI32(Returns[0]);
// printf("Get the result: %d\n", result);
if (result != 0)
re = true;
}
else
{
printf("Error message: %s\n", WasmEdge_ResultGetMessage(Res));
}
WasmEdge_VMDelete(VMCxt);
WasmEdge_StringDelete(FuncName);
if (ok)
return re;
else
return Unexpected<TER>(tecFAILED_PROCESSING);
}
Expected<bool, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
std::vector<uint8_t> const& accountID)
{
auto dataLen = (int32_t)accountID.size();
// printf("accountID size: %d\n", dataLen);
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL);
WasmEdge_Value allocParams[1] = {WasmEdge_ValueGenI32(dataLen)};
WasmEdge_Value allocReturns[1];
WasmEdge_String allocFunc = WasmEdge_StringCreateByCString("allocate");
WasmEdge_Result allocRes = WasmEdge_VMRunWasmFromBuffer(
VMCxt,
wasmCode.data(),
wasmCode.size(),
allocFunc,
allocParams,
1,
allocReturns,
1);
bool ok = WasmEdge_ResultOK(allocRes);
bool re = false;
if (ok)
{
auto pointer = WasmEdge_ValueGetI32(allocReturns[0]);
// printf("Alloc pointer: %d\n", pointer);
const WasmEdge_ModuleInstanceContext* m =
WasmEdge_VMGetActiveModule(VMCxt);
WasmEdge_String mName = WasmEdge_StringCreateByCString("memory");
WasmEdge_MemoryInstanceContext* mi =
WasmEdge_ModuleInstanceFindMemory(m, mName);
WasmEdge_Result setRes = WasmEdge_MemoryInstanceSetData(
mi, accountID.data(), pointer, dataLen);
ok = WasmEdge_ResultOK(setRes);
if (ok)
{
// printf("Set data ok\n");
WasmEdge_Value params[2] = {
WasmEdge_ValueGenI32(pointer), WasmEdge_ValueGenI32(dataLen)};
WasmEdge_Value returns[1];
WasmEdge_String func =
WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Result funcRes =
WasmEdge_VMExecute(VMCxt, func, params, 2, returns, 1);
ok = WasmEdge_ResultOK(funcRes);
if (ok)
{
// printf("func ok\n");
re = (WasmEdge_ValueGetI32(returns[0]) == 1);
}
else
{
printf(
"Func message: %s\n", WasmEdge_ResultGetMessage(funcRes));
}
}
else
{
printf(
"Set error message: %s\n", WasmEdge_ResultGetMessage(setRes));
}
}
else
{
printf(
"Alloc error message: %s\n", WasmEdge_ResultGetMessage(allocRes));
}
WasmEdge_VMDelete(VMCxt);
// TODO free everything
// WasmEdge_StringDelete(FuncName);
if (ok)
{
// printf("runEscrowWasm ok, result %d\n", re);
return re;
}
else
return Unexpected<TER>(tecFAILED_PROCESSING);
}
Expected<bool, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
std::vector<uint8_t> const& escrow_tx_json_data,
std::vector<uint8_t> const& escrow_lo_json_data)
{
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL);
WasmEdge_Result loadRes =
WasmEdge_VMLoadWasmFromBuffer(VMCxt, wasmCode.data(), wasmCode.size());
if (!WasmEdge_ResultOK(loadRes))
{
printf("load error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt);
if (!WasmEdge_ResultOK(validateRes))
{
printf("validate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt);
if (!WasmEdge_ResultOK(instantiateRes))
{
printf("instantiate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
auto wasmAlloc = [VMCxt](std::vector<uint8_t> const& data) -> int32_t {
auto dataLen = (int32_t)data.size();
WasmEdge_Value allocParams[1] = {WasmEdge_ValueGenI32(dataLen)};
WasmEdge_Value allocReturns[1];
WasmEdge_String allocFunc = WasmEdge_StringCreateByCString("allocate");
WasmEdge_Result allocRes = WasmEdge_VMExecute(
VMCxt, allocFunc, allocParams, 1, allocReturns, 1);
if (WasmEdge_ResultOK(allocRes))
{
auto pointer = WasmEdge_ValueGetI32(allocReturns[0]);
// printf("alloc ptr %d, len %d\n", pointer, dataLen);
const WasmEdge_ModuleInstanceContext* m =
WasmEdge_VMGetActiveModule(VMCxt);
WasmEdge_String mName = WasmEdge_StringCreateByCString("memory");
WasmEdge_MemoryInstanceContext* mi =
WasmEdge_ModuleInstanceFindMemory(m, mName);
WasmEdge_Result setRes = WasmEdge_MemoryInstanceSetData(
mi, data.data(), pointer, dataLen);
if (WasmEdge_ResultOK(setRes))
{
return pointer;
}
}
return 0;
};
auto tx_ptr = wasmAlloc(escrow_tx_json_data);
auto lo_ptr = wasmAlloc(escrow_lo_json_data);
if (tx_ptr == 0 || lo_ptr == 0)
{
printf("data error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
auto txLen = (int32_t)escrow_tx_json_data.size();
auto loLen = (int32_t)escrow_lo_json_data.size();
WasmEdge_Value params[4] = {
WasmEdge_ValueGenI32(tx_ptr),
WasmEdge_ValueGenI32(txLen),
WasmEdge_ValueGenI32(lo_ptr),
WasmEdge_ValueGenI32(loLen)};
WasmEdge_Value returns[1];
WasmEdge_String func = WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Result funcRes =
WasmEdge_VMExecute(VMCxt, func, params, 4, returns, 1);
if (WasmEdge_ResultOK(funcRes))
{
// printf("func ok\n");
return WasmEdge_ValueGetI32(returns[0]) == 1;
}
else
{
printf("Func message: %s\n", WasmEdge_ResultGetMessage(funcRes));
return Unexpected<TER>(tecFAILED_PROCESSING);
}
}
Expected<std::pair<bool, std::string>, TER>
runEscrowWasmP4(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
std::vector<uint8_t> const& escrow_tx_json_data,
std::vector<uint8_t> const& escrow_lo_json_data)
{
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL);
WasmEdge_Result loadRes =
WasmEdge_VMLoadWasmFromBuffer(VMCxt, wasmCode.data(), wasmCode.size());
if (!WasmEdge_ResultOK(loadRes))
{
printf("load error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt);
if (!WasmEdge_ResultOK(validateRes))
{
printf("validate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt);
if (!WasmEdge_ResultOK(instantiateRes))
{
printf("instantiate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
auto wasmAlloc = [VMCxt](std::vector<uint8_t> const& data) -> int32_t {
auto dataLen = (int32_t)data.size();
WasmEdge_Value allocParams[1] = {WasmEdge_ValueGenI32(dataLen)};
WasmEdge_Value allocReturns[1];
WasmEdge_String allocFunc = WasmEdge_StringCreateByCString("allocate");
WasmEdge_Result allocRes = WasmEdge_VMExecute(
VMCxt, allocFunc, allocParams, 1, allocReturns, 1);
if (WasmEdge_ResultOK(allocRes))
{
auto pointer = WasmEdge_ValueGetI32(allocReturns[0]);
// printf("alloc ptr %d, len %d\n", pointer, dataLen);
const WasmEdge_ModuleInstanceContext* m =
WasmEdge_VMGetActiveModule(VMCxt);
WasmEdge_String mName = WasmEdge_StringCreateByCString("memory");
WasmEdge_MemoryInstanceContext* mi =
WasmEdge_ModuleInstanceFindMemory(m, mName);
WasmEdge_Result setRes = WasmEdge_MemoryInstanceSetData(
mi, data.data(), pointer, dataLen);
if (WasmEdge_ResultOK(setRes))
{
return pointer;
}
}
return 0;
};
auto tx_ptr = wasmAlloc(escrow_tx_json_data);
auto lo_ptr = wasmAlloc(escrow_lo_json_data);
if (tx_ptr == 0 || lo_ptr == 0)
{
printf("data error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
auto txLen = (int32_t)escrow_tx_json_data.size();
auto loLen = (int32_t)escrow_lo_json_data.size();
WasmEdge_Value params[4] = {
WasmEdge_ValueGenI32(tx_ptr),
WasmEdge_ValueGenI32(txLen),
WasmEdge_ValueGenI32(lo_ptr),
WasmEdge_ValueGenI32(loLen)};
WasmEdge_Value returns[1];
WasmEdge_String func = WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Result funcRes =
WasmEdge_VMExecute(VMCxt, func, params, 4, returns, 1);
if (WasmEdge_ResultOK(funcRes))
{
auto pointer = WasmEdge_ValueGetI32(returns[0]);
const WasmEdge_ModuleInstanceContext* m =
WasmEdge_VMGetActiveModule(VMCxt);
WasmEdge_String mName = WasmEdge_StringCreateByCString("memory");
WasmEdge_MemoryInstanceContext* mi =
WasmEdge_ModuleInstanceFindMemory(m, mName);
uint8_t buff[9];
WasmEdge_Result getRes =
WasmEdge_MemoryInstanceGetData(mi, buff, pointer, 9);
if (!WasmEdge_ResultOK(getRes))
{
printf(
"re mem get message: %s\n", WasmEdge_ResultGetMessage(getRes));
return Unexpected<TER>(tecFAILED_PROCESSING);
}
auto flag = buff[0];
auto leToInt32 = [](const uint8_t* d) -> uint32_t {
uint32_t r = 0;
for (int i = 0; i < 4; ++i)
{
r |= static_cast<uint32_t>(d[i]) << (i * 8);
// printf("leToInt32 %d\n", r);
}
return r;
};
auto ret_pointer =
leToInt32(reinterpret_cast<const uint8_t*>(&buff[1]));
auto ret_len = leToInt32(reinterpret_cast<const uint8_t*>(&buff[5]));
// printf("re flag %d, ptr %d, len %d\n", flag, ret_pointer,
// ret_len);
std::vector<uint8_t> buff2(ret_len);
getRes = WasmEdge_MemoryInstanceGetData(
mi, buff2.data(), ret_pointer, ret_len);
if (!WasmEdge_ResultOK(getRes))
{
printf(
"re 2 mem get message: %s\n",
WasmEdge_ResultGetMessage(getRes));
return Unexpected<TER>(tecFAILED_PROCESSING);
}
std::string newData(buff2.begin(), buff2.end());
// free
WasmEdge_String freeFunc = WasmEdge_StringCreateByCString("deallocate");
WasmEdge_Value freeParams[2] = {
WasmEdge_ValueGenI32(ret_pointer), WasmEdge_ValueGenI32(ret_len)};
WasmEdge_Value freeReturns[0];
WasmEdge_VMExecute(VMCxt, freeFunc, freeParams, 2, freeReturns, 0);
// free pointer too, with len = 9 too
freeParams[0] = WasmEdge_ValueGenI32(pointer);
freeParams[1] = WasmEdge_ValueGenI32(9);
WasmEdge_VMExecute(VMCxt, freeFunc, freeParams, 2, freeReturns, 0);
return std::pair<bool, std::string>(flag == 1, newData);
}
else
{
printf("Func message: %s\n", WasmEdge_ResultGetMessage(funcRes));
return Unexpected<TER>(tecFAILED_PROCESSING);
}
}
WasmEdge_Result
get_ledger_sqn(
void* data,
const WasmEdge_CallingFrameContext*,
const WasmEdge_Value* In,
WasmEdge_Value* Out)
{
Out[0] =
WasmEdge_ValueGenI32(((LedgerDataProvider*)data)->get_ledger_sqn());
return WasmEdge_Result_Success;
}
Expected<bool, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
LedgerDataProvider* ledgerDataProvider)
{
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL);
{ // register host function
WasmEdge_ValType ReturnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* HostFType =
WasmEdge_FunctionTypeCreate(NULL, 0, ReturnList, 1);
WasmEdge_FunctionInstanceContext* HostFunc =
WasmEdge_FunctionInstanceCreate(
HostFType, get_ledger_sqn, ledgerDataProvider, 0);
WasmEdge_FunctionTypeDelete(HostFType);
WasmEdge_String HostName = WasmEdge_StringCreateByCString("host_lib");
WasmEdge_ModuleInstanceContext* HostMod =
WasmEdge_ModuleInstanceCreate(HostName);
WasmEdge_StringDelete(HostName);
WasmEdge_String HostFuncName =
WasmEdge_StringCreateByCString("get_ledger_sqn");
WasmEdge_ModuleInstanceAddFunction(HostMod, HostFuncName, HostFunc);
WasmEdge_StringDelete(HostFuncName);
WasmEdge_Result regRe =
WasmEdge_VMRegisterModuleFromImport(VMCxt, HostMod);
if (!WasmEdge_ResultOK(regRe))
{
printf("host func reg error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
}
WasmEdge_Result loadRes =
WasmEdge_VMLoadWasmFromBuffer(VMCxt, wasmCode.data(), wasmCode.size());
if (!WasmEdge_ResultOK(loadRes))
{
printf("load error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt);
if (!WasmEdge_ResultOK(validateRes))
{
printf("validate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt);
if (!WasmEdge_ResultOK(instantiateRes))
{
printf("instantiate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Value funcReturns[1];
WasmEdge_String func = WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Result funcRes =
WasmEdge_VMExecute(VMCxt, func, NULL, 0, funcReturns, 1);
bool ok = WasmEdge_ResultOK(funcRes);
bool re = false;
if (ok)
{
auto result = WasmEdge_ValueGetI32(funcReturns[0]);
if (result != 0)
re = true;
}
else
{
printf("Error message: %s\n", WasmEdge_ResultGetMessage(funcRes));
}
WasmEdge_VMDelete(VMCxt);
WasmEdge_StringDelete(func);
if (ok)
return re;
else
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result
constInt(
void* data,
const WasmEdge_CallingFrameContext*,
const WasmEdge_Value* In,
WasmEdge_Value* Out)
{
Out[0] = WasmEdge_ValueGenI32(5);
return WasmEdge_Result_Success;
}
Expected<EscrowResultP6, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
int32_t gasLimit,
int32_t input)
{
WasmEdge_ConfigureContext* conf = WasmEdge_ConfigureCreate();
WasmEdge_ConfigureStatisticsSetInstructionCounting(conf, true);
WasmEdge_ConfigureStatisticsSetCostMeasuring(conf, true);
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(conf, NULL);
WasmEdge_StatisticsContext* StatCxt =
WasmEdge_VMGetStatisticsContext(VMCxt);
WasmEdge_StatisticsSetCostLimit(StatCxt, gasLimit);
{ // register host function
WasmEdge_ValType ReturnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* HostFType =
WasmEdge_FunctionTypeCreate(NULL, 0, ReturnList, 1);
WasmEdge_FunctionInstanceContext* HostFunc =
WasmEdge_FunctionInstanceCreate(HostFType, constInt, nullptr, 100);
WasmEdge_FunctionTypeDelete(HostFType);
WasmEdge_String HostName = WasmEdge_StringCreateByCString("host_lib");
WasmEdge_ModuleInstanceContext* HostMod =
WasmEdge_ModuleInstanceCreate(HostName);
WasmEdge_StringDelete(HostName);
WasmEdge_String HostFuncName =
WasmEdge_StringCreateByCString("constInt");
WasmEdge_ModuleInstanceAddFunction(HostMod, HostFuncName, HostFunc);
WasmEdge_StringDelete(HostFuncName);
WasmEdge_Result regRe =
WasmEdge_VMRegisterModuleFromImport(VMCxt, HostMod);
if (!WasmEdge_ResultOK(regRe))
{
printf("host func reg error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
}
WasmEdge_Result loadRes =
WasmEdge_VMLoadWasmFromBuffer(VMCxt, wasmCode.data(), wasmCode.size());
if (!WasmEdge_ResultOK(loadRes))
{
printf("load error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt);
if (!WasmEdge_ResultOK(validateRes))
{
printf("validate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt);
if (!WasmEdge_ResultOK(instantiateRes))
{
printf("instantiate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_String func = WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Value Params[1] = {WasmEdge_ValueGenI32(input)};
WasmEdge_Result funcRes =
WasmEdge_VMExecute(VMCxt, func, Params, 1, NULL, 0);
bool ok = WasmEdge_ResultOK(funcRes);
EscrowResultP6 re;
if (ok)
{
auto sc = WasmEdge_VMGetStatisticsContext(VMCxt);
re.cost = WasmEdge_StatisticsGetTotalCost(sc);
// WasmEdge_StatisticsGetTotalCost, WasmEdge_StatisticsGetInstrCount
re.result = true;
}
else
{
printf("Error message: %s\n", WasmEdge_ResultGetMessage(funcRes));
}
WasmEdge_VMDelete(VMCxt);
WasmEdge_StringDelete(func);
// delete other obj allocated
if (ok)
return re;
else
return Unexpected<TER>(tecFAILED_PROCESSING);
}
} // namespace ripple