一个用于Rust的Ethereum虚拟机桥

吴宝
2023-12-01

原文地址:https://github.com/aion-camus/rust_evm_intf

1 motivation and framework

Our purpose providing this repository is to build a bridge between Rust Blockchain Kernel(such as Parity) and the llvm-based Ethereum Virtual Machine engine. Using this project, we can generate our own VM faster and much more stable.

The wrappered Virtual Machine mainly has three parts:

  • Rust Kernel module for building and execute smart contract, transations, talking with DataBase .etc
  • ffi module for talking with C.
  • evm module which is implemented by C++, using llvm as its backeng execution engine.

2 rust kernel module

  • create
    upper layer modules use create method to create an Virtual Machine instance; however the instance object is managed in Rust, and is not exposed to users.
  • run
    accepts an execution context which is generated by transactions(containing params such as address, gas limit, gas price, call value, call data .etc). return the result of Ethereum Virtual Machine.

3 Foreigh Function Interface

Rust has ffi mechanism to talk to outer world languages. But for now, only C is supported. So if we want to interact with other languages, such as Java/C++, we must use C-based bridge to have all these work.

for calling C from Rust(when creating evm instance, start evm or destroy evm), we could simply use:

extern "C" fn func_name(params) -> return_type {}

or

extern fn func_name(params) -> return_type {}

to tell the rustc that this is an unsafe function that is in C library. What’s more, we should update Rust link params by using #[link(name = "library_name")].

for calling Rust from C(when evm needs read/upgrade execution context, get/put storage from/to database, get block info and so on), first declare Rust functions as no_mangle.

#[no_mangle]

then declare it is an extern function. put them together, write as follows

#[no_mangle]
pub extern fn func_name(params) -> return_type {}

note that all parameters and return types should be compatible with C. It is easily facing unsafe problems, if data type size or member align is not consistent with C types. A more common usage when implementing such functions is to use bare pointer, but still keep in mind the layout pointed by your pointer.

since we have compiled the C++ evm into a dynamic library(in the previous section), we’d better register our Rust functions as callbacks for Ethereum Virtual machine. Or else, a __weak function in C may be needed for resolving compiler problems.

So we also implement a group of register methods, it is ugly, but since every callback’s signature is not the same, it seems to be the straight and fast way to get all these work. And for this register is used once during startup, it may not cost much performance.

A callback list is as follows:

void get_blockhash(const void *obj, struct evm_hash *result, int64_t number);
int exists(const void *obj, const struct evm_address *address);
uint8_t *get_storage(const void *obj, const struct evm_address *address,
    const struct evm_word *key);
void put_storage_cb(const void *obj, const struct evm_address *address,
    const struct evm_word *key,
    const struct evm_word *value);
void get_balance_cb(const void *obj, struct evm_word *result,
    const struct evm_address *address);
size_t get_code_cb(const void *obj, const uint8_t **result_code,
    const struct evm_address *address);
void selfdestruct_cb(const void *obj, const struct evm_address *address,
    const struct evm_address *beneficiary);
void call_cb(const void *obj, struct evm_result *result,
    const struct evm_message *msg);
void get_tx_context_cb(const void *obj, struct evm_tx_context *result);
void get_blockhash_cb(const void *obj, struct evm_hash *result,
    int64_t number);
void log_cb(const void *obj, const struct evm_address *address,
    const uint8_t *data,
    size_t data_size,
    const struct evm_word topics[],
    size_t topics_count);

4 Ethereum Virtual Machine

  • create an evm instance
    evmjit_create returns an evm_instance object. Keep this during transaction lifetime.
  • get evm running
    evm_instance->execute do the job

To get evm running, we first parse execution context to gererate an evm_message, then use our callbacks from previous section and the generated message to run evm.

After evm ends, an evm_result is generated, including status code.

EVM_SUCCESS = 0,               ///< Execution finished with success.
EVM_FAILURE = 1,               ///< Generic execution failure.
EVM_OUT_OF_GAS = 2,
EVM_BAD_INSTRUCTION = 3,
EVM_BAD_JUMP_DESTINATION = 4,
EVM_STACK_OVERFLOW = 5,
EVM_STACK_UNDERFLOW = 6,
EVM_REVERT = 7,
EVM_STATIC_MODE_ERROR = 8,
EVM_REJECTED = -1,
EVM_INTERNAL_ERROR = -2,

gas left, output size and output data(if output size is not zero).

 类似资料: