diff --git a/cmd/ewasm/main.go b/cmd/ewasm/main.go index e67de3f526..bd0726344f 100644 --- a/cmd/ewasm/main.go +++ b/cmd/ewasm/main.go @@ -111,10 +111,8 @@ func runCmd(ctx *cli.Context) error { } evm := coreVM.NewEVM(permissiveContext, statedb, ¶ms.ChainConfig{}, coreVM.Config{}) - evm.StateDB.SetCode(contractAddr, code) - output, leftOver, err := evm.Call(coreVM.AccountRef(callerAddr), contractAddr, input, gas, big.NewInt(0)) if err != nil { diff --git a/core/vm/doc.go b/core/vm/doc.go index 549631b067..130f969042 100644 --- a/core/vm/doc.go +++ b/core/vm/doc.go @@ -31,3 +31,6 @@ const u128Len = 16 // Max recursion depth for contracts const maxCallDepth = 1024 + +// Address of the sentinel (metering) contract +const sentinelContractAddress = "0x000000000000000000000000000000000000000a" diff --git a/core/vm/evm.go b/core/vm/evm.go index fcf90f906a..8bebcbde92 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -393,6 +393,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // EVM. The contract is a scoped environment for this execution context // only. contract := NewContract(caller, AccountRef(address), value, gas) + for _, interpreter := range evm.interpreters { + if interpreter.CanRun(codeAndHash.code) { + var err error + codeAndHash.code, err = interpreter.PreContractCreation(codeAndHash.code, contract) + if err != nil { + return nil, address, gas, nil + } + } + } contract.SetCodeOptionalHash(&address, codeAndHash) if evm.vmConfig.NoRecursion && evm.depth > 0 { @@ -407,9 +416,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := run(evm, contract, nil, false) /* The new contract needs to be metered after it has executed the constructor */ - for _, interpreter := range evm.interpreters { - if interpreter.CanRun(contract.Code) { - ret = interpreter.PostContractCreation(ret) + if err == nil { + for _, interpreter := range evm.interpreters { + if interpreter.CanRun(contract.Code) { + ret, err = interpreter.PostContractCreation(ret) + } } } diff --git a/core/vm/ewasm.go b/core/vm/ewasm.go index 0fb93ea38e..654f8d79fe 100644 --- a/core/vm/ewasm.go +++ b/core/vm/ewasm.go @@ -62,8 +62,9 @@ type InterpreterEWASM struct { metering bool - meteringContract *Contract - meteringVM *exec.VM + meteringContract *Contract + meteringModule *wasm.Module + meteringStartIndex int64 } // NewEWASMInterpreter creates a new wagon-based ewasm interpreter. It @@ -72,29 +73,32 @@ type InterpreterEWASM struct { func NewEWASMInterpreter(evm *EVM, cfg Config) Interpreter { metering := cfg.EWASMInterpreter["metering"] == "true" - var meteringVM *exec.VM - var meteringContract *Contract + inter := &InterpreterEWASM{ + StateDB: evm.StateDB, + evm: evm, + gasTable: evm.chainConfig.GasTable(evm.BlockNumber), + metering: metering, + } + if metering { - meteringContractAddress := common.HexToAddress("0x000000000000000000000000000000000000000a") + meteringContractAddress := common.HexToAddress(sentinelContractAddress) meteringCode := evm.StateDB.GetCode(meteringContractAddress) - module, err := wasm.ReadModule(bytes.NewReader(meteringCode), nil) + var err error + inter.meteringModule, err = wasm.ReadModule(bytes.NewReader(meteringCode), WrappedModuleResolver(inter)) if err != nil { panic(fmt.Sprintf("Error loading the metering contract: %v", err)) } - meteringVM, err = exec.NewVM(module) - if err != nil { - panic(fmt.Sprintf("Error allocating metering contract: %v", err)) + // TODO when the metering contract abides by that rule, check that it + // only exports "main" and "memory". + inter.meteringStartIndex = int64(inter.meteringModule.Export.Entries["main"].Index) + mainSig := inter.meteringModule.FunctionIndexSpace[inter.meteringStartIndex].Sig + if len(mainSig.ParamTypes) != 0 || len(mainSig.ReturnTypes) != 0 { + panic(fmt.Sprintf("Invalid main function for the metering contract: index=%d sig=%v", inter.meteringStartIndex, mainSig)) } } - return &InterpreterEWASM{ - StateDB: evm.StateDB, - evm: evm, - gasTable: evm.chainConfig.GasTable(evm.BlockNumber), - meteringVM: meteringVM, - meteringContract: meteringContract, - metering: metering, - } + + return inter } // Run loops and evaluates the contract's code with the given input data and returns @@ -176,13 +180,35 @@ func (in *InterpreterEWASM) CanRun(file []byte) bool { return true } +// PreContractCreation meters the contract's its init code before it +// is run. +func (in *InterpreterEWASM) PreContractCreation(code []byte, contract *Contract) ([]byte, error) { + savedContract := in.contract + in.contract = contract + + defer func() { + in.contract = savedContract + }() + + if in.metering { + metered, _, err := sentinel(in, code) + if len(metered) < 5 || err != nil { + return nil, fmt.Errorf("Error metering the init contract code, err=%v", err) + } + return metered, nil + } + return code, nil +} + // PostContractCreation meters the contract once its init code has // been run. -func (in *InterpreterEWASM) PostContractCreation(code []byte) []byte { +func (in *InterpreterEWASM) PostContractCreation(code []byte) ([]byte, error) { if in.metering { - // hera currently seems to ignore the error code - metered, _ := sentinel(in, code) - return metered + metered, _, err := sentinel(in, code) + if len(metered) < 5 || err != nil { + return nil, fmt.Errorf("Error metering the generated contract code, err=%v", err) + } + return metered, nil } - return code + return code, nil } diff --git a/core/vm/ewasm_eei.go b/core/vm/ewasm_eei.go index c8c10691bf..2648a5291d 100644 --- a/core/vm/ewasm_eei.go +++ b/core/vm/ewasm_eei.go @@ -691,24 +691,34 @@ func getBlockCoinbase(p *exec.Process, in *InterpreterEWASM, resultOffset int32) p.WriteAt(in.evm.Coinbase.Bytes(), int64(resultOffset)) } -func sentinel(in *InterpreterEWASM, input []byte) ([]byte, error) { +func sentinel(in *InterpreterEWASM, input []byte) ([]byte, uint64, error) { savedContract := in.contract savedVM := in.vm defer func() { in.contract = savedContract in.vm = savedVM }() - meteringContractAddress := common.HexToAddress("0x000000000000000000000000000000000000000a") + meteringContractAddress := common.HexToAddress(sentinelContractAddress) meteringCode := in.StateDB.GetCode(meteringContractAddress) - in.contract = NewContract(in.contract, AccountRef(meteringContractAddress), &big.Int{}, 0) - in.contract = in.meteringContract + in.contract = NewContract(in.contract, AccountRef(meteringContractAddress), &big.Int{}, in.contract.Gas) in.contract.SetCallCode(&meteringContractAddress, crypto.Keccak256Hash(meteringCode), meteringCode) - in.vm = in.meteringVM + vm, err := exec.NewVM(in.meteringModule) + vm.RecoverPanic = true + in.vm = vm + if err != nil { + panic(fmt.Sprintf("Error allocating metering VM: %v", err)) + } + in.contract.Input = input + meteredCode, err := in.vm.ExecCode(in.meteringStartIndex) + if meteredCode == nil { + meteredCode = in.returnData + } + var asBytes []byte if err == nil { asBytes = meteredCode.([]byte) } - return asBytes, err + return asBytes, savedContract.Gas - in.contract.Gas, err } func create(p *exec.Process, in *InterpreterEWASM, valueOffset uint32, codeOffset uint32, length uint32, resultOffset uint32) int32 { @@ -740,8 +750,10 @@ func create(p *exec.Process, in *InterpreterEWASM, valueOffset uint32, codeOffse /* Meter the contract code if metering is enabled */ if in.metering { - /* It seems that hera doesn't handle errors */ - input, _ = sentinel(in, input) + input, _, _ = sentinel(in, input) + if len(input) < 5 { + return ErrEEICallFailure + } } _, addr, gasLeft, _ := in.evm.Create(in.contract, input, gas, big.NewInt(0).SetBytes(value)) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 66dc33d2bc..19999deb71 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -66,10 +66,14 @@ type Interpreter interface { // } // ``` CanRun([]byte) bool + // Hook to be called on the init code of a contract to be created. + // This let the interpreter pre-process the init-code before it is + // executed. + PreContractCreation([]byte, *Contract) ([]byte, error) // Hook to be called once a newly created contract's init code // has been called and is going to be stored. This let the // interpreter post-process the bytecode before it is persisted. - PostContractCreation([]byte) []byte + PostContractCreation([]byte) ([]byte, error) } // EVMInterpreter represents an EVM interpreter @@ -276,9 +280,15 @@ func (in *EVMInterpreter) CanRun(code []byte) bool { return true } +// PreContractCreation doesn't need to do anything in the case +// of EVM1 bytecode. +func (in *EVMInterpreter) PreContractCreation(code []byte, contract *Contract) ([]byte, error) { + return code, nil +} + // PostContractCreation doesn't need to do anything in the case // of EVM1 bytecode, but it could turn out useful when extending // the tracer. -func (in *EVMInterpreter) PostContractCreation(code []byte) []byte { - return code +func (in *EVMInterpreter) PostContractCreation(code []byte) ([]byte, error) { + return code, nil }