core: added basic chain configuration
Added chain configuration options and write out during genesis database insertion. If no "config" was found, nothing is written to the database. Configurations are written on a per genesis base. This means that any chain (which is identified by it's genesis hash) can have their own chain settings.
This commit is contained in:
committed by
Jeffrey Wilcke
parent
10d3466c93
commit
f0cbebb19f
@ -22,9 +22,17 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// RuleSet is an interface that defines the current rule set during the
|
||||
// execution of the EVM instructions (e.g. whether it's homestead)
|
||||
type RuleSet interface {
|
||||
IsHomestead(*big.Int) bool
|
||||
}
|
||||
|
||||
// Environment is an EVM requirement and helper which allows access to outside
|
||||
// information such as states.
|
||||
type Environment interface {
|
||||
// The current ruleset
|
||||
RuleSet() RuleSet
|
||||
// The state database
|
||||
Db() Database
|
||||
// Creates a restorable snapshot
|
||||
@ -53,10 +61,10 @@ type Environment interface {
|
||||
AddLog(*Log)
|
||||
// Type of the VM
|
||||
Vm() Vm
|
||||
// Current calling depth
|
||||
// Get the curret calling depth
|
||||
Depth() int
|
||||
// Set the current calling depth
|
||||
SetDepth(i int)
|
||||
|
||||
// Call another contract
|
||||
Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
|
||||
// Take another's contract code and execute within our own context
|
||||
|
@ -520,7 +520,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// rule) and treat as an error, if the ruleset is frontier we must
|
||||
// ignore this error and pretend the operation was successful.
|
||||
if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
|
||||
if env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
|
||||
stack.push(new(big.Int))
|
||||
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
|
||||
stack.push(new(big.Int))
|
||||
|
@ -30,27 +30,24 @@ import (
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// progStatus is the type for the JIT program status.
|
||||
type progStatus int32
|
||||
|
||||
const (
|
||||
progUnknown progStatus = iota
|
||||
progCompile
|
||||
progReady
|
||||
progError
|
||||
progUnknown progStatus = iota // unknown status
|
||||
progCompile // compile status
|
||||
progReady // ready for use status
|
||||
progError // error status (usually caused during compilation)
|
||||
|
||||
defaultJitMaxCache int = 64
|
||||
defaultJitMaxCache int = 64 // maximum amount of jit cached programs
|
||||
)
|
||||
|
||||
var (
|
||||
EnableJit bool // Enables the JIT VM
|
||||
ForceJit bool // Force the JIT, skip byte VM
|
||||
MaxProgSize int // Max cache size for JIT Programs
|
||||
)
|
||||
var MaxProgSize int // Max cache size for JIT programs
|
||||
|
||||
var programs *lru.Cache
|
||||
var programs *lru.Cache // lru cache for the JIT programs.
|
||||
|
||||
func init() {
|
||||
programs, _ = lru.New(defaultJitMaxCache)
|
||||
SetJITCacheSize(defaultJitMaxCache)
|
||||
}
|
||||
|
||||
// SetJITCacheSize recreates the program cache with the max given size. Setting
|
||||
@ -322,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
|
||||
}()
|
||||
}
|
||||
|
||||
homestead := params.IsHomestead(env.BlockNumber())
|
||||
homestead := env.RuleSet().IsHomestead(env.BlockNumber())
|
||||
for pc < uint64(len(program.instructions)) {
|
||||
instrCount++
|
||||
|
||||
|
@ -84,7 +84,7 @@ func TestCompiling(t *testing.T) {
|
||||
func TestResetInput(t *testing.T) {
|
||||
var sender account
|
||||
|
||||
env := NewEnv()
|
||||
env := NewEnv(false, true)
|
||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
||||
contract.CodeAddr = &common.Address{}
|
||||
|
||||
@ -143,10 +143,7 @@ func runVmBench(test vmBench, b *testing.B) {
|
||||
if test.precompile && !test.forcejit {
|
||||
NewProgram(test.code)
|
||||
}
|
||||
env := NewEnv()
|
||||
|
||||
EnableJit = !test.nojit
|
||||
ForceJit = test.forcejit
|
||||
env := NewEnv(test.nojit, test.forcejit)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
@ -168,12 +165,16 @@ type Env struct {
|
||||
evm *EVM
|
||||
}
|
||||
|
||||
func NewEnv() *Env {
|
||||
func NewEnv(noJit, forceJit bool) *Env {
|
||||
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
|
||||
env.evm = New(env, nil)
|
||||
env.evm = New(env, Config{
|
||||
EnableJit: !noJit,
|
||||
ForceJit: forceJit,
|
||||
})
|
||||
return env
|
||||
}
|
||||
|
||||
func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
|
||||
func (self *Env) Vm() Vm { return self.evm }
|
||||
func (self *Env) Origin() common.Address { return common.Address{} }
|
||||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
||||
|
@ -1,10 +1,6 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
import "math/big"
|
||||
|
||||
type jumpPtr struct {
|
||||
fn instrFn
|
||||
@ -13,12 +9,12 @@ type jumpPtr struct {
|
||||
|
||||
type vmJumpTable [256]jumpPtr
|
||||
|
||||
func newJumpTable(blockNumber *big.Int) vmJumpTable {
|
||||
func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable {
|
||||
var jumpTable vmJumpTable
|
||||
|
||||
// when initialising a new VM execution we must first check the homestead
|
||||
// changes.
|
||||
if params.IsHomestead(blockNumber) {
|
||||
if ruleset.IsHomestead(blockNumber) {
|
||||
jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
|
||||
}
|
||||
|
||||
|
@ -3,20 +3,16 @@ package vm
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
params.HomesteadBlock = big.NewInt(1)
|
||||
|
||||
jumpTable := newJumpTable(big.NewInt(0))
|
||||
jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0))
|
||||
if jumpTable[DELEGATECALL].valid {
|
||||
t.Error("Expected DELEGATECALL not to be present")
|
||||
}
|
||||
|
||||
for _, n := range []int64{1, 2, 100} {
|
||||
jumpTable := newJumpTable(big.NewInt(n))
|
||||
jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(n))
|
||||
if !jumpTable[DELEGATECALL].valid {
|
||||
t.Error("Expected DELEGATECALL to be present for block", n)
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ func newLogger(cfg LogConfig, env Environment) *Logger {
|
||||
// captureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// captureState also tracks SSTORE ops to track dirty values.
|
||||
func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
|
||||
func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, depth int, err error) {
|
||||
// short circuit if no log collector is present
|
||||
if l.cfg.Collector == nil {
|
||||
return
|
||||
|
@ -47,7 +47,7 @@ type dummyEnv struct {
|
||||
|
||||
func newDummyEnv(ref *dummyContractRef) *dummyEnv {
|
||||
return &dummyEnv{
|
||||
Env: NewEnv(),
|
||||
Env: NewEnv(true, false),
|
||||
ref: ref,
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,7 @@ func (d dummyEnv) AddStructLog(StructLog) {}
|
||||
|
||||
func TestStoreCapture(t *testing.T) {
|
||||
var (
|
||||
env = NewEnv()
|
||||
env = NewEnv(true, false)
|
||||
logger = newLogger(LogConfig{Collector: env}, env)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
@ -69,7 +69,7 @@ func TestStoreCapture(t *testing.T) {
|
||||
|
||||
var index common.Hash
|
||||
|
||||
logger.captureState(0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, nil)
|
||||
logger.captureState(0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||
if len(logger.changedValues[contract.Address()]) == 0 {
|
||||
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
|
||||
}
|
||||
@ -91,13 +91,13 @@ func TestStorageCapture(t *testing.T) {
|
||||
stack = newstack()
|
||||
)
|
||||
|
||||
logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, nil)
|
||||
logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||
if ref.calledForEach {
|
||||
t.Error("didn't expect for each to be called")
|
||||
}
|
||||
|
||||
logger = newLogger(LogConfig{Collector: env, FullStorage: true}, env)
|
||||
logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, nil)
|
||||
logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||
if !ref.calledForEach {
|
||||
t.Error("expected for each to be called")
|
||||
}
|
||||
|
@ -27,8 +27,9 @@ import (
|
||||
|
||||
// Env is a basic runtime environment required for running the EVM.
|
||||
type Env struct {
|
||||
depth int
|
||||
state *state.StateDB
|
||||
ruleSet vm.RuleSet
|
||||
depth int
|
||||
state *state.StateDB
|
||||
|
||||
origin common.Address
|
||||
coinbase common.Address
|
||||
@ -48,6 +49,7 @@ type Env struct {
|
||||
// NewEnv returns a new vm.Environment
|
||||
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
||||
env := &Env{
|
||||
ruleSet: cfg.RuleSet,
|
||||
state: state,
|
||||
origin: cfg.Origin,
|
||||
coinbase: cfg.Coinbase,
|
||||
@ -56,7 +58,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
||||
difficulty: cfg.Difficulty,
|
||||
gasLimit: cfg.GasLimit,
|
||||
}
|
||||
env.evm = vm.New(env, &vm.Config{
|
||||
env.evm = vm.New(env, vm.Config{
|
||||
Debug: cfg.Debug,
|
||||
EnableJit: !cfg.DisableJit,
|
||||
ForceJit: !cfg.DisableJit,
|
||||
@ -77,6 +79,7 @@ func (self *Env) AddStructLog(log vm.StructLog) {
|
||||
self.logs = append(self.logs, log)
|
||||
}
|
||||
|
||||
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
|
||||
func (self *Env) Vm() vm.Vm { return self.evm }
|
||||
func (self *Env) Origin() common.Address { return self.origin }
|
||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
||||
|
@ -22,13 +22,20 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// The default, always homestead, rule set for the vm env
|
||||
type ruleSet struct{}
|
||||
|
||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
||||
|
||||
// Config is a basic type specifying certain configuration flags for running
|
||||
// the EVM.
|
||||
type Config struct {
|
||||
RuleSet vm.RuleSet
|
||||
Difficulty *big.Int
|
||||
Origin common.Address
|
||||
Coinbase common.Address
|
||||
@ -46,6 +53,10 @@ type Config struct {
|
||||
|
||||
// sets defaults on the config
|
||||
func setDefaults(cfg *Config) {
|
||||
if cfg.RuleSet == nil {
|
||||
cfg.RuleSet = ruleSet{}
|
||||
}
|
||||
|
||||
if cfg.Difficulty == nil {
|
||||
cfg.Difficulty = new(big.Int)
|
||||
}
|
||||
|
9
core/vm/util_test.go
Normal file
9
core/vm/util_test.go
Normal file
@ -0,0 +1,9 @@
|
||||
package vm
|
||||
|
||||
import "math/big"
|
||||
|
||||
type ruleSet struct {
|
||||
hs *big.Int
|
||||
}
|
||||
|
||||
func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
|
@ -43,18 +43,13 @@ type Config struct {
|
||||
type EVM struct {
|
||||
env Environment
|
||||
jumpTable vmJumpTable
|
||||
cfg *Config
|
||||
cfg Config
|
||||
|
||||
logger *Logger
|
||||
}
|
||||
|
||||
// New returns a new instance of the EVM.
|
||||
func New(env Environment, cfg *Config) *EVM {
|
||||
// initialise a default config if none is present
|
||||
if cfg == nil {
|
||||
cfg = new(Config)
|
||||
}
|
||||
|
||||
func New(env Environment, cfg Config) *EVM {
|
||||
var logger *Logger
|
||||
if cfg.Debug {
|
||||
logger = newLogger(cfg.Logger, env)
|
||||
@ -62,7 +57,7 @@ func New(env Environment, cfg *Config) *EVM {
|
||||
|
||||
return &EVM{
|
||||
env: env,
|
||||
jumpTable: newJumpTable(env.BlockNumber()),
|
||||
jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
|
||||
cfg: cfg,
|
||||
logger: logger,
|
||||
}
|
||||
@ -154,7 +149,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||
defer func() {
|
||||
if err != nil && evm.cfg.Debug {
|
||||
evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, err)
|
||||
evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -196,7 +191,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
mem.Resize(newMemSize.Uint64())
|
||||
// Add a log message
|
||||
if evm.cfg.Debug {
|
||||
evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, nil)
|
||||
evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
|
||||
}
|
||||
|
||||
if opPtr := evm.jumpTable[op]; opPtr.valid {
|
||||
|
@ -22,5 +22,5 @@ import "fmt"
|
||||
|
||||
func NewJitVm(env Environment) VirtualMachine {
|
||||
fmt.Printf("Warning! EVM JIT not enabled.\n")
|
||||
return New(env, nil)
|
||||
return New(env, Config{})
|
||||
}
|
||||
|
Reference in New Issue
Block a user