Skip to content

Commit

Permalink
Add initial support for runevm system contract
Browse files Browse the repository at this point in the history
Rename flag to disable-interface-metering

Minor fix

Revert temp memory allocation for runevm

Add runevm flags to README.md

Add locallyExecuteSystemContract function to use in runevm
  • Loading branch information
s1na committed Apr 30, 2019
1 parent acba3a9 commit c8bd9bf
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ These are to be used via EVMC `set_option`:

- `engine=<engine>` will select the underlying WebAssembly engine, where the only accepted values currently are `binaryen`, `wabt`, and `wavm`
- `metering=true` will enable metering of bytecode at deployment using the [Sentinel system contract] (set to `false` by default)
- `disable-interface-metering=true` will disable metering of EEI methods (set to `false` by default)
- `benchmark=true` will produce execution timings and output it to both standard error output and `hera_benchmarks.log` file.
- `evm1mode=<evm1mode>` will select how EVM1 bytecode is handled
- `sys:<alias/address>=file.wasm` will override the code executing at the specified address with code loaded from a filepath at runtime. This option supports aliases for system contracts as well, such that `sys:sentinel=file.wasm` and `sys:evm2wasm=file.wasm` are both valid. **This option is intended for debugging purposes.**
Expand All @@ -75,6 +76,7 @@ These are to be used via EVMC `set_option`:
- `reject` will reject any EVM1 bytecode with an error (the default setting)
- `fallback` will allow EVM1 bytecode to be passed through to the client for execution
- `evm2wasm` will enable transformation of bytecode using the [EVM Transcompiler]
- `runevm` will transform EVM1 bytecode using [runevm](https://github.com/axic/runevm)

## Interfaces

Expand Down
96 changes: 93 additions & 3 deletions src/hera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum class hera_evm1mode {
reject,
fallback,
evm2wasm_contract,
runevm_contract,
};

using WasmEngineCreateFn = unique_ptr<WasmEngine>(*)();
Expand All @@ -71,6 +72,7 @@ const map<string, hera_evm1mode> evm1mode_options {
{ "reject", hera_evm1mode::reject },
{ "fallback", hera_evm1mode::fallback },
{ "evm2wasm", hera_evm1mode::evm2wasm_contract },
{ "runevm", hera_evm1mode::runevm_contract },
};

struct hera_instance : evmc_instance {
Expand All @@ -88,13 +90,15 @@ struct hera_instance : evmc_instance {
};
hera_evm1mode evm1mode = hera_evm1mode::reject;
bool metering = false;
bool meter_interface = false;
map<evmc_address, bytes> contract_preload_list;

hera_instance() noexcept : evmc_instance({EVMC_ABI_VERSION, "hera", hera_get_buildinfo()->project_version, nullptr, nullptr, nullptr, nullptr, nullptr}) {}
};

const evmc_address sentinelAddress = { .bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa } };
const evmc_address evm2wasmAddress = { .bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xb } };
const evmc_address runevmAddress = { .bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc } };

// Calls a system contract at @address with input data @input.
// It is a "staticcall" with sender 000...000 and no value.
Expand Down Expand Up @@ -132,6 +136,52 @@ pair<evmc_status_code, bytes> callSystemContract(
return {result.status_code, ret};
}

pair<evmc_status_code, bytes> locallyExecuteSystemContract(
evmc_context* context,
evmc_address const& address,
int64_t & gas,
bytes_view input,
bytes_view code,
bytes_view state_code
) {
const evmc_message message = {
.kind = EVMC_CALL,
.flags = EVMC_STATIC,
.depth = 0,
.gas = gas,
.destination = address,
.sender = {},
.input_data = input.data(),
.input_size = input.size(),
.value = {},
.create2_salt = {},
};

unique_ptr<WasmEngine> engine{
// This is the order of preference.
#if HERA_BINARYEN
new BinaryenEngine
#elif HERA_WABT
new WabtEngine
#elif HERA_WAVM
new WavmEngine
#else
#error "No engine requested."
#endif
};

ExecutionResult result = engine->execute(context, code, state_code, message, false);

bytes ret;
evmc_status_code status = result.isRevert ? EVMC_REVERT : EVMC_SUCCESS;
if (status == EVMC_SUCCESS && result.returnValue.size() > 0)
ret = move(result.returnValue);

heraAssert(result.gasLeft > 0, "Runevm ran out of gas.");

return {status, ret};
}

