diff --git a/src/eei.cpp b/src/eei.cpp index a846a0c08..3ebf5e827 100644 --- a/src/eei.cpp +++ b/src/eei.cpp @@ -845,4 +845,35 @@ void WasmEngine::collectBenchmarkingData() } return ret; } + + intx::uint256 EthereumInterface::loadBignum256(uint32_t srcOffset) + { + uint8_t data[32]; + loadMemory(srcOffset, data, 32); + // FIXME: change this to little endian? + return intx::be::uint256(data); + } + + void EthereumInterface::storeBignum256(intx::uint256 const& src, uint32_t dstOffset) + { + uint8_t data[32]; + // FIXME: change this to little endian? + intx::be::store(data, src); + storeMemory(data, dstOffset, 32); + } + + void EthereumInterface::mul256(uint32_t aOffset, uint32_t bOffset, uint32_t retOffset) + { + storeBignum256(loadBignum256(aOffset) * loadBignum256(bOffset), retOffset); + } + + void EthereumInterface::umulmod256(uint32_t aOffset, uint32_t bOffset, uint32_t modOffset, uint32_t retOffset) + { + using intx::uint512; + auto a = loadBignum256(aOffset); + auto b = loadBignum256(bOffset); + auto mod = loadBignum256(modOffset); + auto ret = mod != 0 ? ((uint512{a} * uint512{b}) % uint512{mod}).lo : 0; + storeBignum256(ret, retOffset); + } } diff --git a/src/eei.h b/src/eei.h index a3fa59b84..1988d6f44 100644 --- a/src/eei.h +++ b/src/eei.h @@ -23,6 +23,8 @@ #include #include +#include + #include "exceptions.h" #include "helpers.h" @@ -163,6 +165,10 @@ class EthereumInterface { uint32_t eeiCreate(uint32_t valueOffset, uint32_t dataOffset, uint32_t length, uint32_t resultOffset); void eeiSelfDestruct(uint32_t addressOffset); + // Bignum system library + void mul256(uint32_t aOffset, uint32_t bOffset, uint32_t retOffset); + void umulmod256(uint32_t aOffset, uint32_t bOffset, uint32_t modOffset, uint32_t retOffset); + private: void eeiRevertOrFinish(bool revert, uint32_t offset, uint32_t size); @@ -191,6 +197,9 @@ class EthereumInterface { evmc_uint256be loadUint128(uint32_t srcOffset); void storeUint128(evmc_uint256be const& src, uint32_t dstOffset); + intx::uint256 loadBignum256(uint32_t srcOffset); + void storeBignum256(intx::uint256 const& src, uint32_t dstOffset); + inline int64_t maxCallGas(int64_t gas) { return gas - (gas / 64); } /* Checks for overflow and safely charges gas for variable length data copies */ diff --git a/src/wabt.cpp b/src/wabt.cpp index 6f4419837..34ce964e5 100644 --- a/src/wabt.cpp +++ b/src/wabt.cpp @@ -584,6 +584,39 @@ ExecutionResult WabtEngine::execute( } ); + // Create the bignum host module + // The lifecycle of this pointer is handled by `env`. + hostModule = env.AppendHostModule("bignum"); + heraAssert(hostModule, "Failed to create host module."); + + hostModule->AppendFuncExport( + "mul256", + {{Type::I32, Type::I32, Type::I32}, {}}, + [&interface]( + const interp::HostFunc*, + const interp::FuncSignature*, + const interp::TypedValues& args, + interp::TypedValues& + ) { + interface.mul256(args[0].value.i32, args[1].value.i32, args[2].value.i32); + return interp::Result::Ok; + } + ); + + hostModule->AppendFuncExport( + "umulmod256", + {{Type::I32, Type::I32, Type::I32, Type::I32}, {}}, + [&interface]( + const interp::HostFunc*, + const interp::FuncSignature*, + const interp::TypedValues& args, + interp::TypedValues& + ) { + interface.umulmod256(args[0].value.i32, args[1].value.i32, args[2].value.i32, args[3].value.i32); + return interp::Result::Ok; + } + ); + #if HERA_DEBUGGING // Create debug host module // The lifecycle of this pointer is handled by `env`. @@ -1112,6 +1145,37 @@ void WabtEngine::verifyContract(bytes_view code) { } ); + // Create the bignum host module + // The lifecycle of this pointer is handled by `env`. + hostModule = env.AppendHostModule("bignum"); + heraAssert(hostModule, "Failed to create host module."); + + hostModule->AppendFuncExport( + "mul256", + {{Type::I32, Type::I32, Type::I32}, {}}, + [&]( + const interp::HostFunc*, + const interp::FuncSignature*, + const interp::TypedValues&, + interp::TypedValues& + ) { + return interp::Result::Ok; + } + ); + + hostModule->AppendFuncExport( + "umulmod256", + {{Type::I32, Type::I32, Type::I32, Type::I32}, {}}, + [&]( + const interp::HostFunc*, + const interp::FuncSignature*, + const interp::TypedValues&, + interp::TypedValues& + ) { + return interp::Result::Ok; + } + ); + #if HERA_DEBUGGING // Create debug host module // The lifecycle of this pointer is handled by `env`. diff --git a/src/wavm.cpp b/src/wavm.cpp index 004b2bdfc..d2cb4bab8 100644 --- a/src/wavm.cpp +++ b/src/wavm.cpp @@ -249,6 +249,20 @@ namespace wavm_host_module { interface.top()->eeiSelfDestruct(addressOffset); } + // the host module is called 'bignum' + DEFINE_INTRINSIC_MODULE(bignum) + + // host functions follow + DEFINE_INTRINSIC_FUNCTION(bignum, "mul256", void, mul256, U32 a, U32 b, U32 ret) + { + interface.top()->mul256(a, b, ret); + } + + DEFINE_INTRINSIC_FUNCTION(bignum, "umulmod256", void, umulmod246, U32 a, U32 b, U32 mod, U32 ret) + { + interface.top()->umulmod256(a, b, mod, ret); + } + // this is needed for resolving names of imported host functions struct HeraWavmResolver : Runtime::Resolver { HashMap moduleNameToInstanceMap; @@ -349,10 +363,14 @@ ExecutionResult WavmEngine::internalExecute( Runtime::GCPointer ethereumHostModule = Intrinsics::instantiateModule(compartment, wavm_host_module::INTRINSIC_MODULE_REF(ethereum), "ethereum", {}); heraAssert(ethereumHostModule, "Failed to create host module."); + Runtime::GCPointer bignumHostModule = Intrinsics::instantiateModule(compartment, wavm_host_module::INTRINSIC_MODULE_REF(bignum), "bignum", {}); + heraAssert(bignumHostModule, "Failed to create host module."); + // prepare contract module to resolve links against host module wavm_host_module::HeraWavmResolver resolver; // TODO: move this into the constructor? resolver.moduleNameToInstanceMap.set("ethereum", ethereumHostModule); + resolver.moduleNameToInstanceMap.set("bignum", bignumHostModule); Runtime::LinkResult linkResult = Runtime::linkModule(moduleIR, resolver); ensureCondition(linkResult.success, ContractValidationFailure, "Couldn't link contract against host module."); @@ -457,7 +475,7 @@ void WavmEngine::verifyContract(bytes_view code) for (auto const& import: moduleIR.functions.imports) { #if HERA_DEBUGGING - if (import.moduleName == "debug") + if (import.moduleName == "debug" || import.moduleName == "bignum") continue; #endif