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:
Jeffrey Wilcke
2016-03-01 23:32:43 +01:00
committed by Jeffrey Wilcke
parent 10d3466c93
commit f0cbebb19f
55 changed files with 732 additions and 431 deletions

View File

@ -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

View File

@ -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))

View File

@ -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++

View File

@ -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) }

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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")
}

View File

@ -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 }

View File

@ -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
View 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 }

View File

@ -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 {

View File

@ -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{})
}