mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
core/vm: meter contracts
This is achieved by passing `--vm.ewasm="metering:true"` to geth
This commit is contained in:
parent
78d789cd28
commit
be16aabe6f
5 changed files with 99 additions and 9 deletions
|
|
@ -406,6 +406,9 @@ 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 */
|
||||||
|
ret = evm.interpreter.PostContractCreation(ret)
|
||||||
|
|
||||||
// check whether the max code size has been exceeded
|
// check whether the max code size has been exceeded
|
||||||
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
|
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
|
||||||
// if the contract creation ran successfully and no errors were returned
|
// if the contract creation ran successfully and no errors were returned
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,10 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/go-interpreter/wagon/wasm"
|
"github.com/go-interpreter/wagon/wasm"
|
||||||
|
|
||||||
|
|
@ -58,13 +61,44 @@ type InterpreterEWASM struct {
|
||||||
terminationType terminationType
|
terminationType terminationType
|
||||||
|
|
||||||
staticMode bool
|
staticMode bool
|
||||||
|
|
||||||
|
metering bool
|
||||||
|
|
||||||
|
meteringContract *Contract
|
||||||
|
meteringVM *exec.VM
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEWASMInterpreter creates a new wagon-based ewasm interpreter. It
|
// NewEWASMInterpreter creates a new wagon-based ewasm interpreter. It
|
||||||
// currently takes a *vm.EVM pointer as a proxy to the client's internal
|
// currently takes a *vm.EVM pointer as a proxy to the client's internal
|
||||||
// state; this will be fixed in subsequent updates.
|
// state; this will be fixed in subsequent updates.
|
||||||
func NewEWASMInterpreter(evm *EVM, cfg Config) Interpreter {
|
func NewEWASMInterpreter(evm *EVM, cfg Config) Interpreter {
|
||||||
return &InterpreterEWASM{StateDB: evm.StateDB, evm: evm, gasTable: evm.chainConfig.GasTable(evm.BlockNumber)}
|
metering := cfg.EWASMInterpreter["metering"] == "true"
|
||||||
|
|
||||||
|
var meteringVM *exec.VM
|
||||||
|
var meteringContract *Contract
|
||||||
|
if metering {
|
||||||
|
meteringContractAddress := common.HexToAddress("0x000000000000000000000000000000000000000a")
|
||||||
|
meteringContract = NewContract(nil, AccountRef(meteringContractAddress), &big.Int{}, 0)
|
||||||
|
meteringCode := evm.StateDB.GetCode(meteringContractAddress)
|
||||||
|
meteringContract.SetCallCode(&meteringContractAddress, crypto.Keccak256Hash(meteringCode), meteringCode)
|
||||||
|
|
||||||
|
module, err := wasm.ReadModule(bytes.NewReader(evm.StateDB.GetCode(meteringContractAddress)), 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,
|
||||||
|
evm: evm,
|
||||||
|
gasTable: evm.chainConfig.GasTable(evm.BlockNumber),
|
||||||
|
meteringVM: meteringVM,
|
||||||
|
meteringContract: meteringContract,
|
||||||
|
metering: metering,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -145,3 +179,14 @@ func (in *InterpreterEWASM) CanRun(file []byte) bool {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostContractCreation meters the contract once its init code has
|
||||||
|
// been run.
|
||||||
|
func (in *InterpreterEWASM) PostContractCreation(code []byte) []byte {
|
||||||
|
if in.metering {
|
||||||
|
// hera currently seems to ignore the error code
|
||||||
|
metered, _ := sentinel(in, code)
|
||||||
|
return metered
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -690,10 +690,27 @@ 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) {
|
||||||
|
savedContract := in.contract
|
||||||
|
savedVM := in.vm
|
||||||
|
defer func() {
|
||||||
|
in.contract = savedContract
|
||||||
|
in.vm = savedVM
|
||||||
|
}()
|
||||||
|
in.contract = in.meteringContract
|
||||||
|
in.vm = in.meteringVM
|
||||||
|
meteredCode, err := in.meteringVM.ExecCode(0)
|
||||||
|
return meteredCode.([]byte), 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 {
|
||||||
in.gasAccounting(GasCostCreate)
|
in.gasAccounting(GasCostCreate)
|
||||||
savedVM := in.vm
|
savedVM := in.vm
|
||||||
savedContract := in.contract
|
savedContract := in.contract
|
||||||
|
defer func() {
|
||||||
|
in.vm = savedVM
|
||||||
|
in.contract = savedContract
|
||||||
|
}()
|
||||||
in.terminationType = TerminateInvalid
|
in.terminationType = TerminateInvalid
|
||||||
|
|
||||||
if int(codeOffset)+int(length) > len(in.vm.Memory()) {
|
if int(codeOffset)+int(length) > len(in.vm.Memory()) {
|
||||||
|
|
@ -713,21 +730,24 @@ func create(p *exec.Process, in *InterpreterEWASM, valueOffset uint32, codeOffse
|
||||||
gas := in.contract.Gas - in.contract.Gas/64
|
gas := in.contract.Gas - in.contract.Gas/64
|
||||||
in.gasAccounting(gas)
|
in.gasAccounting(gas)
|
||||||
|
|
||||||
_, addr, gasLeft, _ := in.evm.Create(in.contract, input, gas, big.NewInt(0).SetBytes(value))
|
/* Meter the contract code if metering is enabled */
|
||||||
|
if in.metering {
|
||||||
|
/* It seems that hera doesn't handle errors */
|
||||||
|
input, _ = sentinel(in, input)
|
||||||
|
}
|
||||||
|
|
||||||
in.vm = savedVM
|
_, addr, gasLeft, _ := in.evm.Create(in.contract, input, gas, big.NewInt(0).SetBytes(value))
|
||||||
in.contract = savedContract
|
|
||||||
|
|
||||||
switch in.terminationType {
|
switch in.terminationType {
|
||||||
case TerminateFinish:
|
case TerminateFinish:
|
||||||
in.contract.Gas += gasLeft
|
savedContract.Gas += gasLeft
|
||||||
p.WriteAt(addr.Bytes(), int64(resultOffset))
|
p.WriteAt(addr.Bytes(), int64(resultOffset))
|
||||||
return EEICallSuccess
|
return EEICallSuccess
|
||||||
case TerminateRevert:
|
case TerminateRevert:
|
||||||
in.contract.Gas += gas
|
savedContract.Gas += gas
|
||||||
return ErrEEICallRevert
|
return ErrEEICallRevert
|
||||||
default:
|
default:
|
||||||
in.contract.Gas += gasLeft
|
savedContract.Gas += gasLeft
|
||||||
return ErrEEICallFailure
|
return ErrEEICallFailure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ type Config struct {
|
||||||
JumpTable [256]operation
|
JumpTable [256]operation
|
||||||
|
|
||||||
// Type of the EWASM interpreter
|
// Type of the EWASM interpreter
|
||||||
EWASMInterpreter string
|
EWASMInterpreter map[string]string
|
||||||
// Type of the EVM interpreter
|
// Type of the EVM interpreter
|
||||||
EVMInterpreter string
|
EVMInterpreter string
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +66,10 @@ type Interpreter interface {
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
CanRun([]byte) bool
|
CanRun([]byte) bool
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// EVMInterpreter represents an EVM interpreter
|
// EVMInterpreter represents an EVM interpreter
|
||||||
|
|
@ -271,3 +275,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
func (in *EVMInterpreter) CanRun(code []byte) bool {
|
func (in *EVMInterpreter) CanRun(code []byte) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
|
@ -148,10 +149,20 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ewasmOptions := make(map[string]string)
|
||||||
|
for _, option := range strings.Split(config.EWASMInterpreter, ",") {
|
||||||
|
opt := strings.Split(option, ":")
|
||||||
|
if len(opt) != 2 || len(opt[0]) == 0 {
|
||||||
|
panic(fmt.Sprintf("Invalid ewasm option: expected a format of name:value, got: %s", option))
|
||||||
|
}
|
||||||
|
ewasmOptions[opt[0]] = opt[1]
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
vmConfig = vm.Config{
|
vmConfig = vm.Config{
|
||||||
EnablePreimageRecording: config.EnablePreimageRecording,
|
EnablePreimageRecording: config.EnablePreimageRecording,
|
||||||
EWASMInterpreter: config.EWASMInterpreter,
|
EWASMInterpreter: ewasmOptions,
|
||||||
EVMInterpreter: config.EVMInterpreter,
|
EVMInterpreter: config.EVMInterpreter,
|
||||||
}
|
}
|
||||||
cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
|
cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue