From 99caf5df5e4bc1adf24055faf8b4fbd4d0b19120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Jun 2018 14:38:04 +0200 Subject: [PATCH] evmjit: Switch to aleth-interpreter --- core/vm/evmjit.go | 188 ++++++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 81 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 9b3dbc31f9..0bbc385950 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -18,44 +18,46 @@ package vm /* -#include +#include #include -static const struct evm_context_fn_table* get_context_fn_table() +static const struct evmc_context_fn_table* get_context_fn_table() { int account_exists(void*, void*); - void get_storage(struct evm_uint256be*, void*, void*, struct evm_uint256be*); + void get_storage(struct evmc_uint256be*, void*, void*, struct evmc_uint256be*); void set_storage(void*, void*, void*, void*); void get_balance(void*, void*, void*); - size_t get_code(unsigned char**, void*, void*); + size_t get_code_size(void*, void*); + size_t copy_code(void*, void*, size_t, uint8_t*, size_t); void selfdestruct(void*, void*, void*); - void call(struct evm_result*, void*, struct evm_message*); + void call(struct evmc_result*, void*, struct evmc_message*); void getTxCtx(void*, void*); void getBlockHash(void*, void*, long long); - void set_logs(void*, void*, void*, size_t, void*, size_t); + void emit_log(void*, void*, void*, size_t, void*, size_t); - static const struct evm_context_fn_table fn_table = { - (evm_account_exists_fn) account_exists, - (evm_get_storage_fn) get_storage, - (evm_set_storage_fn) set_storage, - (evm_get_balance_fn) get_balance, - (evm_get_code_fn) get_code, - (evm_selfdestruct_fn) selfdestruct, - (evm_call_fn) call, - (evm_get_tx_context_fn) getTxCtx, - (evm_get_block_hash_fn) getBlockHash, - (evm_log_fn) set_logs + static const struct evmc_context_fn_table fn_table = { + (evmc_account_exists_fn) account_exists, + (evmc_get_storage_fn) get_storage, + (evmc_set_storage_fn) set_storage, + (evmc_get_balance_fn) get_balance, + (evmc_get_code_size_fn) get_code_size, + (evmc_copy_code_fn) copy_code, + (evmc_selfdestruct_fn) selfdestruct, + (evmc_call_fn) call, + (evmc_get_tx_context_fn) getTxCtx, + (evmc_get_block_hash_fn) getBlockHash, + (evmc_emit_log_fn) emit_log }; return &fn_table; } -static struct evm_result evm_execute( - struct evm_instance* instance, - struct evm_context* context, - enum evm_revision rev, - const struct evm_message* msg, +static struct evmc_result evmc_execute( + struct evmc_instance* instance, + struct evmc_context* context, + enum evmc_revision rev, + const struct evmc_message* msg, uint8_t const* code, size_t code_size ) @@ -63,23 +65,23 @@ static struct evm_result evm_execute( return instance->execute(instance, context, rev, msg, code, code_size); } -static void evm_release_result(struct evm_result* result) +static void evmc_release_result(struct evmc_result* result) { result->release(result); } -static void free_result_output(const struct evm_result* result) +static void free_result_output(const struct evmc_result* result) { free((void*)result->output_data); } -static void add_result_releaser(struct evm_result* result) +static void add_result_releaser(struct evmc_result* result) { result->release = free_result_output; } -#cgo CFLAGS: -I/home/chfast/Projects/ethereum/evmjit/include -#cgo LDFLAGS: -levmjit-standalone -lstdc++ -lm -ldl -L/home/chfast/Projects/ethereum/evmjit/build/release-llvm/libevmjit +#cgo CFLAGS: -I/home/chfast/Projects/ethereum/cpp-ethereum -I/home/chfast/Projects/ethereum/cpp-ethereum/evmc/include +#cgo LDFLAGS: -laleth-interpreter -lstdc++ -lm -ldl -L/home/chfast/Projects/ethereum/cpp-ethereum/build/interpreter/libaleth-interpreter */ import "C" @@ -97,7 +99,7 @@ import ( ) type EVMJIT struct { - jit *C.struct_evm_instance + jit *C.struct_evmc_instance env *EVM intPool *intPool readOnly bool @@ -110,13 +112,13 @@ type EVMCContext struct { } type ContextWrapper struct { - c C.struct_evm_context + c C.struct_evmc_context index int } func NewJit(env *EVM, cfg Config) *EVMJIT { // FIXME: Destroy the jit later. - return &EVMJIT{C.evmjit_create(), env, nil, false, nil} + return &EVMJIT{C.evmc_create_interpreter(), env, nil, false, nil} } @@ -158,15 +160,15 @@ func getContract(pCtx unsafe.Pointer) *Contract { return getCtx(ctxWrapper.index).contract } -func HashToEvmc(hash common.Hash) C.struct_evm_uint256be { - return C.struct_evm_uint256be{*(*[32]C.uint8_t)(unsafe.Pointer(&hash[0]))} +func HashToEvmc(hash common.Hash) C.struct_evmc_uint256be { + return C.struct_evmc_uint256be{*(*[32]C.uint8_t)(unsafe.Pointer(&hash[0]))} } -func AddressToEvmc(addr common.Address) C.struct_evm_address { - return C.struct_evm_address{*(*[20]C.uint8_t)(unsafe.Pointer(&addr[0]))} +func AddressToEvmc(addr common.Address) C.struct_evmc_address { + return C.struct_evmc_address{*(*[20]C.uint8_t)(unsafe.Pointer(&addr[0]))} } -func BigToEvmc(i *big.Int) C.struct_evm_uint256be { +func BigToEvmc(i *big.Int) C.struct_evmc_uint256be { return HashToEvmc(common.BigToHash(i)) } @@ -184,7 +186,7 @@ func GoByteSlice(data unsafe.Pointer, size C.size_t) []byte { return *(*[]byte)(unsafe.Pointer(&sliceHeader)) } -func EvmcHashToSlice(uint256 *C.struct_evm_uint256be) []byte { +func EvmcHashToSlice(uint256 *C.struct_evmc_uint256be) []byte { return GoByteSlice(unsafe.Pointer(uint256), 32) } @@ -210,7 +212,7 @@ func account_exists(pCtx unsafe.Pointer, pAddr unsafe.Pointer) C.int { } //export get_storage -func get_storage(pResult *C.struct_evm_uint256be, pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg *C.struct_evm_uint256be) { +func get_storage(pResult *C.struct_evmc_uint256be, pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg *C.struct_evmc_uint256be) { result := EvmcHashToSlice(pResult) env := getEnv(pCtx) @@ -234,8 +236,8 @@ func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer newVal := *(*[32]byte)(pArg2) oldVal := env.StateDB.GetState(addr, key) env.StateDB.SetState(addr, key, newVal) - if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { - env.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas)) + if oldVal != (common.Hash{}) && newVal == (common.Hash{}) { + env.StateDB.AddRefund(params.SstoreRefundGas) } // fmt.Printf("EVMJIT STORE %x: %x := %x\n", addr, key, newVal) } @@ -253,21 +255,37 @@ func get_balance(pResult unsafe.Pointer, pCtx unsafe.Pointer, pAddr unsafe.Point // fmt.Printf("BALANCE %x : %v\n", addr, balance) } -//export get_code -func get_code(ppCode **C.uint8_t, pCtx unsafe.Pointer, pAddr unsafe.Pointer) C.size_t { +//export get_code_size +func get_code_size(pCtx unsafe.Pointer, pAddr unsafe.Pointer) C.size_t { env := getEnv(pCtx) var addr common.Address copy(addr[:], GoByteSlice(pAddr, 20)) - if ppCode != nil { - code := env.StateDB.GetCode(addr) - *ppCode = ptr(code) + return C.size_t(env.StateDB.GetCodeSize(addr)); +} - // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) - return C.size_t(len(code)) - } else { - return C.size_t(env.StateDB.GetCodeSize(addr)) +//export copy_code +func copy_code(pCtx unsafe.Pointer, pAddr unsafe.Pointer, offset C.size_t, p *C.uint8_t, size C.size_t) C.size_t { + env := getEnv(pCtx) + + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) + code := env.StateDB.GetCode(addr) + length := C.size_t(len(code)) + + if offset >= length { + return 0 } + + toCopy := length - offset; + if toCopy > size { + toCopy = size + } + + out := GoByteSlice(unsafe.Pointer(p), size) + + copy(out, code[offset:]) + return toCopy } //export selfdestruct @@ -282,7 +300,7 @@ func selfdestruct(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg unsafe.Pointer db := env.StateDB if !db.HasSuicided(addr) { - db.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas)) + db.AddRefund(params.SuicideRefundGas) } balance := db.GetBalance(addr) db.AddBalance(beneficiary, balance) @@ -292,13 +310,13 @@ func selfdestruct(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg unsafe.Pointer //export getTxCtx func getTxCtx(pResult unsafe.Pointer, pCtx unsafe.Pointer) { env := getEnv(pCtx) - txCtx := (*C.struct_evm_tx_context)(pResult) + txCtx := (*C.struct_evmc_tx_context)(pResult) txCtx.tx_gas_price = BigToEvmc(env.GasPrice) txCtx.tx_origin = AddressToEvmc(env.Origin) txCtx.block_coinbase = AddressToEvmc(env.Coinbase) txCtx.block_number = C.int64_t(env.BlockNumber.Int64()) txCtx.block_timestamp = C.int64_t(env.Time.Int64()) - txCtx.block_gas_limit = C.int64_t(env.GasLimit.Int64()) + txCtx.block_gas_limit = C.int64_t(env.GasLimit) txCtx.block_difficulty = BigToEvmc(env.Difficulty) } @@ -316,8 +334,8 @@ func getBlockHash(pResult unsafe.Pointer, pCtx unsafe.Pointer, number int64) { copy(result, hash[:]) } -//export set_logs -func set_logs(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pData unsafe.Pointer, dataSize C.size_t, pTopics unsafe.Pointer, topicsCount C.size_t) { +//export emit_log +func emit_log(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pData unsafe.Pointer, dataSize C.size_t, pTopics unsafe.Pointer, topicsCount C.size_t) { env := getEnv(pCtx) var addr common.Address @@ -340,13 +358,13 @@ func set_logs(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pData unsafe.Pointer, d } //export call -func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_message) { +func call(result *C.struct_evmc_result, pCtx unsafe.Pointer, msg *C.struct_evmc_message) { env := getEnv(pCtx) contract := getContract(pCtx) - addr := *(*common.Address)(unsafe.Pointer(&msg.address)) + addr := *(*common.Address)(unsafe.Pointer(&msg.destination)) value := (*(*common.Hash)(unsafe.Pointer(&msg.value))).Big() - input := GoByteSlice(unsafe.Pointer(msg.input), msg.input_size) + input := GoByteSlice(unsafe.Pointer(msg.input_data), msg.input_size) gas := uint64(msg.gas) var output []byte @@ -354,8 +372,8 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me var err error switch msg.kind { - case C.EVM_CALL: - staticCall := (msg.flags & C.EVM_STATIC) != 0 + case C.EVMC_CALL: + staticCall := (msg.flags & C.EVMC_STATIC) != 0 if staticCall { // fmt.Printf("STATICCALL(gas %d, %x)\n", gas, addr) output, gasLeft, err = env.StaticCall(contract, addr, input, gas) @@ -365,18 +383,19 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me } - case C.EVM_CALLCODE: + case C.EVMC_CALLCODE: // fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", gas, addr, value) output, gasLeft, err = env.CallCode(contract, addr, input, gas, value) - case C.EVM_DELEGATECALL: + case C.EVMC_DELEGATECALL: // fmt.Printf("DELEGATECALL(gas %d, %x)\n", gas, addr) output, gasLeft, err = env.DelegateCall(contract, addr, input, gas) - case C.EVM_CREATE: + case C.EVMC_CREATE: // fmt.Printf("CREATE(gas %d, %x)\n", gas, addr) var createAddr common.Address - _, createAddr, gasLeft, err = env.Create(contract, input, gas, value) + var createOutput []byte + createOutput, createAddr, gasLeft, err = env.Create(contract, input, gas, value) isHomestead := env.ChainConfig().IsHomestead(env.BlockNumber) if !isHomestead && err == ErrCodeStoreOutOfGas { err = nil @@ -385,18 +404,25 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me // Copy create address to result. ca := GoByteSlice(unsafe.Pointer(&result.create_address.bytes), 20) copy(ca, createAddr[:]) + } else if err == errExecutionReverted { + // Assign return buffer from REVERT. + // TODO: Bad API design: return data buffer and the code is returned in the same place. In worst case + // the code is returned also when there is not enough funds to deploy the code. + output = createOutput } } assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - // fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) + //fmt.Printf("Gas left %d, err: %s, output: %x\n", gasLeft, err, output) result.gas_left = C.int64_t(gasLeft) // Map error to status code. if err == nil { - result.status_code = C.EVM_SUCCESS + result.status_code = C.EVMC_SUCCESS + } else if err == errExecutionReverted { + result.status_code = C.EVMC_REVERT } else { - result.status_code = C.EVM_FAILURE + result.status_code = C.EVMC_FAILURE } if len(output) > 0 { @@ -419,21 +445,21 @@ func ptr(bytes []byte) *C.uint8_t { return (*C.uint8_t)(unsafe.Pointer(header.Data)) } -func getRevision(env *EVM) C.enum_evm_revision { +func getRevision(env *EVM) C.enum_evmc_revision { n := env.BlockNumber if env.ChainConfig().IsByzantium(n) { - return C.EVM_BYZANTIUM + return C.EVMC_BYZANTIUM } if env.ChainConfig().IsEIP158(n) { - return C.EVM_SPURIOUS_DRAGON + return C.EVMC_SPURIOUS_DRAGON } if env.ChainConfig().IsEIP150(n) { - return C.EVM_TANGERINE_WHISTLE + return C.EVMC_TANGERINE_WHISTLE } if env.ChainConfig().IsHomestead(n) { - return C.EVM_HOMESTEAD + return C.EVMC_HOMESTEAD } - return C.EVM_FRONTIER + return C.EVMC_FRONTIER } func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) { @@ -456,8 +482,8 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) wrapper.c.fn_table = C.get_context_fn_table() wrapper.index = pinCtx(&EVMCContext{contract, evm.env}) - var msg C.struct_evm_message - msg.address = AddressToEvmc(contract.Address()) + var msg C.struct_evmc_message + msg.destination = AddressToEvmc(contract.Address()) msg.sender = AddressToEvmc(contract.Caller()) msg.value = BigToEvmc(contract.value) msg.gas = gas @@ -465,45 +491,45 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) codeHash := crypto.Keccak256Hash(code) msg.code_hash = HashToEvmc(codeHash) if evm.readOnly { - msg.flags = C.EVM_STATIC + msg.flags = C.EVMC_STATIC } else { msg.flags = 0 } if len(input) > 0 { cInput := C.CBytes(input) - msg.input = (*C.uint8_t)(cInput) + msg.input_data = (*C.uint8_t)(cInput) msg.input_size = C.size_t(len(input)) defer C.free(cInput) } else { - msg.input = nil + msg.input_data = nil msg.input_size = 0 } // fmt.Printf("EVMJIT pre Run (gas %d %d mode: %d, env: %d) %x %x\n", contract.Gas, gas, rev, wrapper.index, codeHash, contract.Address()) - r := C.evm_execute(evm.jit, &wrapper.c, rev, &msg, codePtr, codeSize) + r := C.evmc_execute(evm.jit, &wrapper.c, rev, &msg, codePtr, codeSize) unpinCtx(wrapper.index) // fmt.Printf("EVMJIT Run [%d]: %d %d %x\n", evm.env.depth - 1, r.status_code, r.gas_left, contract.Address()) if r.gas_left > gas { - panic("OOPS") + panic(fmt.Sprintf("gas left: %d, gas: %d, status: %d", r.gas_left, gas, r.status_code)) } contract.Gas = uint64(r.gas_left) // fmt.Printf("Gas left: %d\n", contract.Gas) output := C.GoBytes(unsafe.Pointer(r.output_data), C.int(r.output_size)) - if r.status_code == C.EVM_REVERT { + if r.status_code == C.EVMC_REVERT { err = errExecutionReverted - } else if r.status_code != C.EVM_SUCCESS { + } else if r.status_code != C.EVMC_SUCCESS { // EVMJIT does not informs about the kind of the EVM expection. err = ErrOutOfGas } if r.release != nil { // fmt.Printf("Releasing result with %p\n", r.release) - C.evm_release_result(&r) + C.evmc_release_result(&r) } return output, err