// Calls the Sentinel contract with input data @input.
// @returns the validated and metered output or empty output otherwise.
bytes sentinel(evmc_context* context, bytes_view input)
Expand Down Expand Up @@ -189,6 +239,38 @@ bytes evm2wasm(evmc_context* context, bytes_view input) {
return ret;
}

// Calls the runevm contract.
// @returns a wasm-based evm interpreter.
bytes runevm(evmc_context* context, bytes code) {
HERA_DEBUG << "Calling runevm (code " << code.size() << " bytes)...\n";

int64_t gas = numeric_limits<int64_t>::max(); // do not charge for metering yet (give unlimited gas)
evmc_status_code status;
bytes ret;

tie(status, ret) = locallyExecuteSystemContract(
context,
runevmAddress,
gas,
{},
code,
code
);

HERA_DEBUG << "runevm done (output " << ret.size() << " bytes) with status=" << status << "\n";

heraAssert(ret.size() > 0, "Runevm returned empty.");
heraAssert(hasWasmPreamble(ret), "Runevm result has no wasm preamble.");

ensureCondition(
status == EVMC_SUCCESS,
ContractValidationFailure,
"runevm has failed."
);

return ret;
}

void hera_destroy_result(evmc_result const* result) noexcept
{
delete[] result->output_data;
Expand All @@ -213,7 +295,7 @@ evmc_result hera_execute(
heraAssert(rev == EVMC_BYZANTIUM, "Only Byzantium supported.");
heraAssert(msg->gas >= 0, "EVMC supplied negative startgas");

bool meterInterfaceGas = true;
bool meterInterfaceGas = hera->meter_interface;

// the bytecode residing in the state - this will be used by interface methods (i.e. codecopy)
bytes_view state_code{code, code_size};
Expand Down Expand Up @@ -247,6 +329,9 @@ evmc_result hera_execute(
HERA_DEBUG << "Non-WebAssembly input, failure.\n";
ret.status_code = EVMC_FAILURE;
return ret;
case hera_evm1mode::runevm_contract:
run_code = runevm(context, hera->contract_preload_list[runevmAddress]);
ensureCondition(run_code.size() > 8, ContractValidationFailure, "Interpreting via runevm failed");
}
}

Expand Down Expand Up @@ -305,7 +390,6 @@ evmc_result hera_execute(
ret.output_data = output_data;
ret.release = hera_destroy_result;
}

ret.status_code = result.isRevert ? EVMC_REVERT : EVMC_SUCCESS;
ret.gas_left = result.gasLeft;
} catch (EndExecution const&) {
Expand Down Expand Up @@ -367,7 +451,8 @@ bool hera_parse_sys_option(hera_instance *hera, string const& _name, string cons
// alias
const map<string, evmc_address> aliases = {
{ string("sentinel"), sentinelAddress },
{ string("evm2wasm"), evm2wasmAddress }
{ string("evm2wasm"), evm2wasmAddress },
{ string("runevm"), runevmAddress },
};

if (aliases.count(name) == 0) {
Expand Down Expand Up @@ -422,6 +507,11 @@ evmc_set_option_result hera_set_option(
return EVMC_SET_OPTION_INVALID_VALUE;
}

if (strcmp(name, "disable-interface-metering") == 0) {
hera->meter_interface = !(strcmp(value, "true") == 0);
return EVMC_SET_OPTION_SUCCESS;
}

if (strcmp(name, "engine") == 0) {
auto it = wasm_engine_map.find(value);
if (it != wasm_engine_map.end()) {
Expand Down

0 comments on commit c8bd9bf

Please sign in to comment.