From deff301c33c333fc045c83024bd457cb5788b00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 16 Dec 2016 17:52:33 +0100 Subject: [PATCH 01/29] core/vm: EVMJIT integration --- core/vm/environment.go | 2 +- core/vm/vm_jit.go | 744 +++++++++++++++++++++++------------------ 2 files changed, 418 insertions(+), 328 deletions(-) diff --git a/core/vm/environment.go b/core/vm/environment.go index 50a09d4444..91c8e38d2c 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -81,7 +81,7 @@ func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainC vmConfig: vmConfig, chainConfig: chainConfig, } - env.evm = New(env, vmConfig) + env.evm = NewJit(env, vmConfig) return env } diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go index f6e4a515bc..cbebb67080 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/vm_jit.go @@ -14,376 +14,466 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build evmjit - package vm /* -void* evmjit_create(); -int evmjit_run(void* _jit, void* _data, void* _env); -void evmjit_destroy(void* _jit); +#include +#include -// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib -// More: https://github.com/ethereum/evmjit -#cgo LDFLAGS: -levmjit +static union evm_variant query_gateway(struct evm_env* env, + enum evm_query_key key, + union evm_variant arg) +{ + void query(void*, size_t, int, void*); + + union evm_variant result; + query(&result, (size_t)env, key, &arg); + return result; +} + +extern void update(void* env, int key, void* arg1, void* arg2); +static void update_gateway(struct evm_env* env, + enum evm_update_key key, + union evm_variant arg1, + union evm_variant arg2) +{ + update(env, key, &arg1, &arg2); +} + +typedef long long int64; + +extern int64 call( + void* env, + int kind, + int64 gas, + void* address, + void* value, + void* input, + size_t input_size, + void* output, + size_t output_size); +static int64_t call_gateway( + struct evm_env* env, + enum evm_call_kind kind, + int64_t gas, + struct evm_uint160be address, + struct evm_uint256be value, + uint8_t const* input, + size_t input_size, + uint8_t* output, + size_t output_size) +{ + return call(env, kind, gas, &address, &value, (void*)input, input_size, output, output_size); +} + +static struct evm_instance* new_evmjit() +{ + struct evm_factory factory = evmjit_get_factory(); + return factory.create(query_gateway, update_gateway, call_gateway); +} + +static struct evm_result evm_execute(struct evm_instance* instance, + struct evm_env* env, + enum evm_mode mode, + struct evm_uint256be code_hash, + uint8_t const* code, + size_t code_size, + int64_t gas, + uint8_t const* input, + size_t input_size, + struct evm_uint256be value) +{ + return instance->execute(instance, env, mode, code_hash, code, code_size, gas, input, input_size, value); +} + +#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 */ import "C" -/* import ( - "bytes" - "errors" "fmt" + "math" "math/big" + "reflect" + "sync" "unsafe" - "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) -type JitVm struct { - env Environment - me ContextRef - callerAddr []byte - price *big.Int - data RuntimeData +type EVMJIT struct { + jit *C.struct_evm_instance + env *Environment } -type i256 [32]byte - -type RuntimeData struct { - gas int64 - gasPrice int64 - callData *byte - callDataSize uint64 - address i256 - caller i256 - origin i256 - callValue i256 - coinBase i256 - difficulty i256 - gasLimit i256 - number uint64 - timestamp int64 - code *byte - codeSize uint64 - codeHash i256 +type EVMCContext struct { + contract *Contract + env *Environment } -func hash2llvm(h []byte) i256 { - var m i256 - copy(m[len(m)-len(h):], h) // right aligned copy - return m +func NewJit(env *Environment, cfg Config) *EVMJIT { + // FIXME: Destroy the jit later. + return &EVMJIT{C.new_evmjit(), env} } -func llvm2hash(m *i256) []byte { - return C.GoBytes(unsafe.Pointer(m), C.int(len(m))) -} -func llvm2hashRef(m *i256) []byte { - return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)] -} +var contextMap = make(map[uintptr]*EVMCContext) +var contextMapMu sync.Mutex -func address2llvm(addr []byte) i256 { - n := hash2llvm(addr) - bswap(&n) - return n -} +func pinCtx(ctx *EVMCContext) uintptr { + contextMapMu.Lock() -// bswap swap bytes of the 256-bit integer on LLVM side -// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations -func bswap(m *i256) *i256 { - for i, l := 0, len(m); i < l/2; i++ { - m[i], m[l-i-1] = m[l-i-1], m[i] + // Find empty slot in the map starting from the map length. + id := uintptr(len(contextMap)) + for contextMap[id] != nil { + id++ } - return m + contextMap[id] = ctx + contextMapMu.Unlock() + return id } -func trim(m []byte) []byte { - skip := 0 - for i := 0; i < len(m); i++ { - if m[i] == 0 { - skip++ - } else { - break +func unpinCtx(id uintptr) { + contextMapMu.Lock() + delete(contextMap, id) + contextMapMu.Unlock() +} + +func getCtx(id uintptr) *EVMCContext { + contextMapMu.Lock() + defer contextMapMu.Unlock() + return contextMap[id] +} + +func HashToEvmc(hash common.Hash) C.struct_evm_uint256be { + return C.struct_evm_uint256be{bytes: *(*[32]C.uint8_t)(unsafe.Pointer(&hash[0]))} +} + +func BigToEvmc(i *big.Int) C.struct_evm_uint256be { + return HashToEvmc(common.BigToHash(i)) +} + +func assert(cond bool, msg string) { + if !cond { + panic(fmt.Sprintf("Assertion failure! %v", msg)) + } +} + +type MemoryRef struct { + ptr *C.uint8_t + len int +} + +func GoByteSlice(data unsafe.Pointer, size C.size_t) []byte { + var sliceHeader reflect.SliceHeader + sliceHeader.Data = uintptr(data) + sliceHeader.Len = int(size) + sliceHeader.Cap = int(size) + return *(*[]byte)(unsafe.Pointer(&sliceHeader)) +} + +//export query +func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointer) { + // Represent the result memory as Go slice of 32 bytes. + result := GoByteSlice(pResult, 32) + // Or as pointer to int64. + pInt64Result := (*int64)(pResult) + + // Copy the argument to [32]byte array. + arg := *(*[32]byte)(pArg) + + // Get the execution context. + ctx := getCtx(ctxIdx) + + switch key { + case C.EVM_SLOAD: + val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg) + copy(result, val[:]) + // fmt.Printf("EVMJIT SLOAD %x : %x\n", arg, result) + case C.EVM_ADDRESS: + addr := ctx.contract.Address() + copy(result[12:], addr[:]) + // fmt.Printf("ADDRESS %x : %x\n", addr, result[12:]) + case C.EVM_CALLER: + addr := ctx.contract.Caller() + copy(result[12:], addr[:]) + case C.EVM_ORIGIN: + addr := ctx.env.Origin + copy(result[12:], addr[:]) + case C.EVM_GAS_PRICE: + val := common.BigToHash(ctx.env.GasPrice) + copy(result, val[:]) + case C.EVM_COINBASE: + addr := ctx.env.Coinbase + copy(result[12:], addr[:]) + case C.EVM_DIFFICULTY: + val := common.BigToHash(ctx.env.Difficulty) + copy(result, val[:]) + case C.EVM_GAS_LIMIT: + *pInt64Result = ctx.env.GasLimit.Int64() + // fmt.Printf("GASLIMIT %d %d\n", *pInt64Result, ctx.env.GasLimit()) + case C.EVM_NUMBER: + *pInt64Result = ctx.env.BlockNumber.Int64() + case C.EVM_TIMESTAMP: + *pInt64Result = ctx.env.Time.Int64() + case C.EVM_CODE_BY_ADDRESS: + var addr common.Address + copy(addr[:], arg[12:]) + code := ctx.env.StateDB.GetCode(addr) + pResAsMemRef := (*MemoryRef)(pResult) + pResAsMemRef.ptr = ptr(code) + pResAsMemRef.len = len(code) + // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) + case C.EVM_CODE_SIZE: + var addr common.Address + copy(addr[:], arg[12:]) + pInt64Result := (*int64)(pResult) + *pInt64Result = int64(ctx.env.StateDB.GetCodeSize(addr)) + // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) + case C.EVM_BALANCE: + var addr common.Address + copy(addr[:], arg[12:]) + balance := ctx.env.StateDB.GetBalance(addr) + val := common.BigToHash(balance) + copy(result, val[:]) + case C.EVM_BLOCKHASH: + n := *(*int64)(pArg) + b := ctx.env.BlockNumber.Int64() + a := b - 256 + var hash common.Hash + if n >= a && n < b { + hash = ctx.env.GetHash(uint64(n)) } - } - return m[skip:] -} - -func getDataPtr(m []byte) *byte { - var p *byte - if len(m) > 0 { - p = &m[0] - } - return p -} - -func big2llvm(n *big.Int) i256 { - m := hash2llvm(n.Bytes()) - bswap(&m) - return m -} - -func llvm2big(m *i256) *big.Int { - n := big.NewInt(0) - for i := 0; i < len(m); i++ { - b := big.NewInt(int64(m[i])) - b.Lsh(b, uint(i)*8) - n.Add(n, b) - } - return n -} - -// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC) -// User must ensure that referenced memory is available to Go until the data is copied or not needed any more -func llvm2bytesRef(data *byte, length uint64) []byte { - if length == 0 { - return nil - } - if data == nil { - panic("Unexpected nil data pointer") - } - return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length] -} - -func untested(condition bool, message string) { - if condition { - panic("Condition `" + message + "` tested. Remove assert.") - } -} - -func assert(condition bool, message string) { - if !condition { - panic("Assert `" + message + "` failed!") - } -} - -func NewJitVm(env Environment) *JitVm { - return &JitVm{env: env} -} - -func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { - // TODO: depth is increased but never checked by VM. VM should not know about it at all. - self.env.SetDepth(self.env.Depth() + 1) - - // TODO: Move it to Env.Call() or sth - if Precompiled[string(me.Address())] != nil { - // if it's address of precompiled contract - // fallback to standard VM - stdVm := New(self.env) - return stdVm.Run(me, caller, code, value, gas, price, callData) - } - - if self.me != nil { - panic("JitVm.Run() can be called only once per JitVm instance") - } - - self.me = me - self.callerAddr = caller.Address() - self.price = price - - self.data.gas = gas.Int64() - self.data.gasPrice = price.Int64() - self.data.callData = getDataPtr(callData) - self.data.callDataSize = uint64(len(callData)) - self.data.address = address2llvm(self.me.Address()) - self.data.caller = address2llvm(caller.Address()) - self.data.origin = address2llvm(self.env.Origin()) - self.data.callValue = big2llvm(value) - self.data.coinBase = address2llvm(self.env.Coinbase()) - self.data.difficulty = big2llvm(self.env.Difficulty()) - self.data.gasLimit = big2llvm(self.env.GasLimit()) - self.data.number = self.env.BlockNumber().Uint64() - self.data.timestamp = self.env.Time() - self.data.code = getDataPtr(code) - self.data.codeSize = uint64(len(code)) - self.data.codeHash = hash2llvm(crypto.Keccak256(code)) // TODO: Get already computed hash? - - jit := C.evmjit_create() - retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self)) - - if retCode < 0 { - err = errors.New("OOG from JIT") - gas.SetInt64(0) // Set gas to 0, JIT does not bother - } else { - gas.SetInt64(self.data.gas) - if retCode == 1 { // RETURN - ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize)) - } else if retCode == 2 { // SUICIDE - // TODO: Suicide support logic should be moved to Env to be shared by VM implementations - state := self.Env().State() - receiverAddr := llvm2hashRef(bswap(&self.data.address)) - receiver := state.GetOrNewStateObject(receiverAddr) - balance := state.GetBalance(me.Address()) - receiver.AddBalance(balance) - state.Delete(me.Address()) + copy(result, hash[:]) + // fmt.Printf("BLOCKHASH %x : %x (%d, %d, %d)\n", result, hash, n, a, b) + case C.EVM_ACCOUNT_EXISTS: + var addr common.Address + copy(addr[:], arg[12:]) + eip158 := ctx.env.ChainConfig().IsEIP158(ctx.env.BlockNumber) + var exist int64 + if eip158 { + if !ctx.env.StateDB.Empty(addr) { + exist = 1 + } + } else if ctx.env.StateDB.Exist(addr) { + exist = 1 } - } + *pInt64Result = exist + // fmt.Printf("EXISTS? %x : %v\n", addr, exist) + case C.EVM_CALL_DEPTH: + *pInt64Result = int64(ctx.env.Depth - 1) - C.evmjit_destroy(jit) - return -} - -func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine { - return self -} - -func (self *JitVm) Endl() VirtualMachine { - return self -} - -func (self *JitVm) Env() Environment { - return self.env -} - -//export env_sha3 -func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) { - data := llvm2bytesRef(dataPtr, length) - hash := crypto.Keccak256(data) - result := (*i256)(resultPtr) - *result = hash2llvm(hash) -} - -//export env_sstore -func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) { - vm := (*JitVm)(vmPtr) - index := llvm2hash(bswap((*i256)(indexPtr))) - value := llvm2hash(bswap((*i256)(valuePtr))) - value = trim(value) - if len(value) == 0 { - prevValue := vm.env.State().GetState(vm.me.Address(), index) - if len(prevValue) != 0 { - vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund) - } - } - - vm.env.State().SetState(vm.me.Address(), index, value) -} - -//export env_sload -func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) { - vm := (*JitVm)(vmPtr) - index := llvm2hash(bswap((*i256)(indexPtr))) - value := vm.env.State().GetState(vm.me.Address(), index) - result := (*i256)(resultPtr) - *result = hash2llvm(value) - bswap(result) -} - -//export env_balance -func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) { - vm := (*JitVm)(_vm) - addr := llvm2hash((*i256)(_addr)) - balance := vm.Env().State().GetBalance(addr) - result := (*i256)(_result) - *result = big2llvm(balance) -} - -//export env_blockhash -func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) { - vm := (*JitVm)(_vm) - number := llvm2big((*i256)(_number)) - result := (*i256)(_result) - - currNumber := vm.Env().BlockNumber() - limit := big.NewInt(0).Sub(currNumber, big.NewInt(256)) - if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 { - hash := vm.Env().GetHash(uint64(number.Int64())) - *result = hash2llvm(hash) - } else { - *result = i256{} + default: + // fmt.Printf("Unhandled %d\n", key) } } -//export env_call -func env_call(_vm unsafe.Pointer, _gas *int64, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool { - vm := (*JitVm)(_vm) +//export update +func update(pCtx unsafe.Pointer, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { + arg1 := *(*[32]byte)(pArg1) + arg2 := *(*[32]byte)(pArg2) + ctx := getCtx(uintptr(pCtx)) - //fmt.Printf("env_call (depth %d)\n", vm.Env().Depth()) - - defer func() { - if r := recover(); r != nil { - fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r) + switch key { + case C.EVM_SSTORE: + val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg1) + ctx.env.StateDB.SetState(ctx.contract.Address(), arg1, arg2) + if !common.EmptyHash(val) && common.EmptyHash(arg2) { + // panic("SSTORE REFUND") + ctx.env.StateDB.AddRefund(params.SstoreRefundGas) } - }() - - balance := vm.Env().State().GetBalance(vm.me.Address()) - value := llvm2big((*i256)(_value)) - - if balance.Cmp(value) >= 0 { - receiveAddr := llvm2hash((*i256)(_receiveAddr)) - inData := C.GoBytes(inDataPtr, C.int(inDataLen)) - outData := llvm2bytesRef(outDataPtr, outDataLen) - codeAddr := llvm2hash((*i256)(_codeAddr)) - gas := big.NewInt(*_gas) - var out []byte - var err error - if bytes.Equal(codeAddr, receiveAddr) { - out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value) - } else { - out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value) + // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) + case C.EVM_LOG: + dataRef := (*MemoryRef)(pArg1) + topicsRef := (*MemoryRef)(pArg2) + data := C.GoBytes(unsafe.Pointer(dataRef.ptr), C.int(dataRef.len)) + // FIXME: Avoid double copy of topics. + tData := C.GoBytes(unsafe.Pointer(topicsRef.ptr), C.int(topicsRef.len)) + nTopics := topicsRef.len / 32 + topics := make([]common.Hash, nTopics) + for i := 0; i < nTopics; i++ { + copy(topics[i][:], tData[i*32:(i+1)*32]) } - *_gas = gas.Int64() + log := NewLog(ctx.contract.Address(), topics, data, ctx.env.BlockNumber.Uint64()) + ctx.env.StateDB.AddLog(log) + case C.EVM_SELFDESTRUCT: + db := ctx.env.StateDB + addr := ctx.contract.Address() + if !db.HasSuicided(addr) { + db.AddRefund(params.SuicideRefundGas) + } + balance := db.GetBalance(addr) + beneficiary := common.BytesToAddress(arg1[12:]) + db.AddBalance(beneficiary, balance) + db.Suicide(addr) + } +} + +//export call +func call( + pCtx unsafe.Pointer, + kind int32, + gas int64, + pAddr unsafe.Pointer, + pValue unsafe.Pointer, + pInput unsafe.Pointer, + inputSize C.size_t, + pOutput unsafe.Pointer, + outputSize C.size_t) int64 { + + ctx := getCtx(uintptr(pCtx)) + address := *(*[20]byte)(pAddr) + value := (*(*common.Hash)(pValue)).Big() + input := GoByteSlice(pInput, inputSize) + output := GoByteSlice(pOutput, outputSize) + bigGas := new(big.Int).SetInt64(gas) + + // FIXME: C.EVM_CALL_FAILURE not available. + const callFailure = math.MinInt64 + + switch kind { + case C.EVM_CALL: + // fmt.Printf("CALL(gas %d, %x)\n", bigGas, address) + ctx.contract.Gas.SetInt64(0) + ret, err := ctx.env.Call(ctx.contract, address, input, bigGas, value) + gasLeft := ctx.contract.Gas.Int64() + assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) + // fmt.Printf("Gas left %d\n", gasLeft) if err == nil { - copy(outData, out) - return true + copy(output, ret) + assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) + // This can happen for precompiled contracts: + // if (gasLeft == gas) { + // assert(len(output) == 0, fmt.Sprintf("Non-zero output: %d %x", len(output), output)) + // } + return gasLeft + } + return gasLeft | callFailure + case C.EVM_CALLCODE: + // fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, address, value) + ctx.contract.Gas.SetInt64(0) + ret, err := ctx.env.CallCode(ctx.contract, address, input, bigGas, value) + gasLeft := ctx.contract.Gas.Int64() + assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) + // fmt.Printf("Gas left %d\n", gasLeft) + if err == nil { + copy(output, ret) + return gasLeft + } else { + // fmt.Printf("Error: %v\n", err) + } + return gasLeft | callFailure + case C.EVM_DELEGATECALL: + // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) + ctx.contract.Gas.SetInt64(0) + ret, err := ctx.env.DelegateCall(ctx.contract, address, input, bigGas) + gasLeft := ctx.contract.Gas.Int64() + assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) + // fmt.Printf("Gas left %d\n", gasLeft) + if err == nil { + copy(output, ret) + return gasLeft + } + return gasLeft | callFailure + case C.EVM_CREATE: + // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) + ctx.contract.Gas.SetInt64(0) + _, addr, err := ctx.env.Create(ctx.contract, input, bigGas, value) + gasLeft := ctx.contract.Gas.Int64() + assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) + if (ctx.env.ChainConfig().IsHomestead(ctx.env.BlockNumber) && err == CodeStoreOutOfGasError) || + (err != nil && err != CodeStoreOutOfGasError) { + return callFailure + } else { + copy(output, addr[:]) + return gasLeft + } + } + return 0 +} + +func ptr(bytes []byte) *C.uint8_t { + header := *(*reflect.SliceHeader)(unsafe.Pointer(&bytes)) + return (*C.uint8_t)(unsafe.Pointer(header.Data)) +} + +func getMode(env *Environment) C.enum_evm_mode { + n := env.BlockNumber + if env.ChainConfig().IsEIP158(n) { + return C.EVM_CLEARING + } + if env.ChainConfig().IsEIP150(n) { + return C.EVM_ANTI_DOS + } + if env.ChainConfig().IsHomestead(n) { + return C.EVM_HOMESTEAD + } + return C.EVM_FRONTIER +} + +func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) { + evm.env.Depth++ + defer func() { evm.env.Depth-- }() + + if contract.CodeAddr != nil { + if p := Precompiled[contract.CodeAddr.Str()]; p != nil { + return evm.RunPrecompiled(p, input, contract) } } - return false + // Don't bother with the execution if there's no code. + if len(contract.Code) == 0 { + return nil, nil + } + + code := contract.Code + codePtr := (*C.uint8_t)(unsafe.Pointer(&code[0])) + codeSize := C.size_t(len(code)) + codeHash := HashToEvmc(crypto.Keccak256Hash(code)) + gas := C.int64_t(contract.Gas.Int64()) + inputPtr := ptr(input) + inputLen := C.size_t(len(input)) + value := BigToEvmc(contract.value) + mode := getMode(evm.env) + // fmt.Printf("EVMJIT pre Run (gas %d %d mode: %d, env: %d) %x\n", contract.Gas, gas, mode, env, evm.contract.Address()) + + // Create context for this execution. + ctxId := pinCtx(&EVMCContext{contract, evm.env}) + + r := C.evm_execute(evm.jit, unsafe.Pointer(ctxId), mode, codeHash, codePtr, codeSize, gas, inputPtr, inputLen, value) + + unpinCtx(ctxId) + + // fmt.Printf("EVMJIT Run %d %d %x\n", r.code, r.gas_left, evm.contract.Address()) + if r.gas_left > gas { + panic("OOPS") + } + contract.Gas.SetInt64(int64(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.code != 0 { + // FIXME: Produce better error messages. + err = OutOfGasError + } + + if r.internal_memory != nil { + C.free(r.internal_memory) + } + return output, err } -//export env_create -func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) { - vm := (*JitVm)(_vm) - - value := llvm2big((*i256)(_value)) - initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance - result := (*i256)(_result) - *result = i256{} - - gas := big.NewInt(*_gas) - ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value) - if suberr == nil { - dataGas := big.NewInt(int64(len(ret))) // TODO: Not the best design. env.Create can do it, it has the reference to gas counter - dataGas.Mul(dataGas, params.CreateDataGas) - gas.Sub(gas, dataGas) - *result = hash2llvm(ref.Address()) +func (evm *EVMJIT) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { + // fmt.Printf("PRECOMPILED %x\n", *contract.CodeAddr) + gas := p.Gas(len(input)) + if contract.UseGas(gas) { + ret = p.Call(input) + return ret, nil + } else { + return nil, OutOfGasError } - *_gas = gas.Int64() } - -//export env_log -func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) { - vm := (*JitVm)(_vm) - - data := C.GoBytes(dataPtr, C.int(dataLen)) - - topics := make([][]byte, 0, 4) - if _topic1 != nil { - topics = append(topics, llvm2hash((*i256)(_topic1))) - } - if _topic2 != nil { - topics = append(topics, llvm2hash((*i256)(_topic2))) - } - if _topic3 != nil { - topics = append(topics, llvm2hash((*i256)(_topic3))) - } - if _topic4 != nil { - topics = append(topics, llvm2hash((*i256)(_topic4))) - } - - vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64())) -} - -//export env_extcode -func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte { - vm := (*JitVm)(_vm) - addr := llvm2hash((*i256)(_addr)) - code := vm.Env().State().GetCode(addr) - *o_size = uint64(len(code)) - return getDataPtr(code) -}*/ From 9391a910b69b8842ee2355962c2554addf41eb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 22 Dec 2016 14:12:48 +0100 Subject: [PATCH 02/29] core/vm: EVMJIT: improve callbacks --- core/vm/vm_jit.go | 94 ++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 66 deletions(-) diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go index cbebb67080..c5706fa29f 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/vm_jit.go @@ -21,56 +21,20 @@ package vm #include #include -static union evm_variant query_gateway(struct evm_env* env, - enum evm_query_key key, - union evm_variant arg) -{ - void query(void*, size_t, int, void*); - - union evm_variant result; - query(&result, (size_t)env, key, &arg); - return result; -} - -extern void update(void* env, int key, void* arg1, void* arg2); -static void update_gateway(struct evm_env* env, - enum evm_update_key key, - union evm_variant arg1, - union evm_variant arg2) -{ - update(env, key, &arg1, &arg2); -} - -typedef long long int64; - -extern int64 call( - void* env, - int kind, - int64 gas, - void* address, - void* value, - void* input, - size_t input_size, - void* output, - size_t output_size); -static int64_t call_gateway( - struct evm_env* env, - enum evm_call_kind kind, - int64_t gas, - struct evm_uint160be address, - struct evm_uint256be value, - uint8_t const* input, - size_t input_size, - uint8_t* output, - size_t output_size) -{ - return call(env, kind, gas, &address, &value, (void*)input, input_size, output, output_size); -} static struct evm_instance* new_evmjit() { + // Declare exported Go functions. The are ABI compatible with C callbacks + // but differ by type names and const pointers. + void query(void*, size_t, int, void*); + void update(size_t env, int key, void* arg1, void* arg2); + long long call(void* env, int kind, long long gas, void* address, + void* value, void* input, size_t input_size, void* output, + size_t output_size); + struct evm_factory factory = evmjit_get_factory(); - return factory.create(query_gateway, update_gateway, call_gateway); + return factory.create((evm_query_fn)query, (evm_update_fn)update, + (evm_call_fn)call); } static struct evm_result evm_execute(struct evm_instance* instance, @@ -183,44 +147,39 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe // Or as pointer to int64. pInt64Result := (*int64)(pResult) - // Copy the argument to [32]byte array. - arg := *(*[32]byte)(pArg) - // Get the execution context. ctx := getCtx(ctxIdx) switch key { case C.EVM_SLOAD: + arg := *(*[32]byte)(pArg) val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg) copy(result, val[:]) // fmt.Printf("EVMJIT SLOAD %x : %x\n", arg, result) case C.EVM_ADDRESS: addr := ctx.contract.Address() copy(result[12:], addr[:]) - // fmt.Printf("ADDRESS %x : %x\n", addr, result[12:]) case C.EVM_CALLER: addr := ctx.contract.Caller() copy(result[12:], addr[:]) case C.EVM_ORIGIN: - addr := ctx.env.Origin - copy(result[12:], addr[:]) + copy(result[12:], ctx.env.Origin[:]) case C.EVM_GAS_PRICE: val := common.BigToHash(ctx.env.GasPrice) copy(result, val[:]) case C.EVM_COINBASE: - addr := ctx.env.Coinbase - copy(result[12:], addr[:]) + copy(result[12:], ctx.env.Coinbase[:]) case C.EVM_DIFFICULTY: val := common.BigToHash(ctx.env.Difficulty) copy(result, val[:]) case C.EVM_GAS_LIMIT: *pInt64Result = ctx.env.GasLimit.Int64() - // fmt.Printf("GASLIMIT %d %d\n", *pInt64Result, ctx.env.GasLimit()) case C.EVM_NUMBER: *pInt64Result = ctx.env.BlockNumber.Int64() case C.EVM_TIMESTAMP: *pInt64Result = ctx.env.Time.Int64() case C.EVM_CODE_BY_ADDRESS: + arg := GoByteSlice(pArg, 32) var addr common.Address copy(addr[:], arg[12:]) code := ctx.env.StateDB.GetCode(addr) @@ -229,12 +188,14 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe pResAsMemRef.len = len(code) // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) case C.EVM_CODE_SIZE: + arg := GoByteSlice(pArg, 32) var addr common.Address copy(addr[:], arg[12:]) pInt64Result := (*int64)(pResult) *pInt64Result = int64(ctx.env.StateDB.GetCodeSize(addr)) // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) case C.EVM_BALANCE: + arg := GoByteSlice(pArg, 32) var addr common.Address copy(addr[:], arg[12:]) balance := ctx.env.StateDB.GetBalance(addr) @@ -251,6 +212,7 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe copy(result, hash[:]) // fmt.Printf("BLOCKHASH %x : %x (%d, %d, %d)\n", result, hash, n, a, b) case C.EVM_ACCOUNT_EXISTS: + arg := GoByteSlice(pArg, 32) var addr common.Address copy(addr[:], arg[12:]) eip158 := ctx.env.ChainConfig().IsEIP158(ctx.env.BlockNumber) @@ -268,22 +230,21 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe *pInt64Result = int64(ctx.env.Depth - 1) default: - // fmt.Printf("Unhandled %d\n", key) + panic(fmt.Sprintf("Unhandled EVM-C query %d\n", key)) } } //export update -func update(pCtx unsafe.Pointer, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { - arg1 := *(*[32]byte)(pArg1) - arg2 := *(*[32]byte)(pArg2) - ctx := getCtx(uintptr(pCtx)) +func update(ctxIdx uintptr, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { + ctx := getCtx(ctxIdx) switch key { case C.EVM_SSTORE: - val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg1) - ctx.env.StateDB.SetState(ctx.contract.Address(), arg1, arg2) - if !common.EmptyHash(val) && common.EmptyHash(arg2) { - // panic("SSTORE REFUND") + key := *(*[32]byte)(pArg1) + newVal := *(*[32]byte)(pArg2) + oldVal := ctx.env.StateDB.GetState(ctx.contract.Address(), key) + ctx.env.StateDB.SetState(ctx.contract.Address(), key, newVal) + if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { ctx.env.StateDB.AddRefund(params.SstoreRefundGas) } // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) @@ -301,13 +262,14 @@ func update(pCtx unsafe.Pointer, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.P log := NewLog(ctx.contract.Address(), topics, data, ctx.env.BlockNumber.Uint64()) ctx.env.StateDB.AddLog(log) case C.EVM_SELFDESTRUCT: + arg := GoByteSlice(pArg1, 32) db := ctx.env.StateDB addr := ctx.contract.Address() if !db.HasSuicided(addr) { db.AddRefund(params.SuicideRefundGas) } balance := db.GetBalance(addr) - beneficiary := common.BytesToAddress(arg1[12:]) + beneficiary := common.BytesToAddress(arg[12:]) db.AddBalance(beneficiary, balance) db.Suicide(addr) } @@ -457,7 +419,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) output := C.GoBytes(unsafe.Pointer(r.output_data), C.int(r.output_size)) if r.code != 0 { - // FIXME: Produce better error messages. + // EVMJIT does not informs about the kind of the EVM expection. err = OutOfGasError } From 4e677b3dc015de6878a15b081c37d675cfe4c8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 22 Dec 2016 20:45:14 +0100 Subject: [PATCH 03/29] core/vm: EVMJIT: fix some pending issues --- core/vm/vm_jit.go | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go index c5706fa29f..d299f0d531 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/vm_jit.go @@ -38,17 +38,17 @@ static struct evm_instance* new_evmjit() } static struct evm_result evm_execute(struct evm_instance* instance, - struct evm_env* env, - enum evm_mode mode, - struct evm_uint256be code_hash, - uint8_t const* code, - size_t code_size, - int64_t gas, - uint8_t const* input, - size_t input_size, - struct evm_uint256be value) + struct evm_env* env, enum evm_mode mode, struct evm_uint256be code_hash, + uint8_t const* code, size_t code_size, int64_t gas, uint8_t const* input, + size_t input_size, struct evm_uint256be value) { - return instance->execute(instance, env, mode, code_hash, code, code_size, gas, input, input_size, value); + return instance->execute(instance, env, mode, code_hash, code, code_size, + gas, input, input_size, value); +} + +static void evm_release_result(struct evm_result* result) +{ + result->release(result); } #cgo CFLAGS: -I/home/chfast/Projects/ethereum/evmjit/include @@ -58,7 +58,6 @@ import "C" import ( "fmt" - "math" "math/big" "reflect" "sync" @@ -294,9 +293,6 @@ func call( output := GoByteSlice(pOutput, outputSize) bigGas := new(big.Int).SetInt64(gas) - // FIXME: C.EVM_CALL_FAILURE not available. - const callFailure = math.MinInt64 - switch kind { case C.EVM_CALL: // fmt.Printf("CALL(gas %d, %x)\n", bigGas, address) @@ -308,13 +304,9 @@ func call( if err == nil { copy(output, ret) assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - // This can happen for precompiled contracts: - // if (gasLeft == gas) { - // assert(len(output) == 0, fmt.Sprintf("Non-zero output: %d %x", len(output), output)) - // } return gasLeft } - return gasLeft | callFailure + return gasLeft | C.EVM_CALL_FAILURE case C.EVM_CALLCODE: // fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, address, value) ctx.contract.Gas.SetInt64(0) @@ -328,7 +320,7 @@ func call( } else { // fmt.Printf("Error: %v\n", err) } - return gasLeft | callFailure + return gasLeft | C.EVM_CALL_FAILURE case C.EVM_DELEGATECALL: // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) ctx.contract.Gas.SetInt64(0) @@ -340,7 +332,7 @@ func call( copy(output, ret) return gasLeft } - return gasLeft | callFailure + return gasLeft | C.EVM_CALL_FAILURE case C.EVM_CREATE: // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) ctx.contract.Gas.SetInt64(0) @@ -349,7 +341,7 @@ func call( assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) if (ctx.env.ChainConfig().IsHomestead(ctx.env.BlockNumber) && err == CodeStoreOutOfGasError) || (err != nil && err != CodeStoreOutOfGasError) { - return callFailure + return C.EVM_CALL_FAILURE } else { copy(output, addr[:]) return gasLeft @@ -423,14 +415,11 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) err = OutOfGasError } - if r.internal_memory != nil { - C.free(r.internal_memory) - } + C.evm_release_result(&r) return output, err } func (evm *EVMJIT) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { - // fmt.Printf("PRECOMPILED %x\n", *contract.CodeAddr) gas := p.Gas(len(input)) if contract.UseGas(gas) { ret = p.Call(input) From 284c0138fbd1ebe2d979b1b7501f72cfc635e93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 20 Jan 2017 22:14:10 +0100 Subject: [PATCH 04/29] core/vm: after merge --- core/vm/environment.go | 6 ++--- core/vm/vm_jit.go | 56 ++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/core/vm/environment.go b/core/vm/environment.go index c19ef464ba..e7b83034dc 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -74,7 +74,7 @@ type EVM struct { vmConfig Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. - interpreter *Interpreter + interpreter *EVMJIT // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 @@ -89,7 +89,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon chainConfig: chainConfig, } - evm.interpreter = NewInterpreter(evm, vmConfig) + evm.interpreter = NewJit(evm, vmConfig) return evm } @@ -325,4 +325,4 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } // Interpreter returns the EVM interpreter -func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } +func (evm *EVM) Interpreter() *EVMJIT { return evm.interpreter } diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go index d299f0d531..de5f06fcbf 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/vm_jit.go @@ -19,7 +19,6 @@ package vm /* #include -#include static struct evm_instance* new_evmjit() @@ -63,6 +62,7 @@ import ( "sync" "unsafe" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -70,15 +70,15 @@ import ( type EVMJIT struct { jit *C.struct_evm_instance - env *Environment + env *EVM } type EVMCContext struct { contract *Contract - env *Environment + env *EVM } -func NewJit(env *Environment, cfg Config) *EVMJIT { +func NewJit(env *EVM, cfg Config) *EVMJIT { // FIXME: Destroy the jit later. return &EVMJIT{C.new_evmjit(), env} } @@ -226,7 +226,7 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe *pInt64Result = exist // fmt.Printf("EXISTS? %x : %v\n", addr, exist) case C.EVM_CALL_DEPTH: - *pInt64Result = int64(ctx.env.Depth - 1) + *pInt64Result = int64(ctx.env.depth - 1) default: panic(fmt.Sprintf("Unhandled EVM-C query %d\n", key)) @@ -258,8 +258,12 @@ func update(ctxIdx uintptr, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointe for i := 0; i < nTopics; i++ { copy(topics[i][:], tData[i*32:(i+1)*32]) } - log := NewLog(ctx.contract.Address(), topics, data, ctx.env.BlockNumber.Uint64()) - ctx.env.StateDB.AddLog(log) + ctx.env.StateDB.AddLog(&types.Log{ + Address: ctx.contract.Address(), + Topics: topics, + Data: data, + BlockNumber: ctx.env.BlockNumber.Uint64(), + }) case C.EVM_SELFDESTRUCT: arg := GoByteSlice(pArg1, 32) db := ctx.env.StateDB @@ -293,6 +297,10 @@ func call( output := GoByteSlice(pOutput, outputSize) bigGas := new(big.Int).SetInt64(gas) + // TODO: For some reason C.EVM_CALL_FAILURE "constant" is not visible + // by go linker. This probably should be reported as cgo bug. + const callFailureFlag int64 = -(1 << 63); + switch kind { case C.EVM_CALL: // fmt.Printf("CALL(gas %d, %x)\n", bigGas, address) @@ -306,7 +314,7 @@ func call( assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) return gasLeft } - return gasLeft | C.EVM_CALL_FAILURE + return gasLeft | callFailureFlag case C.EVM_CALLCODE: // fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, address, value) ctx.contract.Gas.SetInt64(0) @@ -320,7 +328,7 @@ func call( } else { // fmt.Printf("Error: %v\n", err) } - return gasLeft | C.EVM_CALL_FAILURE + return gasLeft | callFailureFlag case C.EVM_DELEGATECALL: // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) ctx.contract.Gas.SetInt64(0) @@ -332,16 +340,16 @@ func call( copy(output, ret) return gasLeft } - return gasLeft | C.EVM_CALL_FAILURE + return gasLeft | callFailureFlag case C.EVM_CREATE: // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) ctx.contract.Gas.SetInt64(0) _, addr, err := ctx.env.Create(ctx.contract, input, bigGas, value) gasLeft := ctx.contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - if (ctx.env.ChainConfig().IsHomestead(ctx.env.BlockNumber) && err == CodeStoreOutOfGasError) || - (err != nil && err != CodeStoreOutOfGasError) { - return C.EVM_CALL_FAILURE + if (ctx.env.ChainConfig().IsHomestead(ctx.env.BlockNumber) && err == ErrCodeStoreOutOfGas) || + (err != nil && err != ErrCodeStoreOutOfGas) { + return callFailureFlag } else { copy(output, addr[:]) return gasLeft @@ -355,7 +363,7 @@ func ptr(bytes []byte) *C.uint8_t { return (*C.uint8_t)(unsafe.Pointer(header.Data)) } -func getMode(env *Environment) C.enum_evm_mode { +func getMode(env *EVM) C.enum_evm_mode { n := env.BlockNumber if env.ChainConfig().IsEIP158(n) { return C.EVM_CLEARING @@ -370,12 +378,12 @@ func getMode(env *Environment) C.enum_evm_mode { } func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) { - evm.env.Depth++ - defer func() { evm.env.Depth-- }() + evm.env.depth++ + defer func() { evm.env.depth-- }() if contract.CodeAddr != nil { - if p := Precompiled[contract.CodeAddr.Str()]; p != nil { - return evm.RunPrecompiled(p, input, contract) + if p := PrecompiledContracts[*contract.CodeAddr]; p != nil { + return RunPrecompiledContract(p, input, contract) } } @@ -412,19 +420,9 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) if r.code != 0 { // EVMJIT does not informs about the kind of the EVM expection. - err = OutOfGasError + err = ErrOutOfGas } C.evm_release_result(&r) return output, err } - -func (evm *EVMJIT) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { - gas := p.Gas(len(input)) - if contract.UseGas(gas) { - ret = p.Call(input) - return ret, nil - } else { - return nil, OutOfGasError - } -} From 2b5f3f2042a85c69c59f5dbd7cd311443e44e332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 22 Jan 2017 11:51:50 +0100 Subject: [PATCH 05/29] core/vm: move vm_jit.go to evmjit.go --- core/vm/{vm_jit.go => evmjit.go} | 33 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) rename core/vm/{vm_jit.go => evmjit.go} (94%) diff --git a/core/vm/vm_jit.go b/core/vm/evmjit.go similarity index 94% rename from core/vm/vm_jit.go rename to core/vm/evmjit.go index de5f06fcbf..ebe1c87852 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/evmjit.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -154,7 +154,7 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe arg := *(*[32]byte)(pArg) val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg) copy(result, val[:]) - // fmt.Printf("EVMJIT SLOAD %x : %x\n", arg, result) + // fmt.Printf("EVMJIT SLOAD %x : %x\n", arg, result) case C.EVM_ADDRESS: addr := ctx.contract.Address() copy(result[12:], addr[:]) @@ -185,14 +185,14 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe pResAsMemRef := (*MemoryRef)(pResult) pResAsMemRef.ptr = ptr(code) pResAsMemRef.len = len(code) - // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) + // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) case C.EVM_CODE_SIZE: arg := GoByteSlice(pArg, 32) var addr common.Address copy(addr[:], arg[12:]) pInt64Result := (*int64)(pResult) *pInt64Result = int64(ctx.env.StateDB.GetCodeSize(addr)) - // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) + // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) case C.EVM_BALANCE: arg := GoByteSlice(pArg, 32) var addr common.Address @@ -209,7 +209,7 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe hash = ctx.env.GetHash(uint64(n)) } copy(result, hash[:]) - // fmt.Printf("BLOCKHASH %x : %x (%d, %d, %d)\n", result, hash, n, a, b) + // fmt.Printf("BLOCKHASH %x : %x (%d, %d, %d)\n", result, hash, n, a, b) case C.EVM_ACCOUNT_EXISTS: arg := GoByteSlice(pArg, 32) var addr common.Address @@ -224,7 +224,7 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe exist = 1 } *pInt64Result = exist - // fmt.Printf("EXISTS? %x : %v\n", addr, exist) + // fmt.Printf("EXISTS? %x : %v\n", addr, exist) case C.EVM_CALL_DEPTH: *pInt64Result = int64(ctx.env.depth - 1) @@ -246,7 +246,7 @@ func update(ctxIdx uintptr, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointe if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { ctx.env.StateDB.AddRefund(params.SstoreRefundGas) } - // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) + // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) case C.EVM_LOG: dataRef := (*MemoryRef)(pArg1) topicsRef := (*MemoryRef)(pArg2) @@ -280,15 +280,15 @@ func update(ctxIdx uintptr, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointe //export call func call( - pCtx unsafe.Pointer, - kind int32, - gas int64, - pAddr unsafe.Pointer, - pValue unsafe.Pointer, - pInput unsafe.Pointer, - inputSize C.size_t, - pOutput unsafe.Pointer, - outputSize C.size_t) int64 { +pCtx unsafe.Pointer, +kind int32, +gas int64, +pAddr unsafe.Pointer, +pValue unsafe.Pointer, +pInput unsafe.Pointer, +inputSize C.size_t, +pOutput unsafe.Pointer, +outputSize C.size_t) int64 { ctx := getCtx(uintptr(pCtx)) address := *(*[20]byte)(pAddr) @@ -426,3 +426,4 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) C.evm_release_result(&r) return output, err } + From fae74919b1437010426d3625f7cbdbfcb431034e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 26 Jan 2017 00:17:31 +0100 Subject: [PATCH 06/29] core/vm: Upgrade EVM-C API to v3 --- core/vm/evmjit.go | 121 ++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index ebe1c87852..8ab64bc1e1 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -25,24 +25,26 @@ static struct evm_instance* new_evmjit() { // Declare exported Go functions. The are ABI compatible with C callbacks // but differ by type names and const pointers. - void query(void*, size_t, int, void*); - void update(size_t env, int key, void* arg1, void* arg2); + void queryState(void*, size_t, int, void* addr, void*); + void updateState(size_t env, int key, void* addr, void* arg1, void* arg2); long long call(void* env, int kind, long long gas, void* address, void* value, void* input, size_t input_size, void* output, size_t output_size); + void getTxCtx(void*, size_t); + void getBlockHash(void*, size_t, long long); struct evm_factory factory = evmjit_get_factory(); - return factory.create((evm_query_fn)query, (evm_update_fn)update, - (evm_call_fn)call); + return factory.create((evm_query_state_fn)queryState, + (evm_update_state_fn)updateState, (evm_call_fn)call, + (evm_get_tx_context_fn)getTxCtx, (evm_get_block_hash_fn)getBlockHash); } static struct evm_result evm_execute(struct evm_instance* instance, struct evm_env* env, enum evm_mode mode, struct evm_uint256be code_hash, - uint8_t const* code, size_t code_size, int64_t gas, uint8_t const* input, - size_t input_size, struct evm_uint256be value) + uint8_t const* code, size_t code_size, struct evm_message msg) { return instance->execute(instance, env, mode, code_hash, code, code_size, - gas, input, input_size, value); + msg); } static void evm_release_result(struct evm_result* result) @@ -116,6 +118,10 @@ func HashToEvmc(hash common.Hash) C.struct_evm_uint256be { return C.struct_evm_uint256be{bytes: *(*[32]C.uint8_t)(unsafe.Pointer(&hash[0]))} } +func AddressToEvmc(addr common.Address) C.struct_evm_uint160be { + return C.struct_evm_uint160be{bytes: *(*[20]C.uint8_t)(unsafe.Pointer(&addr[0]))} +} + func BigToEvmc(i *big.Int) C.struct_evm_uint256be { return HashToEvmc(common.BigToHash(i)) } @@ -139,8 +145,35 @@ func GoByteSlice(data unsafe.Pointer, size C.size_t) []byte { return *(*[]byte)(unsafe.Pointer(&sliceHeader)) } -//export query -func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointer) { +//export getTxCtx +func getTxCtx(pResult unsafe.Pointer, ctxIdx uintptr) { + ctx := getCtx(ctxIdx) + txCtx := (*C.struct_evm_tx_context)(pResult) + txCtx.tx_gas_price = BigToEvmc(ctx.env.GasPrice) + txCtx.tx_origin = AddressToEvmc(ctx.env.Origin) + txCtx.block_coinbase = AddressToEvmc(ctx.env.Coinbase) + txCtx.block_number = C.int64_t(ctx.env.BlockNumber.Int64()) + txCtx.block_timestamp = C.int64_t(ctx.env.Time.Int64()) + txCtx.block_gas_limit = C.int64_t(ctx.env.GasLimit.Int64()) + txCtx.block_difficulty = BigToEvmc(ctx.env.Difficulty) +} + +//export getBlockHash +func getBlockHash(pResult unsafe.Pointer, ctxIdx uintptr, number int64) { + // Represent the result memory as Go slice of 32 bytes. + result := GoByteSlice(pResult, 32) + ctx := getCtx(ctxIdx) + b := ctx.env.BlockNumber.Int64() + a := b - 256 + var hash common.Hash + if number >= a && number < b { + hash = ctx.env.GetHash(uint64(number)) + } + copy(result, hash[:]) +} + +//export queryState +func queryState(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pAddr unsafe.Pointer, pArg unsafe.Pointer) { // Represent the result memory as Go slice of 32 bytes. result := GoByteSlice(pResult, 32) // Or as pointer to int64. @@ -149,71 +182,29 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe // Get the execution context. ctx := getCtx(ctxIdx) + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) + switch key { case C.EVM_SLOAD: arg := *(*[32]byte)(pArg) val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg) copy(result, val[:]) - // fmt.Printf("EVMJIT SLOAD %x : %x\n", arg, result) - case C.EVM_ADDRESS: - addr := ctx.contract.Address() - copy(result[12:], addr[:]) - case C.EVM_CALLER: - addr := ctx.contract.Caller() - copy(result[12:], addr[:]) - case C.EVM_ORIGIN: - copy(result[12:], ctx.env.Origin[:]) - case C.EVM_GAS_PRICE: - val := common.BigToHash(ctx.env.GasPrice) - copy(result, val[:]) - case C.EVM_COINBASE: - copy(result[12:], ctx.env.Coinbase[:]) - case C.EVM_DIFFICULTY: - val := common.BigToHash(ctx.env.Difficulty) - copy(result, val[:]) - case C.EVM_GAS_LIMIT: - *pInt64Result = ctx.env.GasLimit.Int64() - case C.EVM_NUMBER: - *pInt64Result = ctx.env.BlockNumber.Int64() - case C.EVM_TIMESTAMP: - *pInt64Result = ctx.env.Time.Int64() case C.EVM_CODE_BY_ADDRESS: - arg := GoByteSlice(pArg, 32) - var addr common.Address - copy(addr[:], arg[12:]) code := ctx.env.StateDB.GetCode(addr) pResAsMemRef := (*MemoryRef)(pResult) pResAsMemRef.ptr = ptr(code) pResAsMemRef.len = len(code) // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) case C.EVM_CODE_SIZE: - arg := GoByteSlice(pArg, 32) - var addr common.Address - copy(addr[:], arg[12:]) pInt64Result := (*int64)(pResult) *pInt64Result = int64(ctx.env.StateDB.GetCodeSize(addr)) // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) case C.EVM_BALANCE: - arg := GoByteSlice(pArg, 32) - var addr common.Address - copy(addr[:], arg[12:]) balance := ctx.env.StateDB.GetBalance(addr) val := common.BigToHash(balance) copy(result, val[:]) - case C.EVM_BLOCKHASH: - n := *(*int64)(pArg) - b := ctx.env.BlockNumber.Int64() - a := b - 256 - var hash common.Hash - if n >= a && n < b { - hash = ctx.env.GetHash(uint64(n)) - } - copy(result, hash[:]) - // fmt.Printf("BLOCKHASH %x : %x (%d, %d, %d)\n", result, hash, n, a, b) case C.EVM_ACCOUNT_EXISTS: - arg := GoByteSlice(pArg, 32) - var addr common.Address - copy(addr[:], arg[12:]) eip158 := ctx.env.ChainConfig().IsEIP158(ctx.env.BlockNumber) var exist int64 if eip158 { @@ -224,17 +215,13 @@ func query(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pArg unsafe.Pointe exist = 1 } *pInt64Result = exist - // fmt.Printf("EXISTS? %x : %v\n", addr, exist) - case C.EVM_CALL_DEPTH: - *pInt64Result = int64(ctx.env.depth - 1) - default: panic(fmt.Sprintf("Unhandled EVM-C query %d\n", key)) } } -//export update -func update(ctxIdx uintptr, key int32, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { +//export updateState +func updateState(ctxIdx uintptr, key int32, pAddr unsafe.Pointer, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { ctx := getCtx(ctxIdx) switch key { @@ -404,11 +391,19 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) // fmt.Printf("EVMJIT pre Run (gas %d %d mode: %d, env: %d) %x\n", contract.Gas, gas, mode, env, evm.contract.Address()) // Create context for this execution. - ctxId := pinCtx(&EVMCContext{contract, evm.env}) + ctxIdx := pinCtx(&EVMCContext{contract, evm.env}) - r := C.evm_execute(evm.jit, unsafe.Pointer(ctxId), mode, codeHash, codePtr, codeSize, gas, inputPtr, inputLen, value) + var msg C.struct_evm_message + msg.address = AddressToEvmc(contract.Address()) + msg.sender = AddressToEvmc(contract.Caller()) + msg.value = value + msg.input = inputPtr + msg.input_size = inputLen + msg.gas = gas + msg.depth = C.int32_t(evm.env.depth - 1) + r := C.evm_execute(evm.jit, unsafe.Pointer(ctxIdx), mode, codeHash, codePtr, codeSize, msg) - unpinCtx(ctxId) + unpinCtx(ctxIdx) // fmt.Printf("EVMJIT Run %d %d %x\n", r.code, r.gas_left, evm.contract.Address()) if r.gas_left > gas { From c030867ccfa7e13be7124c101684ddf289099364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 26 Jan 2017 00:45:34 +0100 Subject: [PATCH 07/29] core/vm/evmjit: Do not use full env context if not needed --- core/vm/evmjit.go | 66 ++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 8ab64bc1e1..45b0fd854f 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -108,10 +108,14 @@ func unpinCtx(id uintptr) { contextMapMu.Unlock() } -func getCtx(id uintptr) *EVMCContext { +func getCtx(idx uintptr) *EVMCContext { contextMapMu.Lock() defer contextMapMu.Unlock() - return contextMap[id] + return contextMap[idx] +} + +func getEnv(idx uintptr) *EVM { + return getCtx(idx).env } func HashToEvmc(hash common.Hash) C.struct_evm_uint256be { @@ -147,27 +151,27 @@ func GoByteSlice(data unsafe.Pointer, size C.size_t) []byte { //export getTxCtx func getTxCtx(pResult unsafe.Pointer, ctxIdx uintptr) { - ctx := getCtx(ctxIdx) + env := getEnv(ctxIdx) txCtx := (*C.struct_evm_tx_context)(pResult) - txCtx.tx_gas_price = BigToEvmc(ctx.env.GasPrice) - txCtx.tx_origin = AddressToEvmc(ctx.env.Origin) - txCtx.block_coinbase = AddressToEvmc(ctx.env.Coinbase) - txCtx.block_number = C.int64_t(ctx.env.BlockNumber.Int64()) - txCtx.block_timestamp = C.int64_t(ctx.env.Time.Int64()) - txCtx.block_gas_limit = C.int64_t(ctx.env.GasLimit.Int64()) - txCtx.block_difficulty = BigToEvmc(ctx.env.Difficulty) + 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_difficulty = BigToEvmc(env.Difficulty) } //export getBlockHash func getBlockHash(pResult unsafe.Pointer, ctxIdx uintptr, number int64) { // Represent the result memory as Go slice of 32 bytes. result := GoByteSlice(pResult, 32) - ctx := getCtx(ctxIdx) - b := ctx.env.BlockNumber.Int64() + env := getEnv(ctxIdx) + b := env.BlockNumber.Int64() a := b - 256 var hash common.Hash if number >= a && number < b { - hash = ctx.env.GetHash(uint64(number)) + hash = env.GetHash(uint64(number)) } copy(result, hash[:]) } @@ -180,7 +184,7 @@ func queryState(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pAddr unsafe. pInt64Result := (*int64)(pResult) // Get the execution context. - ctx := getCtx(ctxIdx) + env := getEnv(ctxIdx) var addr common.Address copy(addr[:], GoByteSlice(pAddr, 20)) @@ -188,30 +192,30 @@ func queryState(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pAddr unsafe. switch key { case C.EVM_SLOAD: arg := *(*[32]byte)(pArg) - val := ctx.env.StateDB.GetState(ctx.contract.Address(), arg) + val := env.StateDB.GetState(addr, arg) copy(result, val[:]) case C.EVM_CODE_BY_ADDRESS: - code := ctx.env.StateDB.GetCode(addr) + code := env.StateDB.GetCode(addr) pResAsMemRef := (*MemoryRef)(pResult) pResAsMemRef.ptr = ptr(code) pResAsMemRef.len = len(code) // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) case C.EVM_CODE_SIZE: pInt64Result := (*int64)(pResult) - *pInt64Result = int64(ctx.env.StateDB.GetCodeSize(addr)) + *pInt64Result = int64(env.StateDB.GetCodeSize(addr)) // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) case C.EVM_BALANCE: - balance := ctx.env.StateDB.GetBalance(addr) + balance := env.StateDB.GetBalance(addr) val := common.BigToHash(balance) copy(result, val[:]) case C.EVM_ACCOUNT_EXISTS: - eip158 := ctx.env.ChainConfig().IsEIP158(ctx.env.BlockNumber) + eip158 := env.ChainConfig().IsEIP158(env.BlockNumber) var exist int64 if eip158 { - if !ctx.env.StateDB.Empty(addr) { + if !env.StateDB.Empty(addr) { exist = 1 } - } else if ctx.env.StateDB.Exist(addr) { + } else if env.StateDB.Exist(addr) { exist = 1 } *pInt64Result = exist @@ -222,16 +226,19 @@ func queryState(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pAddr unsafe. //export updateState func updateState(ctxIdx uintptr, key int32, pAddr unsafe.Pointer, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { - ctx := getCtx(ctxIdx) + env := getEnv(ctxIdx) + + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) switch key { case C.EVM_SSTORE: key := *(*[32]byte)(pArg1) newVal := *(*[32]byte)(pArg2) - oldVal := ctx.env.StateDB.GetState(ctx.contract.Address(), key) - ctx.env.StateDB.SetState(ctx.contract.Address(), key, newVal) + oldVal := env.StateDB.GetState(addr, key) + env.StateDB.SetState(addr, key, newVal) if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { - ctx.env.StateDB.AddRefund(params.SstoreRefundGas) + env.StateDB.AddRefund(params.SstoreRefundGas) } // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) case C.EVM_LOG: @@ -245,16 +252,15 @@ func updateState(ctxIdx uintptr, key int32, pAddr unsafe.Pointer, pArg1 unsafe.P for i := 0; i < nTopics; i++ { copy(topics[i][:], tData[i*32:(i+1)*32]) } - ctx.env.StateDB.AddLog(&types.Log{ - Address: ctx.contract.Address(), + env.StateDB.AddLog(&types.Log{ + Address: addr, Topics: topics, Data: data, - BlockNumber: ctx.env.BlockNumber.Uint64(), + BlockNumber: env.BlockNumber.Uint64(), }) case C.EVM_SELFDESTRUCT: arg := GoByteSlice(pArg1, 32) - db := ctx.env.StateDB - addr := ctx.contract.Address() + db := env.StateDB if !db.HasSuicided(addr) { db.AddRefund(params.SuicideRefundGas) } From 88d61235e3879e1506dcfd37b2a16ecb8496a305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 24 Oct 2017 18:39:50 +0200 Subject: [PATCH 08/29] evmjit: Update EVM-C callback functions --- core/vm/evmjit.go | 330 ++++++++++++++++++++++++++-------------------- 1 file changed, 190 insertions(+), 140 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 45b0fd854f..dbd9da8f71 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -21,30 +21,44 @@ package vm #include -static struct evm_instance* new_evmjit() +static const struct evm_context_fn_table* get_context_fn_table() { - // Declare exported Go functions. The are ABI compatible with C callbacks - // but differ by type names and const pointers. - void queryState(void*, size_t, int, void* addr, void*); - void updateState(size_t env, int key, void* addr, void* arg1, void* arg2); - long long call(void* env, int kind, long long gas, void* address, - void* value, void* input, size_t input_size, void* output, - size_t output_size); - void getTxCtx(void*, size_t); - void getBlockHash(void*, size_t, long long); + int account_exists(void*, void*); + void get_storage(struct evm_uint256be*, void*, void*, struct evm_uint256be*); + void set_storage(void*, void*, void*, void*); + void get_balance(void*, void*, void*); + size_t get_code(unsigned char**, void*, void*); + void selfdestruct(void*, void*, void*); + void getTxCtx(void*, void*); + void getBlockHash(void*, void*, long long); + void set_logs(void*, void*, void*, size_t, void*, size_t); - struct evm_factory factory = evmjit_get_factory(); - return factory.create((evm_query_state_fn)queryState, - (evm_update_state_fn)updateState, (evm_call_fn)call, - (evm_get_tx_context_fn)getTxCtx, (evm_get_block_hash_fn)getBlockHash); + 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, + NULL, + (evm_get_tx_context_fn) getTxCtx, + (evm_get_block_hash_fn) getBlockHash, + (evm_log_fn) set_logs + }; + return &fn_table; } -static struct evm_result evm_execute(struct evm_instance* instance, - struct evm_env* env, enum evm_mode mode, struct evm_uint256be code_hash, - uint8_t const* code, size_t code_size, struct evm_message msg) + +static struct evm_result evm_execute( + struct evm_instance* instance, + struct evm_context* context, + enum evm_revision rev, + const struct evm_message* msg, + uint8_t const* code, + size_t code_size +) { - return instance->execute(instance, env, mode, code_hash, code, code_size, - msg); + return instance->execute(instance, context, rev, msg, code, code_size); } static void evm_release_result(struct evm_result* result) @@ -66,7 +80,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" + // "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -80,20 +94,25 @@ type EVMCContext struct { env *EVM } +type ContextWrapper struct { + c C.struct_evm_context + index int +} + func NewJit(env *EVM, cfg Config) *EVMJIT { // FIXME: Destroy the jit later. - return &EVMJIT{C.new_evmjit(), env} + return &EVMJIT{C.evmjit_create(), env} } -var contextMap = make(map[uintptr]*EVMCContext) +var contextMap = make(map[int]*EVMCContext) var contextMapMu sync.Mutex -func pinCtx(ctx *EVMCContext) uintptr { +func pinCtx(ctx *EVMCContext) int { contextMapMu.Lock() // Find empty slot in the map starting from the map length. - id := uintptr(len(contextMap)) + id := len(contextMap) for contextMap[id] != nil { id++ } @@ -102,28 +121,29 @@ func pinCtx(ctx *EVMCContext) uintptr { return id } -func unpinCtx(id uintptr) { +func unpinCtx(id int) { contextMapMu.Lock() delete(contextMap, id) contextMapMu.Unlock() } -func getCtx(idx uintptr) *EVMCContext { +func getCtx(idx int) *EVMCContext { contextMapMu.Lock() defer contextMapMu.Unlock() return contextMap[idx] } -func getEnv(idx uintptr) *EVM { - return getCtx(idx).env +func getEnv(pCtx unsafe.Pointer) *EVM { + ctxWrapper := (*ContextWrapper)(pCtx) + return getCtx(ctxWrapper.index).env } func HashToEvmc(hash common.Hash) C.struct_evm_uint256be { - return C.struct_evm_uint256be{bytes: *(*[32]C.uint8_t)(unsafe.Pointer(&hash[0]))} + return C.struct_evm_uint256be{*(*[32]C.uint8_t)(unsafe.Pointer(&hash[0]))} } -func AddressToEvmc(addr common.Address) C.struct_evm_uint160be { - return C.struct_evm_uint160be{bytes: *(*[20]C.uint8_t)(unsafe.Pointer(&addr[0]))} +func AddressToEvmc(addr common.Address) C.struct_evm_address { + return C.struct_evm_address{*(*[20]C.uint8_t)(unsafe.Pointer(&addr[0]))} } func BigToEvmc(i *big.Int) C.struct_evm_uint256be { @@ -149,9 +169,112 @@ func GoByteSlice(data unsafe.Pointer, size C.size_t) []byte { return *(*[]byte)(unsafe.Pointer(&sliceHeader)) } +func EvmcHashToSlice(uint256 *C.struct_evm_uint256be) []byte { + return GoByteSlice(unsafe.Pointer(uint256), 32) +} + +//export account_exists +func account_exists(pCtx unsafe.Pointer, pAddr unsafe.Pointer) C.int { + // Get the execution context. + env := getEnv(pCtx) + + arg := GoByteSlice(pAddr, 20) + var addr common.Address + copy(addr[:], arg[:]) + eip158 := env.ChainConfig().IsEIP158(env.BlockNumber) + var exist C.int + if eip158 { + if !env.StateDB.Empty(addr) { + exist = 1 + } + } else if env.StateDB.Exist(addr) { + exist = 1 + } + // fmt.Printf("EXISTS? %x : %v\n", addr, exist) + return exist +} + +//export get_storage +func get_storage(pResult *C.struct_evm_uint256be, pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg *C.struct_evm_uint256be) { + result := EvmcHashToSlice(pResult) + env := getEnv(pCtx) + + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) + + arg := *(*[32]byte)(unsafe.Pointer(pArg)) + val := env.StateDB.GetState(addr, arg) + copy(result, val[:]) +} + +//export set_storage +func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { + env := getEnv(pCtx) + + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) + + key := *(*[32]byte)(pArg1) + 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(params.SstoreRefundGas) + } + // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) +} + +//export get_balance +func get_balance(pResult unsafe.Pointer, pCtx unsafe.Pointer, pAddr unsafe.Pointer) { + result := GoByteSlice(pResult, 32) + env := getEnv(pCtx) + + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) + balance := env.StateDB.GetBalance(addr) + val := common.BigToHash(balance) + copy(result, val[:]) +} + +//export get_code +func get_code(ppCode **C.uint8_t, 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) + + // 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 selfdestruct +func selfdestruct(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg unsafe.Pointer) { + env := getEnv(pCtx) + + var addr common.Address + copy(addr[:], GoByteSlice(pAddr, 20)) + + var beneficiary common.Address + copy(beneficiary[:], GoByteSlice(pArg, 20)) + + db := env.StateDB + if !db.HasSuicided(addr) { + db.AddRefund(params.SuicideRefundGas) + } + balance := db.GetBalance(addr) + db.AddBalance(beneficiary, balance) + db.Suicide(addr) +} + //export getTxCtx -func getTxCtx(pResult unsafe.Pointer, ctxIdx uintptr) { - env := getEnv(ctxIdx) +func getTxCtx(pResult unsafe.Pointer, pCtx unsafe.Pointer) { + env := getEnv(pCtx) txCtx := (*C.struct_evm_tx_context)(pResult) txCtx.tx_gas_price = BigToEvmc(env.GasPrice) txCtx.tx_origin = AddressToEvmc(env.Origin) @@ -163,10 +286,10 @@ func getTxCtx(pResult unsafe.Pointer, ctxIdx uintptr) { } //export getBlockHash -func getBlockHash(pResult unsafe.Pointer, ctxIdx uintptr, number int64) { +func getBlockHash(pResult unsafe.Pointer, pCtx unsafe.Pointer, number int64) { // Represent the result memory as Go slice of 32 bytes. result := GoByteSlice(pResult, 32) - env := getEnv(ctxIdx) + env := getEnv(pCtx) b := env.BlockNumber.Int64() a := b - 256 var hash common.Hash @@ -176,99 +299,27 @@ func getBlockHash(pResult unsafe.Pointer, ctxIdx uintptr, number int64) { copy(result, hash[:]) } -//export queryState -func queryState(pResult unsafe.Pointer, ctxIdx uintptr, key int32, pAddr unsafe.Pointer, pArg unsafe.Pointer) { - // Represent the result memory as Go slice of 32 bytes. - result := GoByteSlice(pResult, 32) - // Or as pointer to int64. - pInt64Result := (*int64)(pResult) - - // Get the execution context. - env := getEnv(ctxIdx) +//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) { + env := getEnv(pCtx) var addr common.Address copy(addr[:], GoByteSlice(pAddr, 20)) - switch key { - case C.EVM_SLOAD: - arg := *(*[32]byte)(pArg) - val := env.StateDB.GetState(addr, arg) - copy(result, val[:]) - case C.EVM_CODE_BY_ADDRESS: - code := env.StateDB.GetCode(addr) - pResAsMemRef := (*MemoryRef)(pResult) - pResAsMemRef.ptr = ptr(code) - pResAsMemRef.len = len(code) - // fmt.Printf("EXTCODE %x : %d\n", addr, pResAsMemRef.len) - case C.EVM_CODE_SIZE: - pInt64Result := (*int64)(pResult) - *pInt64Result = int64(env.StateDB.GetCodeSize(addr)) - // fmt.Printf("EXTCODESIZE %x : %d\n", addr, *pInt64Result) - case C.EVM_BALANCE: - balance := env.StateDB.GetBalance(addr) - val := common.BigToHash(balance) - copy(result, val[:]) - case C.EVM_ACCOUNT_EXISTS: - eip158 := env.ChainConfig().IsEIP158(env.BlockNumber) - var exist int64 - if eip158 { - if !env.StateDB.Empty(addr) { - exist = 1 - } - } else if env.StateDB.Exist(addr) { - exist = 1 - } - *pInt64Result = exist - default: - panic(fmt.Sprintf("Unhandled EVM-C query %d\n", key)) - } -} - -//export updateState -func updateState(ctxIdx uintptr, key int32, pAddr unsafe.Pointer, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { - env := getEnv(ctxIdx) - - var addr common.Address - copy(addr[:], GoByteSlice(pAddr, 20)) - - switch key { - case C.EVM_SSTORE: - key := *(*[32]byte)(pArg1) - 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(params.SstoreRefundGas) - } - // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) - case C.EVM_LOG: - dataRef := (*MemoryRef)(pArg1) - topicsRef := (*MemoryRef)(pArg2) - data := C.GoBytes(unsafe.Pointer(dataRef.ptr), C.int(dataRef.len)) - // FIXME: Avoid double copy of topics. - tData := C.GoBytes(unsafe.Pointer(topicsRef.ptr), C.int(topicsRef.len)) - nTopics := topicsRef.len / 32 - topics := make([]common.Hash, nTopics) - for i := 0; i < nTopics; i++ { - copy(topics[i][:], tData[i*32:(i+1)*32]) - } - env.StateDB.AddLog(&types.Log{ - Address: addr, - Topics: topics, - Data: data, - BlockNumber: env.BlockNumber.Uint64(), - }) - case C.EVM_SELFDESTRUCT: - arg := GoByteSlice(pArg1, 32) - db := env.StateDB - if !db.HasSuicided(addr) { - db.AddRefund(params.SuicideRefundGas) - } - balance := db.GetBalance(addr) - beneficiary := common.BytesToAddress(arg[12:]) - db.AddBalance(beneficiary, balance) - db.Suicide(addr) + data := C.GoBytes(pData, C.int(dataSize)) + tData := C.GoBytes(pTopics, C.int(topicsCount * 32)) + + nTopics := int(topicsCount) + topics := make([]common.Hash, nTopics) + for i := 0; i < nTopics; i++ { + copy(topics[i][:], tData[i*32:(i+1)*32]) } + env.StateDB.AddLog(&types.Log{ + Address: addr, + Topics: topics, + Data: data, + BlockNumber: env.BlockNumber.Uint64(), + }) } //export call @@ -283,7 +334,8 @@ inputSize C.size_t, pOutput unsafe.Pointer, outputSize C.size_t) int64 { - ctx := getCtx(uintptr(pCtx)) + ctxWrapper := (*ContextWrapper)(pCtx) + ctx := getCtx(ctxWrapper.index) address := *(*[20]byte)(pAddr) value := (*(*common.Hash)(pValue)).Big() input := GoByteSlice(pInput, inputSize) @@ -356,13 +408,13 @@ func ptr(bytes []byte) *C.uint8_t { return (*C.uint8_t)(unsafe.Pointer(header.Data)) } -func getMode(env *EVM) C.enum_evm_mode { +func getRevision(env *EVM) C.enum_evm_revision { n := env.BlockNumber if env.ChainConfig().IsEIP158(n) { - return C.EVM_CLEARING + return C.EVM_SPURIOUS_DRAGON } if env.ChainConfig().IsEIP150(n) { - return C.EVM_ANTI_DOS + return C.EVM_TANGERINE_WHISTLE } if env.ChainConfig().IsHomestead(n) { return C.EVM_HOMESTEAD @@ -388,28 +440,27 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) code := contract.Code codePtr := (*C.uint8_t)(unsafe.Pointer(&code[0])) codeSize := C.size_t(len(code)) - codeHash := HashToEvmc(crypto.Keccak256Hash(code)) gas := C.int64_t(contract.Gas.Int64()) - inputPtr := ptr(input) - inputLen := C.size_t(len(input)) - value := BigToEvmc(contract.value) - mode := getMode(evm.env) + rev := getRevision(evm.env) // fmt.Printf("EVMJIT pre Run (gas %d %d mode: %d, env: %d) %x\n", contract.Gas, gas, mode, env, evm.contract.Address()) // Create context for this execution. - ctxIdx := pinCtx(&EVMCContext{contract, evm.env}) + wrapper := ContextWrapper{} + 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()) msg.sender = AddressToEvmc(contract.Caller()) - msg.value = value - msg.input = inputPtr - msg.input_size = inputLen + msg.value = BigToEvmc(contract.value) + msg.input = ptr(input) + msg.input_size = C.size_t(len(input)) msg.gas = gas msg.depth = C.int32_t(evm.env.depth - 1) - r := C.evm_execute(evm.jit, unsafe.Pointer(ctxIdx), mode, codeHash, codePtr, codeSize, msg) - unpinCtx(ctxIdx) + r := C.evm_execute(evm.jit, &wrapper.c, rev, &msg, codePtr, codeSize) + + unpinCtx(wrapper.index) // fmt.Printf("EVMJIT Run %d %d %x\n", r.code, r.gas_left, evm.contract.Address()) if r.gas_left > gas { @@ -419,7 +470,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) // fmt.Printf("Gas left: %d\n", contract.Gas) output := C.GoBytes(unsafe.Pointer(r.output_data), C.int(r.output_size)) - if r.code != 0 { + if r.status_code != 0 { // EVMJIT does not informs about the kind of the EVM expection. err = ErrOutOfGas } @@ -427,4 +478,3 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) C.evm_release_result(&r) return output, err } - From bc9a71e7ab234826875824556c3dd450c5b4b436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 Dec 2017 15:17:16 +0100 Subject: [PATCH 09/29] evmjit: Correctly release result --- core/vm/evmjit.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index dbd9da8f71..e61117bee6 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -80,7 +80,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/common" - // "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -457,6 +457,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) msg.input_size = C.size_t(len(input)) msg.gas = gas msg.depth = C.int32_t(evm.env.depth - 1) + msg.code_hash = HashToEvmc(crypto.Keccak256Hash(code)) r := C.evm_execute(evm.jit, &wrapper.c, rev, &msg, codePtr, codeSize) @@ -475,6 +476,9 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) err = ErrOutOfGas } - C.evm_release_result(&r) + if r.release != nil { + C.evm_release_result(&r) + } + return output, err } From db67881a29519bce527d07668aff4caae27f905c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 Dec 2017 16:33:17 +0100 Subject: [PATCH 10/29] evmjit: Add some debug logs --- core/vm/evmjit.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index e61117bee6..c900ad7578 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -190,12 +190,13 @@ func account_exists(pCtx unsafe.Pointer, pAddr unsafe.Pointer) C.int { } else if env.StateDB.Exist(addr) { exist = 1 } - // fmt.Printf("EXISTS? %x : %v\n", addr, exist) + fmt.Printf("EXISTS? %x : %v\n", addr, exist) return exist } //export get_storage func get_storage(pResult *C.struct_evm_uint256be, pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg *C.struct_evm_uint256be) { + fmt.Printf("SLOAD\n") result := EvmcHashToSlice(pResult) env := getEnv(pCtx) @@ -205,10 +206,12 @@ func get_storage(pResult *C.struct_evm_uint256be, pCtx unsafe.Pointer, pAddr uns arg := *(*[32]byte)(unsafe.Pointer(pArg)) val := env.StateDB.GetState(addr, arg) copy(result, val[:]) + fmt.Printf("SLOAD %x : %v\n", addr, val) } //export set_storage func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { + fmt.Printf("SSTORE\n") env := getEnv(pCtx) var addr common.Address @@ -221,11 +224,12 @@ func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { env.StateDB.AddRefund(params.SstoreRefundGas) } - // fmt.Printf("EVMJIT STORE %x : %x [%x, %d]\n", arg1, arg2, ctx.contract.Address(), int(uintptr(pEnv))) + fmt.Printf("EVMJIT STORE %x : %x [%x]\n", key, newVal, addr) } //export get_balance func get_balance(pResult unsafe.Pointer, pCtx unsafe.Pointer, pAddr unsafe.Pointer) { + fmt.Printf("BALANCE\n") result := GoByteSlice(pResult, 32) env := getEnv(pCtx) @@ -234,6 +238,7 @@ func get_balance(pResult unsafe.Pointer, pCtx unsafe.Pointer, pAddr unsafe.Point balance := env.StateDB.GetBalance(addr) val := common.BigToHash(balance) copy(result, val[:]) + fmt.Printf("BALANCE %x : %v\n", addr, balance) } //export get_code @@ -324,15 +329,16 @@ func set_logs(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pData unsafe.Pointer, d //export call func call( -pCtx unsafe.Pointer, -kind int32, -gas int64, -pAddr unsafe.Pointer, -pValue unsafe.Pointer, -pInput unsafe.Pointer, -inputSize C.size_t, -pOutput unsafe.Pointer, -outputSize C.size_t) int64 { + pCtx unsafe.Pointer, + kind int32, + gas int64, + pAddr unsafe.Pointer, + pValue unsafe.Pointer, + pInput unsafe.Pointer, + inputSize C.size_t, + pOutput unsafe.Pointer, + outputSize C.size_t) int64 { + fmt.Printf("CALL\n") ctxWrapper := (*ContextWrapper)(pCtx) ctx := getCtx(ctxWrapper.index) @@ -442,7 +448,6 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) codeSize := C.size_t(len(code)) gas := C.int64_t(contract.Gas.Int64()) rev := getRevision(evm.env) - // fmt.Printf("EVMJIT pre Run (gas %d %d mode: %d, env: %d) %x\n", contract.Gas, gas, mode, env, evm.contract.Address()) // Create context for this execution. wrapper := ContextWrapper{} @@ -457,13 +462,16 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) msg.input_size = C.size_t(len(input)) msg.gas = gas msg.depth = C.int32_t(evm.env.depth - 1) - msg.code_hash = HashToEvmc(crypto.Keccak256Hash(code)) + codeHash := crypto.Keccak256Hash(code) + msg.code_hash = HashToEvmc(codeHash) + + 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) unpinCtx(wrapper.index) - // fmt.Printf("EVMJIT Run %d %d %x\n", r.code, r.gas_left, evm.contract.Address()) + fmt.Printf("EVMJIT Run %d %d %x\n", r.status_code, r.gas_left, contract.Address()) if r.gas_left > gas { panic("OOPS") } From dd082d444143d4628e96983f8a49ccc8379d5279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 Dec 2017 17:19:14 +0100 Subject: [PATCH 11/29] evmjit: WIP implement calls --- core/vm/evmjit.go | 129 +++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index c900ad7578..197f3e8999 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -29,6 +29,7 @@ static const struct evm_context_fn_table* get_context_fn_table() void get_balance(void*, void*, void*); size_t get_code(unsigned char**, void*, void*); void selfdestruct(void*, void*, void*); + void call(struct evm_result*, void*, struct evm_message*); void getTxCtx(void*, void*); void getBlockHash(void*, void*, long long); void set_logs(void*, void*, void*, size_t, void*, size_t); @@ -40,7 +41,7 @@ static const struct evm_context_fn_table* get_context_fn_table() (evm_get_balance_fn) get_balance, (evm_get_code_fn) get_code, (evm_selfdestruct_fn) selfdestruct, - NULL, + (evm_call_fn) call, (evm_get_tx_context_fn) getTxCtx, (evm_get_block_hash_fn) getBlockHash, (evm_log_fn) set_logs @@ -138,6 +139,11 @@ func getEnv(pCtx unsafe.Pointer) *EVM { return getCtx(ctxWrapper.index).env } +func getContract(pCtx unsafe.Pointer) *Contract { + ctxWrapper := (*ContextWrapper)(pCtx) + 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]))} } @@ -328,85 +334,100 @@ func set_logs(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pData unsafe.Pointer, d } //export call -func call( - pCtx unsafe.Pointer, - kind int32, - gas int64, - pAddr unsafe.Pointer, - pValue unsafe.Pointer, - pInput unsafe.Pointer, - inputSize C.size_t, - pOutput unsafe.Pointer, - outputSize C.size_t) int64 { +func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_message) { fmt.Printf("CALL\n") - ctxWrapper := (*ContextWrapper)(pCtx) - ctx := getCtx(ctxWrapper.index) - address := *(*[20]byte)(pAddr) - value := (*(*common.Hash)(pValue)).Big() - input := GoByteSlice(pInput, inputSize) - output := GoByteSlice(pOutput, outputSize) + env := getEnv(pCtx) + contract := getContract(pCtx) + + addr := *(*common.Address)(unsafe.Pointer(&msg.address)) + value := (*(*common.Hash)(unsafe.Pointer(&msg.value))).Big() + input := GoByteSlice(unsafe.Pointer(msg.input), msg.input_size) + // output := GoByteSlice(pOutput, outputSize) + gas := int64(msg.gas) bigGas := new(big.Int).SetInt64(gas) // TODO: For some reason C.EVM_CALL_FAILURE "constant" is not visible // by go linker. This probably should be reported as cgo bug. const callFailureFlag int64 = -(1 << 63); - switch kind { + switch msg.kind { case C.EVM_CALL: - // fmt.Printf("CALL(gas %d, %x)\n", bigGas, address) - ctx.contract.Gas.SetInt64(0) - ret, err := ctx.env.Call(ctx.contract, address, input, bigGas, value) - gasLeft := ctx.contract.Gas.Int64() + fmt.Printf("CALL(gas %d, %x)\n", bigGas, addr) + contract.Gas.SetInt64(0) + ret, err := env.Call(contract, addr, input, bigGas, value) + gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - // fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d\n", gasLeft) + result.gas_left = C.int64_t(gasLeft) if err == nil { - copy(output, ret) - assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - return gasLeft + result.status_code = C.EVM_SUCCESS + coutput := C.CBytes(ret) + result.output_data = (*C.uint8_t)(coutput) + result.output_size = C.size_t(len(ret)) + // FIXME: free output + return } - return gasLeft | callFailureFlag + result.status_code = C.EVM_FAILURE + return + case C.EVM_CALLCODE: - // fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, address, value) - ctx.contract.Gas.SetInt64(0) - ret, err := ctx.env.CallCode(ctx.contract, address, input, bigGas, value) - gasLeft := ctx.contract.Gas.Int64() + fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, addr, value) + contract.Gas.SetInt64(0) + ret, err := env.CallCode(contract, addr, input, bigGas, value) + gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - // fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d\n", gasLeft) + result.gas_left = C.int64_t(gasLeft) if err == nil { - copy(output, ret) - return gasLeft - } else { - // fmt.Printf("Error: %v\n", err) + result.status_code = C.EVM_SUCCESS + coutput := C.CBytes(ret) + result.output_data = (*C.uint8_t)(coutput) + result.output_size = C.size_t(len(ret)) + // FIXME: free output + return } - return gasLeft | callFailureFlag + result.status_code = C.EVM_FAILURE + return + case C.EVM_DELEGATECALL: - // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) - ctx.contract.Gas.SetInt64(0) - ret, err := ctx.env.DelegateCall(ctx.contract, address, input, bigGas) - gasLeft := ctx.contract.Gas.Int64() + fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, addr) + contract.Gas.SetInt64(0) + ret, err := env.DelegateCall(contract, addr, input, bigGas) + gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - // fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d\n", gasLeft) + result.gas_left = C.int64_t(gasLeft) if err == nil { - copy(output, ret) - return gasLeft + result.status_code = C.EVM_SUCCESS + coutput := C.CBytes(ret) + result.output_data = (*C.uint8_t)(coutput) + result.output_size = C.size_t(len(ret)) + // FIXME: free output + return } - return gasLeft | callFailureFlag + result.status_code = C.EVM_FAILURE + return + case C.EVM_CREATE: - // fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, address) - ctx.contract.Gas.SetInt64(0) - _, addr, err := ctx.env.Create(ctx.contract, input, bigGas, value) - gasLeft := ctx.contract.Gas.Int64() + fmt.Printf("CREATE(gas %d, %x)\n", bigGas, addr) + contract.Gas.SetInt64(0) + _, createAddr, err := env.Create(contract, input, bigGas, value) + gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - if (ctx.env.ChainConfig().IsHomestead(ctx.env.BlockNumber) && err == ErrCodeStoreOutOfGas) || + fmt.Printf("Gas left %d\n", gasLeft) + result.gas_left = C.int64_t(gasLeft) + result.status_code = C.EVM_FAILURE + if (env.ChainConfig().IsHomestead(env.BlockNumber) && err == ErrCodeStoreOutOfGas) || (err != nil && err != ErrCodeStoreOutOfGas) { - return callFailureFlag + return } else { - copy(output, addr[:]) - return gasLeft + result.status_code = C.EVM_SUCCESS + ca := GoByteSlice(unsafe.Pointer(&result.create_address.bytes), 20) + copy(ca, createAddr[:]) } + return } - return 0 } func ptr(bytes []byte) *C.uint8_t { From 07f1584628e00e43faf53a3a98797ee6b288e416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 11:29:01 +0100 Subject: [PATCH 12/29] evmjit: Fix call result initialization --- core/vm/evmjit.go | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 197f3e8999..39ed4237e6 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -202,7 +202,6 @@ 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) { - fmt.Printf("SLOAD\n") result := EvmcHashToSlice(pResult) env := getEnv(pCtx) @@ -212,12 +211,11 @@ func get_storage(pResult *C.struct_evm_uint256be, pCtx unsafe.Pointer, pAddr uns arg := *(*[32]byte)(unsafe.Pointer(pArg)) val := env.StateDB.GetState(addr, arg) copy(result, val[:]) - fmt.Printf("SLOAD %x : %v\n", addr, val) + fmt.Printf("SLOAD %x: %x\n", addr, val) } //export set_storage func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer, pArg2 unsafe.Pointer) { - fmt.Printf("SSTORE\n") env := getEnv(pCtx) var addr common.Address @@ -230,12 +228,11 @@ func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { env.StateDB.AddRefund(params.SstoreRefundGas) } - fmt.Printf("EVMJIT STORE %x : %x [%x]\n", key, newVal, addr) + fmt.Printf("EVMJIT STORE %x: %x := %x\n", addr, key, newVal) } //export get_balance func get_balance(pResult unsafe.Pointer, pCtx unsafe.Pointer, pAddr unsafe.Pointer) { - fmt.Printf("BALANCE\n") result := GoByteSlice(pResult, 32) env := getEnv(pCtx) @@ -335,8 +332,6 @@ 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) { - fmt.Printf("CALL\n") - env := getEnv(pCtx) contract := getContract(pCtx) @@ -347,9 +342,11 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me gas := int64(msg.gas) bigGas := new(big.Int).SetInt64(gas) - // TODO: For some reason C.EVM_CALL_FAILURE "constant" is not visible - // by go linker. This probably should be reported as cgo bug. - const callFailureFlag int64 = -(1 << 63); + // Prepare result with default values. + result.release = nil + result.status_code = C.EVM_FAILURE + result.output_data = nil + result.output_size = 0 switch msg.kind { case C.EVM_CALL: @@ -358,9 +355,10 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me ret, err := env.Call(contract, addr, input, bigGas, value) gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) result.gas_left = C.int64_t(gasLeft) if err == nil { + fmt.Printf("Output: %x\n", ret) result.status_code = C.EVM_SUCCESS coutput := C.CBytes(ret) result.output_data = (*C.uint8_t)(coutput) @@ -377,7 +375,7 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me ret, err := env.CallCode(contract, addr, input, bigGas, value) gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) result.gas_left = C.int64_t(gasLeft) if err == nil { result.status_code = C.EVM_SUCCESS @@ -396,7 +394,7 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me ret, err := env.DelegateCall(contract, addr, input, bigGas) gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) result.gas_left = C.int64_t(gasLeft) if err == nil { result.status_code = C.EVM_SUCCESS @@ -415,7 +413,7 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me _, createAddr, err := env.Create(contract, input, bigGas, value) gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d\n", gasLeft) + fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) result.gas_left = C.int64_t(gasLeft) result.status_code = C.EVM_FAILURE if (env.ChainConfig().IsHomestead(env.BlockNumber) && err == ErrCodeStoreOutOfGas) || @@ -506,6 +504,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) } if r.release != nil { + fmt.Printf("Releasing result with %p\n", r.release) C.evm_release_result(&r) } From 10ee998a0408f600a737cb7d78aa2cc65562c433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 12:25:41 +0100 Subject: [PATCH 13/29] evmjit: Cleanup calls --- core/vm/evmjit.go | 107 +++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 68 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 39ed4237e6..6db3ff897e 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -338,93 +338,64 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me addr := *(*common.Address)(unsafe.Pointer(&msg.address)) value := (*(*common.Hash)(unsafe.Pointer(&msg.value))).Big() input := GoByteSlice(unsafe.Pointer(msg.input), msg.input_size) - // output := GoByteSlice(pOutput, outputSize) gas := int64(msg.gas) bigGas := new(big.Int).SetInt64(gas) - // Prepare result with default values. - result.release = nil - result.status_code = C.EVM_FAILURE - result.output_data = nil - result.output_size = 0 + contract.Gas.SetInt64(0) + + var output []byte + var err error switch msg.kind { case C.EVM_CALL: fmt.Printf("CALL(gas %d, %x)\n", bigGas, addr) - contract.Gas.SetInt64(0) - ret, err := env.Call(contract, addr, input, bigGas, value) - gasLeft := contract.Gas.Int64() - assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) - result.gas_left = C.int64_t(gasLeft) - if err == nil { - fmt.Printf("Output: %x\n", ret) - result.status_code = C.EVM_SUCCESS - coutput := C.CBytes(ret) - result.output_data = (*C.uint8_t)(coutput) - result.output_size = C.size_t(len(ret)) - // FIXME: free output - return - } - result.status_code = C.EVM_FAILURE - return + output, err = env.Call(contract, addr, input, bigGas, value) case C.EVM_CALLCODE: fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, addr, value) - contract.Gas.SetInt64(0) - ret, err := env.CallCode(contract, addr, input, bigGas, value) - gasLeft := contract.Gas.Int64() - assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) - result.gas_left = C.int64_t(gasLeft) - if err == nil { - result.status_code = C.EVM_SUCCESS - coutput := C.CBytes(ret) - result.output_data = (*C.uint8_t)(coutput) - result.output_size = C.size_t(len(ret)) - // FIXME: free output - return - } - result.status_code = C.EVM_FAILURE - return + output, err = env.CallCode(contract, addr, input, bigGas, value) case C.EVM_DELEGATECALL: fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, addr) - contract.Gas.SetInt64(0) - ret, err := env.DelegateCall(contract, addr, input, bigGas) - gasLeft := contract.Gas.Int64() - assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) - result.gas_left = C.int64_t(gasLeft) - if err == nil { - result.status_code = C.EVM_SUCCESS - coutput := C.CBytes(ret) - result.output_data = (*C.uint8_t)(coutput) - result.output_size = C.size_t(len(ret)) - // FIXME: free output - return - } - result.status_code = C.EVM_FAILURE - return + output, err = env.DelegateCall(contract, addr, input, bigGas) case C.EVM_CREATE: fmt.Printf("CREATE(gas %d, %x)\n", bigGas, addr) - contract.Gas.SetInt64(0) - _, createAddr, err := env.Create(contract, input, bigGas, value) - gasLeft := contract.Gas.Int64() - assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) - fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) - result.gas_left = C.int64_t(gasLeft) - result.status_code = C.EVM_FAILURE - if (env.ChainConfig().IsHomestead(env.BlockNumber) && err == ErrCodeStoreOutOfGas) || - (err != nil && err != ErrCodeStoreOutOfGas) { - return - } else { - result.status_code = C.EVM_SUCCESS + var createAddr common.Address + _, createAddr, err = env.Create(contract, input, bigGas, value) + isHomestead := env.ChainConfig().IsHomestead(env.BlockNumber) + if !isHomestead && err == ErrCodeStoreOutOfGas { + err = nil + } + if err == nil { + // Copy create address to result. ca := GoByteSlice(unsafe.Pointer(&result.create_address.bytes), 20) copy(ca, createAddr[:]) } - return + } + + gasLeft := contract.Gas.Int64() + assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) + fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) + result.gas_left = C.int64_t(gasLeft) + + // Map error to status code. + if err == nil { + result.status_code = C.EVM_SUCCESS + } else { + result.status_code = C.EVM_FAILURE + } + + if len(output) > 0 { + cOutput := C.CBytes(output) + result.output_data = (*C.uint8_t)(cOutput) + result.output_size = C.size_t(len(output)) + // FIXME: free output + result.release = nil + } else { + result.output_data = nil + result.output_size = 0 + result.release = nil } } From 5fc5f538bac95f562db83476e6bda49974a81434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 12:48:34 +0100 Subject: [PATCH 14/29] evmjit: Release call outputs --- core/vm/evmjit.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 6db3ff897e..b75c2bc672 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -19,6 +19,7 @@ package vm /* #include +#include static const struct evm_context_fn_table* get_context_fn_table() @@ -67,6 +68,16 @@ static void evm_release_result(struct evm_result* result) result->release(result); } +static void free_result_output(const struct evm_result* result) +{ + free((void*)result->output_data); +} + +static void add_result_releaser(struct evm_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 */ @@ -390,8 +401,10 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me cOutput := C.CBytes(output) result.output_data = (*C.uint8_t)(cOutput) result.output_size = C.size_t(len(output)) - // FIXME: free output - result.release = nil + // We can use result.release = C.release_result_output, but there are + // some problems with linking. Probably definition of + // release_result_output() must be in different C file. + C.add_result_releaser(result) } else { result.output_data = nil result.output_size = 0 From cfddfcf993d5c81df0d39d8685511eb1d094d0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 12:56:20 +0100 Subject: [PATCH 15/29] evmjit: Comment out some logs --- core/vm/evmjit.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index b75c2bc672..46d6696bf7 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -207,7 +207,7 @@ func account_exists(pCtx unsafe.Pointer, pAddr unsafe.Pointer) C.int { } else if env.StateDB.Exist(addr) { exist = 1 } - fmt.Printf("EXISTS? %x : %v\n", addr, exist) + // fmt.Printf("EXISTS? %x : %v\n", addr, exist) return exist } @@ -222,7 +222,7 @@ func get_storage(pResult *C.struct_evm_uint256be, pCtx unsafe.Pointer, pAddr uns arg := *(*[32]byte)(unsafe.Pointer(pArg)) val := env.StateDB.GetState(addr, arg) copy(result, val[:]) - fmt.Printf("SLOAD %x: %x\n", addr, val) + // fmt.Printf("SLOAD %x: %x\n", addr, val) } //export set_storage @@ -239,7 +239,7 @@ func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { env.StateDB.AddRefund(params.SstoreRefundGas) } - fmt.Printf("EVMJIT STORE %x: %x := %x\n", addr, key, newVal) + // fmt.Printf("EVMJIT STORE %x: %x := %x\n", addr, key, newVal) } //export get_balance @@ -468,7 +468,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) codeHash := crypto.Keccak256Hash(code) msg.code_hash = HashToEvmc(codeHash) - fmt.Printf("EVMJIT pre Run (gas %d %d mode: %d, env: %d) %x %x\n", contract.Gas, gas, rev, wrapper.index, codeHash, contract.Address()) + // 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) @@ -488,7 +488,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) } if r.release != nil { - fmt.Printf("Releasing result with %p\n", r.release) + // fmt.Printf("Releasing result with %p\n", r.release) C.evm_release_result(&r) } From c7322532af067b9e3e16a5efd1f93aa0f815b915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 14:03:01 +0100 Subject: [PATCH 16/29] evmjit: Update after merge --- core/vm/evmjit.go | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 46d6696bf7..a0dec20563 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -237,7 +237,7 @@ func set_storage(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg1 unsafe.Pointer oldVal := env.StateDB.GetState(addr, key) env.StateDB.SetState(addr, key, newVal) if !common.EmptyHash(oldVal) && common.EmptyHash(newVal) { - env.StateDB.AddRefund(params.SstoreRefundGas) + env.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas)) } // fmt.Printf("EVMJIT STORE %x: %x := %x\n", addr, key, newVal) } @@ -284,7 +284,7 @@ func selfdestruct(pCtx unsafe.Pointer, pAddr unsafe.Pointer, pArg unsafe.Pointer db := env.StateDB if !db.HasSuicided(addr) { - db.AddRefund(params.SuicideRefundGas) + db.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas)) } balance := db.GetBalance(addr) db.AddBalance(beneficiary, balance) @@ -349,31 +349,29 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me addr := *(*common.Address)(unsafe.Pointer(&msg.address)) value := (*(*common.Hash)(unsafe.Pointer(&msg.value))).Big() input := GoByteSlice(unsafe.Pointer(msg.input), msg.input_size) - gas := int64(msg.gas) - bigGas := new(big.Int).SetInt64(gas) - - contract.Gas.SetInt64(0) + gas := uint64(msg.gas) var output []byte + var gasLeft uint64 var err error switch msg.kind { case C.EVM_CALL: - fmt.Printf("CALL(gas %d, %x)\n", bigGas, addr) - output, err = env.Call(contract, addr, input, bigGas, value) + fmt.Printf("CALL(gas %d, %x)\n", gas, addr) + output, gasLeft, err = env.Call(contract, addr, input, gas, value) case C.EVM_CALLCODE: - fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", bigGas, addr, value) - output, err = env.CallCode(contract, addr, input, bigGas, value) + 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: - fmt.Printf("DELEGATECALL(gas %d, %x)\n", bigGas, addr) - output, err = env.DelegateCall(contract, addr, input, bigGas) + fmt.Printf("DELEGATECALL(gas %d, %x)\n", gas, addr) + output, gasLeft, err = env.DelegateCall(contract, addr, input, gas) case C.EVM_CREATE: - fmt.Printf("CREATE(gas %d, %x)\n", bigGas, addr) + fmt.Printf("CREATE(gas %d, %x)\n", gas, addr) var createAddr common.Address - _, createAddr, err = env.Create(contract, input, bigGas, value) + _, createAddr, gasLeft, err = env.Create(contract, input, gas, value) isHomestead := env.ChainConfig().IsHomestead(env.BlockNumber) if !isHomestead && err == ErrCodeStoreOutOfGas { err = nil @@ -385,7 +383,6 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me } } - gasLeft := contract.Gas.Int64() assert(gasLeft <= gas, fmt.Sprintf("%d <= %d", gasLeft, gas)) fmt.Printf("Gas left %d, err: %s\n", gasLeft, err) result.gas_left = C.int64_t(gasLeft) @@ -435,12 +432,6 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) evm.env.depth++ defer func() { evm.env.depth-- }() - if contract.CodeAddr != nil { - if p := PrecompiledContracts[*contract.CodeAddr]; p != nil { - return RunPrecompiledContract(p, input, contract) - } - } - // Don't bother with the execution if there's no code. if len(contract.Code) == 0 { return nil, nil @@ -449,7 +440,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) code := contract.Code codePtr := (*C.uint8_t)(unsafe.Pointer(&code[0])) codeSize := C.size_t(len(code)) - gas := C.int64_t(contract.Gas.Int64()) + gas := C.int64_t(contract.Gas) rev := getRevision(evm.env) // Create context for this execution. @@ -478,7 +469,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) if r.gas_left > gas { panic("OOPS") } - contract.Gas.SetInt64(int64(r.gas_left)) + 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)) From 88aa0db42bc9f00d4247e71a7e9967d9f72ce472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 14:34:26 +0100 Subject: [PATCH 17/29] evmjit: Use EVMJIT instead of Interpreter --- core/vm/evm.go | 6 +++--- core/vm/evmjit.go | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 344435f73c..3b01947cb1 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -100,7 +100,7 @@ type EVM struct { vmConfig Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. - interpreter *Interpreter + interpreter *EVMJIT // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 @@ -121,7 +121,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon chainRules: chainConfig.Rules(ctx.BlockNumber), } - evm.interpreter = NewInterpreter(evm, vmConfig) + evm.interpreter = NewJit(evm, vmConfig) return evm } @@ -374,4 +374,4 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } // Interpreter returns the EVM interpreter -func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } +func (evm *EVM) Interpreter() *EVMJIT { return evm.interpreter } diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index a0dec20563..60bc3a7527 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -99,6 +99,9 @@ import ( type EVMJIT struct { jit *C.struct_evm_instance env *EVM + intPool *intPool + readOnly bool + returnData []byte // Last CALL's return data for subsequent reuse } type EVMCContext struct { @@ -113,7 +116,7 @@ type ContextWrapper struct { func NewJit(env *EVM, cfg Config) *EVMJIT { // FIXME: Destroy the jit later. - return &EVMJIT{C.evmjit_create(), env} + return &EVMJIT{C.evmjit_create(), env, nil, false, nil} } @@ -428,7 +431,7 @@ func getRevision(env *EVM) C.enum_evm_revision { return C.EVM_FRONTIER } -func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) { +func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) { evm.env.depth++ defer func() { evm.env.depth-- }() From 6a0df901d10ddc1e2a579ca2f39d0dc8f3f8cd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 15:19:25 +0100 Subject: [PATCH 18/29] evmjit: Add static call checks --- core/vm/evmjit.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 60bc3a7527..2767a343d0 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -358,6 +358,9 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me var gasLeft uint64 var err error + staticCall := (msg.flags & C.EVM_STATIC) != 0 + assert(!staticCall, "static call?") + switch msg.kind { case C.EVM_CALL: fmt.Printf("CALL(gas %d, %x)\n", gas, addr) @@ -410,6 +413,7 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me result.output_size = 0 result.release = nil } + fmt.Printf("CALL end\n") } func ptr(bytes []byte) *C.uint8_t { @@ -461,6 +465,8 @@ func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []by msg.depth = C.int32_t(evm.env.depth - 1) codeHash := crypto.Keccak256Hash(code) msg.code_hash = HashToEvmc(codeHash) + assert(evm.readOnly == false, "read only?") + msg.flags = 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()) @@ -468,7 +474,7 @@ func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []by unpinCtx(wrapper.index) - fmt.Printf("EVMJIT Run %d %d %x\n", r.status_code, r.gas_left, contract.Address()) + 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") } From 07379eb387cb423cb25a2c3a8fee6139fe21da51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 15:53:13 +0100 Subject: [PATCH 19/29] evmjit: Add REVERT and Byzantium rev --- core/vm/evmjit.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 2767a343d0..3271a86b52 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -423,6 +423,9 @@ func ptr(bytes []byte) *C.uint8_t { func getRevision(env *EVM) C.enum_evm_revision { n := env.BlockNumber + if env.ChainConfig().IsByzantium(n) { + return C.EVM_BYZANTIUM + } if env.ChainConfig().IsEIP158(n) { return C.EVM_SPURIOUS_DRAGON } @@ -482,7 +485,9 @@ func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []by // 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 != 0 { + if r.status_code == C.EVM_REVERT { + evm.env.StateDB.RevertToSnapshot(snapshot) + } else if r.status_code != C.EVM_SUCCESS { // EVMJIT does not informs about the kind of the EVM expection. err = ErrOutOfGas } From d0bbe110b4c3666cb49559ac1de727c8925d2b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Dec 2017 16:03:04 +0100 Subject: [PATCH 20/29] evmjit: Implement STATICCALL --- core/vm/evmjit.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 3271a86b52..d2c3e0ba5c 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -358,13 +358,17 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me var gasLeft uint64 var err error - staticCall := (msg.flags & C.EVM_STATIC) != 0 - assert(!staticCall, "static call?") - switch msg.kind { case C.EVM_CALL: - fmt.Printf("CALL(gas %d, %x)\n", gas, addr) - output, gasLeft, err = env.Call(contract, addr, input, gas, value) + staticCall := (msg.flags & C.EVM_STATIC) != 0 + if staticCall { + fmt.Printf("STATICCALL(gas %d, %x)\n", gas, addr) + output, gasLeft, err = env.StaticCall(contract, addr, input, gas) + } else { + fmt.Printf("CALL(gas %d, %x)\n", gas, addr) + output, gasLeft, err = env.Call(contract, addr, input, gas, value) + } + case C.EVM_CALLCODE: fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", gas, addr, value) @@ -468,8 +472,11 @@ func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []by msg.depth = C.int32_t(evm.env.depth - 1) codeHash := crypto.Keccak256Hash(code) msg.code_hash = HashToEvmc(codeHash) - assert(evm.readOnly == false, "read only?") - msg.flags = 0 + if evm.readOnly { + msg.flags = C.EVM_STATIC + } else { + msg.flags = 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()) From 4e82d06dbdd4414282c7fe8a0d9df9baa4d044e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 Dec 2017 11:23:50 +0100 Subject: [PATCH 21/29] evmjit: Fix REVERT --- core/vm/evmjit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index d2c3e0ba5c..dd5e610771 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -493,7 +493,7 @@ func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []by output := C.GoBytes(unsafe.Pointer(r.output_data), C.int(r.output_size)) if r.status_code == C.EVM_REVERT { - evm.env.StateDB.RevertToSnapshot(snapshot) + err = errExecutionReverted } else if r.status_code != C.EVM_SUCCESS { // EVMJIT does not informs about the kind of the EVM expection. err = ErrOutOfGas From 8db11d782215fccd8a9236920159e8404cc7f7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 Dec 2017 13:17:55 +0100 Subject: [PATCH 22/29] ethstats: Add +EVMJIT --- ethstats/ethstats.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 77784ff4ac..f8e7cddd88 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -381,11 +381,13 @@ func (s *Service) login(conn *websocket.Conn) error { network = fmt.Sprintf("%d", infos.Protocols["les"].(*eth.EthNodeInfo).Network) protocol = fmt.Sprintf("les/%d", les.ClientProtocolVersions[0]) } + node := strings.Replace(infos.Name, "Geth", "Geth+EVMJIT", 1) + log.Warn("Node name: " + node) auth := &authMsg{ Id: s.node, Info: nodeInfo{ Name: s.node, - Node: infos.Name, + Node: node, Port: infos.Ports.Listener, Network: network, Protocol: protocol, From 1cf355cdc9aee96919c0143344a1499a02b5eddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Dec 2017 13:24:38 +0100 Subject: [PATCH 23/29] evmjit: Remove snapshot arg --- core/vm/evmjit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index dd5e610771..66a0ff72e4 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -442,7 +442,7 @@ func getRevision(env *EVM) C.enum_evm_revision { return C.EVM_FRONTIER } -func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) { +func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) { evm.env.depth++ defer func() { evm.env.depth-- }() @@ -480,7 +480,7 @@ func (evm *EVMJIT) Run(snapshot int, contract *Contract, input []byte) (ret []by // 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.evm_execute(evm.jit, &wrapper.c, rev, nil, codePtr, codeSize) unpinCtx(wrapper.index) From dcc3a314c8fdebe3d3f626b7cc59903902be0aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Dec 2017 14:07:58 +0100 Subject: [PATCH 24/29] evmjit: Fix passing Go pointer --- core/vm/evmjit.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 66a0ff72e4..720d5ae590 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -176,11 +176,6 @@ func assert(cond bool, msg string) { } } -type MemoryRef struct { - ptr *C.uint8_t - len int -} - func GoByteSlice(data unsafe.Pointer, size C.size_t) []byte { var sliceHeader reflect.SliceHeader sliceHeader.Data = uintptr(data) @@ -466,8 +461,6 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) msg.address = AddressToEvmc(contract.Address()) msg.sender = AddressToEvmc(contract.Caller()) msg.value = BigToEvmc(contract.value) - msg.input = ptr(input) - msg.input_size = C.size_t(len(input)) msg.gas = gas msg.depth = C.int32_t(evm.env.depth - 1) codeHash := crypto.Keccak256Hash(code) @@ -478,9 +471,19 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) msg.flags = 0 } + if len(input) > 0 { + cInput := C.CBytes(input) + msg.input = (*C.uint8_t)(cInput) + msg.input_size = C.size_t(len(input)) + defer C.free(cInput) + } else { + msg.input = 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, nil, codePtr, codeSize) + r := C.evm_execute(evm.jit, &wrapper.c, rev, &msg, codePtr, codeSize) unpinCtx(wrapper.index) From ae9f13e52e475b979066bdc3363e4732abd5b851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Dec 2017 14:39:24 +0100 Subject: [PATCH 25/29] evmjit: Disable debug logs --- core/vm/evmjit.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 720d5ae590..9b3dbc31f9 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -250,7 +250,7 @@ func get_balance(pResult unsafe.Pointer, pCtx unsafe.Pointer, pAddr unsafe.Point balance := env.StateDB.GetBalance(addr) val := common.BigToHash(balance) copy(result, val[:]) - fmt.Printf("BALANCE %x : %v\n", addr, balance) + // fmt.Printf("BALANCE %x : %v\n", addr, balance) } //export get_code @@ -357,24 +357,24 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me case C.EVM_CALL: staticCall := (msg.flags & C.EVM_STATIC) != 0 if staticCall { - fmt.Printf("STATICCALL(gas %d, %x)\n", gas, addr) + // fmt.Printf("STATICCALL(gas %d, %x)\n", gas, addr) output, gasLeft, err = env.StaticCall(contract, addr, input, gas) } else { - fmt.Printf("CALL(gas %d, %x)\n", gas, addr) + // fmt.Printf("CALL(gas %d, %x)\n", gas, addr) output, gasLeft, err = env.Call(contract, addr, input, gas, value) } case C.EVM_CALLCODE: - fmt.Printf("CALLCODE(gas %d, %x, value %d)\n", gas, addr, value) + // 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: - fmt.Printf("DELEGATECALL(gas %d, %x)\n", gas, addr) + // fmt.Printf("DELEGATECALL(gas %d, %x)\n", gas, addr) output, gasLeft, err = env.DelegateCall(contract, addr, input, gas) case C.EVM_CREATE: - fmt.Printf("CREATE(gas %d, %x)\n", gas, addr) + // fmt.Printf("CREATE(gas %d, %x)\n", gas, addr) var createAddr common.Address _, createAddr, gasLeft, err = env.Create(contract, input, gas, value) isHomestead := env.ChainConfig().IsHomestead(env.BlockNumber) @@ -389,7 +389,7 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me } 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\n", gasLeft, err) result.gas_left = C.int64_t(gasLeft) // Map error to status code. @@ -412,7 +412,6 @@ func call(result *C.struct_evm_result, pCtx unsafe.Pointer, msg *C.struct_evm_me result.output_size = 0 result.release = nil } - fmt.Printf("CALL end\n") } func ptr(bytes []byte) *C.uint8_t { @@ -487,7 +486,7 @@ func (evm *EVMJIT) Run(contract *Contract, input []byte) (ret []byte, err error) unpinCtx(wrapper.index) - fmt.Printf("EVMJIT Run [%d]: %d %d %x\n", evm.env.depth - 1, r.status_code, r.gas_left, contract.Address()) + // 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") } 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 26/29] 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 From d1439fdc9275927a6c02563d094957b760eeacb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 Jun 2018 14:11:10 +0200 Subject: [PATCH 27/29] cmd: Add --vm flag --- cmd/geth/main.go | 1 + cmd/geth/usage.go | 1 + cmd/utils/flags.go | 10 +++++++++- core/vm/interpreter.go | 3 +++ eth/backend.go | 2 +- eth/config.go | 3 +++ 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index edf2a557a7..0a4367d5bc 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -112,6 +112,7 @@ var ( utils.TestnetFlag, utils.RinkebyFlag, utils.VMEnableDebugFlag, + utils.EVMCPathFlag, utils.NetworkIdFlag, utils.RPCCORSDomainFlag, utils.RPCVirtualHostsFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index d934c6b021..3dc4bc4c27 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -201,6 +201,7 @@ var AppHelpFlagGroups = []flagGroup{ Name: "VIRTUAL MACHINE", Flags: []cli.Flag{ utils.VMEnableDebugFlag, + utils.EVMCPathFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 41a1ac35fe..e30bd4540b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -355,6 +355,11 @@ var ( Name: "vmdebug", Usage: "Record information useful for VM and contract debugging", } + EVMCPathFlag = cli.StringFlag{ + Name: "vm", + Usage: "Path to EVMC compatible VM plugin", + } + // Logging and debug settings EthStatsURLFlag = cli.StringFlag{ Name: "ethstats", @@ -1070,6 +1075,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) } + if ctx.GlobalIsSet(EVMCPathFlag.Name) { + cfg.EVMCPath = ctx.GlobalString(EVMCPathFlag.Name) + } // Override any default configs for hard coded networks. switch { @@ -1250,7 +1258,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) { cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100 } - vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} + vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name), EVMCPath: ctx.GlobalString(EVMCPathFlag.Name)} chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg) if err != nil { Fatalf("Can't create BlockChain: %v", err) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7090e0261f..0cf31df366 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -39,6 +39,9 @@ type Config struct { // may be left uninitialised and will be set to the default // table. JumpTable [256]operation + + // Path to EVMC VM module + EVMCPath string } // Interpreter is used to run Ethereum based contracts and will utilise the diff --git a/eth/backend.go b/eth/backend.go index a18abdfb56..5055387287 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -143,7 +143,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) } var ( - vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} + vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording, EVMCPath: config.EVMCPath} cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout} ) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig) diff --git a/eth/config.go b/eth/config.go index 426d2bf1ef..383dbdda57 100644 --- a/eth/config.go +++ b/eth/config.go @@ -112,6 +112,9 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool + // Path to EVMC VM module + EVMCPath string + // Miscellaneous options DocRoot string `toml:"-"` } From 68eea63083912feda82d51104e75932a0e5ce311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 Jun 2018 14:54:29 +0200 Subject: [PATCH 28/29] evmc: Load VM dynamically --- core/vm/evmjit.go | 58 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/core/vm/evmjit.go b/core/vm/evmjit.go index 0bbc385950..490476ec7b 100644 --- a/core/vm/evmjit.go +++ b/core/vm/evmjit.go @@ -18,8 +18,9 @@ package vm /* -#include +#include #include +#include static const struct evmc_context_fn_table* get_context_fn_table() @@ -80,8 +81,16 @@ static void add_result_releaser(struct evmc_result* result) result->release = free_result_output; } -#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 +typedef struct evmc_instance* (*evmc_create_fn)(); + +static struct evmc_instance* create(void* create_symbol) +{ + evmc_create_fn fn = (evmc_create_fn)create_symbol; + return fn(); +} + +#cgo CFLAGS: -I/home/chfast/Projects/ethereum/cpp-ethereum/evmc/include +#cgo LDFLAGS: -ldl */ import "C" @@ -96,6 +105,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/log" ) type EVMJIT struct { @@ -116,9 +126,47 @@ type ContextWrapper struct { index int } +var loadMu sync.Mutex +var createSymbol unsafe.Pointer +var loaded = false + +func loadVM(path string) { + if len(path) == 0 { + panic("EVMC path not provided, use --vm flag") + } + + loadMu.Lock() + defer loadMu.Unlock() + + if loaded { + return + } + + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + handle := C.dlopen(cpath, C.RTLD_LAZY) + if handle == nil { + panic(fmt.Sprintf("cannot open %s", path)) + } + + centrypoint := C.CString("evmc_create") + defer C.free(unsafe.Pointer((centrypoint))) + + C.dlerror() + createSymbol = C.dlsym(handle, centrypoint) + err := C.dlerror() + if err != nil { + panic(fmt.Sprintf("cannot find evmc_create in %s", path)) + } + loaded = true + log.Info("EVMC VM loaded", "path", path) +} + func NewJit(env *EVM, cfg Config) *EVMJIT { - // FIXME: Destroy the jit later. - return &EVMJIT{C.evmc_create_interpreter(), env, nil, false, nil} + loadVM(cfg.EVMCPath) + // FIXME: Destroy the instance later. + // FIXME: Create the instance once. + return &EVMJIT{C.create(createSymbol), env, nil, false, nil} } From a3a31bf3bce0768958fea51fd9912aecee755508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 Jun 2018 15:39:09 +0200 Subject: [PATCH 29/29] ethstats: Geth+EVMC --- ethstats/ethstats.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 0ff381e8a1..452c8f1a0a 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -380,8 +380,7 @@ func (s *Service) login(conn *websocket.Conn) error { network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network) protocol = fmt.Sprintf("les/%d", les.ClientProtocolVersions[0]) } - node := strings.Replace(infos.Name, "Geth", "Geth+EVMJIT", 1) - log.Warn("Node name: " + node) + node := strings.Replace(infos.Name, "Geth", "Geth+EVMC", 1) auth := &authMsg{ ID: s.node, Info: nodeInfo{