mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
core/vm: fix metering
Node is now able to synchronize with the testnet when passing geth the option: `--vm.ewasm="metering:true"`.
This commit is contained in:
parent
8fb0c4b53d
commit
972931c10b
6 changed files with 98 additions and 38 deletions
|
|
@ -111,10 +111,8 @@ func runCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
evm := coreVM.NewEVM(permissiveContext, statedb, ¶ms.ChainConfig{}, coreVM.Config{})
|
evm := coreVM.NewEVM(permissiveContext, statedb, ¶ms.ChainConfig{}, coreVM.Config{})
|
||||||
|
|
||||||
evm.StateDB.SetCode(contractAddr, code)
|
evm.StateDB.SetCode(contractAddr, code)
|
||||||
|
|
||||||
|
|
||||||
output, leftOver, err := evm.Call(coreVM.AccountRef(callerAddr), contractAddr, input, gas, big.NewInt(0))
|
output, leftOver, err := evm.Call(coreVM.AccountRef(callerAddr), contractAddr, input, gas, big.NewInt(0))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -31,3 +31,6 @@ const u128Len = 16
|
||||||
|
|
||||||
// Max recursion depth for contracts
|
// Max recursion depth for contracts
|
||||||
const maxCallDepth = 1024
|
const maxCallDepth = 1024
|
||||||
|
|
||||||
|
// Address of the sentinel (metering) contract
|
||||||
|
const sentinelContractAddress = "0x000000000000000000000000000000000000000a"
|
||||||
|
|
|
||||||
|
|
@ -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
|
// EVM. The contract is a scoped environment for this execution context
|
||||||
// only.
|
// only.
|
||||||
contract := NewContract(caller, AccountRef(address), value, gas)
|
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)
|
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||||
|
|
||||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
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)
|
ret, err := run(evm, contract, nil, false)
|
||||||
|
|
||||||
/* The new contract needs to be metered after it has executed the constructor */
|
/* The new contract needs to be metered after it has executed the constructor */
|
||||||
|
if err == nil {
|
||||||
for _, interpreter := range evm.interpreters {
|
for _, interpreter := range evm.interpreters {
|
||||||
if interpreter.CanRun(contract.Code) {
|
if interpreter.CanRun(contract.Code) {
|
||||||
ret = interpreter.PostContractCreation(ret)
|
ret, err = interpreter.PostContractCreation(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ type InterpreterEWASM struct {
|
||||||
metering bool
|
metering bool
|
||||||
|
|
||||||
meteringContract *Contract
|
meteringContract *Contract
|
||||||
meteringVM *exec.VM
|
meteringModule *wasm.Module
|
||||||
|
meteringStartIndex int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEWASMInterpreter creates a new wagon-based ewasm interpreter. It
|
// NewEWASMInterpreter creates a new wagon-based ewasm interpreter. It
|
||||||
|
|
@ -72,29 +73,32 @@ type InterpreterEWASM struct {
|
||||||
func NewEWASMInterpreter(evm *EVM, cfg Config) Interpreter {
|
func NewEWASMInterpreter(evm *EVM, cfg Config) Interpreter {
|
||||||
metering := cfg.EWASMInterpreter["metering"] == "true"
|
metering := cfg.EWASMInterpreter["metering"] == "true"
|
||||||
|
|
||||||
var meteringVM *exec.VM
|
inter := &InterpreterEWASM{
|
||||||
var meteringContract *Contract
|
|
||||||
if metering {
|
|
||||||
meteringContractAddress := common.HexToAddress("0x000000000000000000000000000000000000000a")
|
|
||||||
meteringCode := evm.StateDB.GetCode(meteringContractAddress)
|
|
||||||
|
|
||||||
module, err := wasm.ReadModule(bytes.NewReader(meteringCode), nil)
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &InterpreterEWASM{
|
|
||||||
StateDB: evm.StateDB,
|
StateDB: evm.StateDB,
|
||||||
evm: evm,
|
evm: evm,
|
||||||
gasTable: evm.chainConfig.GasTable(evm.BlockNumber),
|
gasTable: evm.chainConfig.GasTable(evm.BlockNumber),
|
||||||
meteringVM: meteringVM,
|
|
||||||
meteringContract: meteringContract,
|
|
||||||
metering: metering,
|
metering: metering,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if metering {
|
||||||
|
meteringContractAddress := common.HexToAddress(sentinelContractAddress)
|
||||||
|
meteringCode := evm.StateDB.GetCode(meteringContractAddress)
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
// 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 inter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run loops and evaluates the contract's code with the given input data and returns
|
// 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
|
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
|
// PostContractCreation meters the contract once its init code has
|
||||||
// been run.
|
// been run.
|
||||||
func (in *InterpreterEWASM) PostContractCreation(code []byte) []byte {
|
func (in *InterpreterEWASM) PostContractCreation(code []byte) ([]byte, error) {
|
||||||
if in.metering {
|
if in.metering {
|
||||||
// hera currently seems to ignore the error code
|
metered, _, err := sentinel(in, code)
|
||||||
metered, _ := sentinel(in, code)
|
if len(metered) < 5 || err != nil {
|
||||||
return metered
|
return nil, fmt.Errorf("Error metering the generated contract code, err=%v", err)
|
||||||
}
|
}
|
||||||
return code
|
return metered, nil
|
||||||
|
}
|
||||||
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -691,24 +691,34 @@ func getBlockCoinbase(p *exec.Process, in *InterpreterEWASM, resultOffset int32)
|
||||||
p.WriteAt(in.evm.Coinbase.Bytes(), int64(resultOffset))
|
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
|
savedContract := in.contract
|
||||||
savedVM := in.vm
|
savedVM := in.vm
|
||||||
defer func() {
|
defer func() {
|
||||||
in.contract = savedContract
|
in.contract = savedContract
|
||||||
in.vm = savedVM
|
in.vm = savedVM
|
||||||
}()
|
}()
|
||||||
meteringContractAddress := common.HexToAddress("0x000000000000000000000000000000000000000a")
|
meteringContractAddress := common.HexToAddress(sentinelContractAddress)
|
||||||
meteringCode := in.StateDB.GetCode(meteringContractAddress)
|
meteringCode := in.StateDB.GetCode(meteringContractAddress)
|
||||||
in.contract = NewContract(in.contract, AccountRef(meteringContractAddress), &big.Int{}, 0)
|
in.contract = NewContract(in.contract, AccountRef(meteringContractAddress), &big.Int{}, in.contract.Gas)
|
||||||
in.contract = in.meteringContract
|
|
||||||
in.contract.SetCallCode(&meteringContractAddress, crypto.Keccak256Hash(meteringCode), meteringCode)
|
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
|
var asBytes []byte
|
||||||
if err == nil {
|
if err == nil {
|
||||||
asBytes = meteredCode.([]byte)
|
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 {
|
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 */
|
/* Meter the contract code if metering is enabled */
|
||||||
if in.metering {
|
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))
|
_, addr, gasLeft, _ := in.evm.Create(in.contract, input, gas, big.NewInt(0).SetBytes(value))
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,14 @@ type Interpreter interface {
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
CanRun([]byte) bool
|
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
|
// Hook to be called once a newly created contract's init code
|
||||||
// has been called and is going to be stored. This let the
|
// has been called and is going to be stored. This let the
|
||||||
// interpreter post-process the bytecode before it is persisted.
|
// interpreter post-process the bytecode before it is persisted.
|
||||||
PostContractCreation([]byte) []byte
|
PostContractCreation([]byte) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EVMInterpreter represents an EVM interpreter
|
// EVMInterpreter represents an EVM interpreter
|
||||||
|
|
@ -276,9 +280,15 @@ func (in *EVMInterpreter) CanRun(code []byte) bool {
|
||||||
return true
|
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
|
// PostContractCreation doesn't need to do anything in the case
|
||||||
// of EVM1 bytecode, but it could turn out useful when extending
|
// of EVM1 bytecode, but it could turn out useful when extending
|
||||||
// the tracer.
|
// the tracer.
|
||||||
func (in *EVMInterpreter) PostContractCreation(code []byte) []byte {
|
func (in *EVMInterpreter) PostContractCreation(code []byte) ([]byte, error) {
|
||||||
return code
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue