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
@ -168,7 +168,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
evmux := new(event.TypeMux)
|
||||
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
@ -41,15 +41,17 @@ var (
|
||||
//
|
||||
// BlockValidator implements Validator.
|
||||
type BlockValidator struct {
|
||||
bc *BlockChain // Canonical block chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
config *ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator {
|
||||
func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
|
||||
validator := &BlockValidator{
|
||||
Pow: pow,
|
||||
bc: blockchain,
|
||||
config: config,
|
||||
Pow: pow,
|
||||
bc: blockchain,
|
||||
}
|
||||
return validator
|
||||
}
|
||||
@ -80,7 +82,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
|
||||
header := block.Header()
|
||||
// validate the block header
|
||||
if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil {
|
||||
if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// verify the uncles are correctly rewarded
|
||||
@ -175,7 +177,7 @@ func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
|
||||
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
|
||||
}
|
||||
|
||||
if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
|
||||
if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
|
||||
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
|
||||
}
|
||||
}
|
||||
@ -195,13 +197,13 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
|
||||
if v.bc.HasHeader(header.Hash()) {
|
||||
return nil
|
||||
}
|
||||
return ValidateHeader(v.Pow, header, parent, checkPow, false)
|
||||
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
|
||||
}
|
||||
|
||||
// Validates a header. Returns an error if the header is invalid.
|
||||
//
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||
func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
||||
}
|
||||
@ -219,7 +221,7 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che
|
||||
return BlockEqualTSErr
|
||||
}
|
||||
|
||||
expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
if expd.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
|
||||
}
|
||||
@ -251,8 +253,8 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||
// the difficulty that a new block should have when created at time
|
||||
// given the parent block's time and difficulty.
|
||||
func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
if params.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
|
||||
func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
|
||||
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
|
||||
} else {
|
||||
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
|
||||
@ -363,11 +365,11 @@ func CalcGasLimit(parent *types.Block) *big.Int {
|
||||
gl = gl.Add(gl, contrib)
|
||||
gl.Set(common.BigMax(gl, params.MinGasLimit))
|
||||
|
||||
// however, if we're now below the target (GenesisGasLimit) we increase the
|
||||
// however, if we're now below the target (TargetGasLimit) we increase the
|
||||
// limit as much as we can (parentGasLimit / 1024 -1)
|
||||
if gl.Cmp(params.GenesisGasLimit) < 0 {
|
||||
if gl.Cmp(params.TargetGasLimit) < 0 {
|
||||
gl.Add(parent.GasLimit(), decay)
|
||||
gl.Set(common.BigMin(gl, params.GenesisGasLimit))
|
||||
gl.Set(common.BigMin(gl, params.TargetGasLimit))
|
||||
}
|
||||
return gl
|
||||
}
|
||||
|
@ -27,15 +27,20 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
)
|
||||
|
||||
func testChainConfig() *ChainConfig {
|
||||
return &ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}
|
||||
}
|
||||
|
||||
func proc() (Validator, *BlockChain) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var mux event.TypeMux
|
||||
|
||||
WriteTestNetGenesisBlock(db)
|
||||
blockchain, err := NewBlockChain(db, thePow(), &mux)
|
||||
blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
@ -49,13 +54,14 @@ func TestNumber(t *testing.T) {
|
||||
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
|
||||
header := makeHeader(chain.Genesis(), statedb)
|
||||
header.Number = big.NewInt(3)
|
||||
err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
|
||||
cfg := testChainConfig()
|
||||
err := ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
|
||||
if err != BlockNumberErr {
|
||||
t.Errorf("expected block number error, got %q", err)
|
||||
}
|
||||
|
||||
header = makeHeader(chain.Genesis(), statedb)
|
||||
err = ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
|
||||
err = ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
|
||||
if err == BlockNumberErr {
|
||||
t.Errorf("didn't expect block number error")
|
||||
}
|
||||
|
@ -80,11 +80,12 @@ const (
|
||||
// included in the canonical one where as GetBlockByNumber always represents the
|
||||
// canonical chain.
|
||||
type BlockChain struct {
|
||||
config *ChainConfig // chain & network configuration
|
||||
|
||||
hc *HeaderChain
|
||||
chainDb ethdb.Database
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
vmConfig *vm.Config
|
||||
|
||||
mu sync.RWMutex // global mutex for locking chain operations
|
||||
chainmu sync.RWMutex // blockchain insertion lock
|
||||
@ -113,13 +114,14 @@ type BlockChain struct {
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialiser the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
||||
func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
futureBlocks, _ := lru.New(maxFutureBlocks)
|
||||
|
||||
bc := &BlockChain{
|
||||
config: config,
|
||||
chainDb: chainDb,
|
||||
eventMux: mux,
|
||||
quit: make(chan struct{}),
|
||||
@ -129,24 +131,21 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
|
||||
futureBlocks: futureBlocks,
|
||||
pow: pow,
|
||||
}
|
||||
bc.SetValidator(NewBlockValidator(bc, pow))
|
||||
bc.SetProcessor(NewStateProcessor(bc))
|
||||
bc.SetValidator(NewBlockValidator(config, bc, pow))
|
||||
bc.SetProcessor(NewStateProcessor(config, bc))
|
||||
|
||||
gv := func() HeaderValidator { return bc.Validator() }
|
||||
var err error
|
||||
bc.hc, err = NewHeaderChain(chainDb, gv, bc.getProcInterrupt)
|
||||
bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bc.genesisBlock = bc.GetBlockByNumber(0)
|
||||
if bc.genesisBlock == nil {
|
||||
bc.genesisBlock, err = WriteDefaultGenesisBlock(chainDb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
|
||||
return nil, ErrNoGenesis
|
||||
}
|
||||
|
||||
if err := bc.loadLastState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -163,10 +162,6 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
func (self *BlockChain) SetConfig(vmConfig *vm.Config) {
|
||||
self.vmConfig = vmConfig
|
||||
}
|
||||
|
||||
func (self *BlockChain) getProcInterrupt() bool {
|
||||
return atomic.LoadInt32(&self.procInterrupt) == 1
|
||||
}
|
||||
@ -896,7 +891,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
return i, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.vmConfig)
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
|
@ -53,7 +53,7 @@ func thePow() pow.PoW {
|
||||
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
|
||||
var eventMux event.TypeMux
|
||||
WriteTestNetGenesisBlock(db)
|
||||
blockchain, err := NewBlockChain(db, thePow(), &eventMux)
|
||||
blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux)
|
||||
if err != nil {
|
||||
t.Error("failed creating blockchain:", err)
|
||||
t.FailNow()
|
||||
@ -141,7 +141,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, nil)
|
||||
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return err
|
||||
@ -435,7 +435,7 @@ func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return n
|
||||
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||
func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
|
||||
@ -473,13 +473,14 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
|
||||
func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
|
||||
var eventMux event.TypeMux
|
||||
bc := &BlockChain{
|
||||
chainDb: db,
|
||||
chainDb: db,
|
||||
genesisBlock: genesis,
|
||||
eventMux: &eventMux,
|
||||
pow: FakePow{},
|
||||
eventMux: &eventMux,
|
||||
pow: FakePow{},
|
||||
config: testChainConfig(),
|
||||
}
|
||||
valFn := func() HeaderValidator { return bc.Validator() }
|
||||
bc.hc, _ = NewHeaderChain(db, valFn, bc.getProcInterrupt)
|
||||
bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt)
|
||||
bc.bodyCache, _ = lru.New(100)
|
||||
bc.bodyRLPCache, _ = lru.New(100)
|
||||
bc.blockCache, _ = lru.New(100)
|
||||
@ -613,7 +614,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
|
||||
defer func() { delete(BadHashes, headers[3].Hash()) }()
|
||||
}
|
||||
// Create a new chain manager and check it rolled back the state
|
||||
ncm, err := NewBlockChain(db, FakePow{}, new(event.TypeMux))
|
||||
ncm, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
@ -667,7 +668,7 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||
failHash = headers[failAt].Hash()
|
||||
|
||||
blockchain.pow = failPow{failNum}
|
||||
blockchain.validator = NewBlockValidator(blockchain, failPow{failNum})
|
||||
blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum})
|
||||
|
||||
failRes, err = blockchain.InsertHeaderChain(headers, 1)
|
||||
}
|
||||
@ -733,7 +734,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
|
||||
archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux))
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
@ -741,7 +742,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
// Fast import the chain as a non-archive node to test
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
||||
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
|
||||
fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux))
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
@ -817,7 +818,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
|
||||
archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux))
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
@ -829,7 +830,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
||||
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
|
||||
fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux))
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
@ -848,7 +849,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
|
||||
light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux))
|
||||
light, _ := NewBlockChain(lightDb, testChainConfig(), FakePow{}, new(event.TypeMux))
|
||||
|
||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||
@ -913,7 +914,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
})
|
||||
// Import the chain. This runs all block validation rules.
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||
}
|
||||
@ -986,7 +987,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
)
|
||||
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||
|
||||
subs := evmux.Subscribe(RemovedLogsEvent{})
|
||||
chain, _ := GenerateChain(genesis, db, 2, func(i int, gen *BlockGen) {
|
||||
@ -1022,7 +1023,7 @@ func TestReorgSideEvent(t *testing.T) {
|
||||
)
|
||||
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||
|
||||
chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
if i == 2 {
|
||||
|
@ -23,11 +23,30 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
)
|
||||
|
||||
/*
|
||||
* TODO: move this to another package.
|
||||
*/
|
||||
|
||||
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
|
||||
func MakeChainConfig(testnet bool) *ChainConfig {
|
||||
homesteadBlock := params.MainNetHomesteadBlock
|
||||
// set a different default homestead block for the testnet
|
||||
if testnet {
|
||||
homesteadBlock = params.TestNetHomesteadBlock
|
||||
}
|
||||
|
||||
return &ChainConfig{
|
||||
HomesteadBlock: homesteadBlock,
|
||||
}
|
||||
}
|
||||
|
||||
// FakePow is a non-validating proof of work implementation.
|
||||
// It returns true from Verify for any block.
|
||||
type FakePow struct{}
|
||||
@ -91,7 +110,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
||||
b.SetCoinbase(common.Address{})
|
||||
}
|
||||
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
receipt, _, _, err := ApplyTransaction(nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, nil)
|
||||
receipt, _, _, err := ApplyTransaction(MakeChainConfig(true), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -148,7 +167,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
|
||||
panic("block time out of range")
|
||||
}
|
||||
b.header.Difficulty = CalcDifficulty(b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
|
||||
b.header.Difficulty = CalcDifficulty(MakeChainConfig(true), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
|
||||
}
|
||||
|
||||
// GenerateChain creates a chain of n blocks. The first block's
|
||||
@ -203,7 +222,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
||||
Root: state.IntermediateRoot(),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: CalcDifficulty(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||
Difficulty: CalcDifficulty(MakeChainConfig(true), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||
GasLimit: CalcGasLimit(parent),
|
||||
GasUsed: new(big.Int),
|
||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
@ -222,7 +241,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
blockchain, _ := NewBlockChain(db, MakeChainConfig(false), FakePow{}, evmux)
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
return db, blockchain, nil
|
||||
|
@ -77,7 +77,7 @@ func ExampleGenerateChain() {
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
blockchain, _ := NewBlockChain(db, MakeChainConfig(true), FakePow{}, evmux)
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
fmt.Printf("insert error (block %d): %v\n", i, err)
|
||||
return
|
||||
|
46
core/config.go
Normal file
46
core/config.go
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
//
|
||||
// ChainConfig is stored in the database on a per block basis. This means
|
||||
// that any network, identified by its genesis block, can have its own
|
||||
// set of configuration options.
|
||||
type ChainConfig struct {
|
||||
HomesteadBlock *big.Int // homestead switch block
|
||||
|
||||
VmConfig vm.Config `json:"-"`
|
||||
}
|
||||
|
||||
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
||||
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
|
||||
if num == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return num.Cmp(c.HomesteadBlock) >= 0
|
||||
}
|
@ -19,6 +19,7 @@ package core
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
@ -50,6 +51,8 @@ var (
|
||||
MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000}
|
||||
|
||||
blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
|
||||
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
)
|
||||
|
||||
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
|
||||
@ -527,3 +530,34 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) {
|
||||
enc, _ := rlp.EncodeToBytes(uint(vsn))
|
||||
db.Put([]byte("BlockchainVersion"), enc)
|
||||
}
|
||||
|
||||
// WriteChainConfig writes the chain config settings to the database.
|
||||
func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error {
|
||||
// short circuit and ignore if nil config. GetChainConfig
|
||||
// will return a default.
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
jsonChainConfig, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Put(append(configPrefix, hash[:]...), jsonChainConfig)
|
||||
}
|
||||
|
||||
// GetChainConfig will fetch the network settings based on the given hash.
|
||||
func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) {
|
||||
jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...))
|
||||
if len(jsonChainConfig) == 0 {
|
||||
return nil, ChainConfigNotFoundErr
|
||||
}
|
||||
|
||||
var config ChainConfig
|
||||
if err := json.Unmarshal(jsonChainConfig, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.SetSnapshot(snapshotPreTransfer)
|
||||
|
@ -43,15 +43,16 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
||||
}
|
||||
|
||||
var genesis struct {
|
||||
Nonce string
|
||||
Timestamp string
|
||||
ParentHash string
|
||||
ExtraData string
|
||||
GasLimit string
|
||||
Difficulty string
|
||||
Mixhash string
|
||||
Coinbase string
|
||||
Alloc map[string]struct {
|
||||
ChainConfig *ChainConfig
|
||||
Nonce string
|
||||
Timestamp string
|
||||
ParentHash string
|
||||
ExtraData string
|
||||
GasLimit string
|
||||
Difficulty string
|
||||
Mixhash string
|
||||
Coinbase string
|
||||
Alloc map[string]struct {
|
||||
Code string
|
||||
Storage map[string]string
|
||||
Balance string
|
||||
@ -114,6 +115,10 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
||||
if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,8 @@ import (
|
||||
// It is not thread safe either, the encapsulating chain structures should do
|
||||
// the necessary mutex locking/unlocking.
|
||||
type HeaderChain struct {
|
||||
config *ChainConfig
|
||||
|
||||
chainDb ethdb.Database
|
||||
genesisHeader *types.Header
|
||||
|
||||
@ -62,7 +64,7 @@ type getHeaderValidatorFn func() HeaderValidator
|
||||
// getValidator should return the parent's validator
|
||||
// procInterrupt points to the parent's interrupt semaphore
|
||||
// wg points to the parent's shutdown wait group
|
||||
func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
headerCache, _ := lru.New(headerCacheLimit)
|
||||
tdCache, _ := lru.New(tdCacheLimit)
|
||||
|
||||
@ -73,6 +75,7 @@ func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, p
|
||||
}
|
||||
|
||||
hc := &HeaderChain{
|
||||
config: config,
|
||||
chainDb: chainDb,
|
||||
headerCache: headerCache,
|
||||
tdCache: tdCache,
|
||||
@ -436,15 +439,17 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
|
||||
//
|
||||
// headerValidator implements HeaderValidator.
|
||||
type headerValidator struct {
|
||||
hc *HeaderChain // Canonical header chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
config *ChainConfig
|
||||
hc *HeaderChain // Canonical header chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewHeaderValidator(chain *HeaderChain, pow pow.PoW) HeaderValidator {
|
||||
func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
|
||||
return &headerValidator{
|
||||
Pow: pow,
|
||||
hc: chain,
|
||||
config: config,
|
||||
Pow: pow,
|
||||
hc: chain,
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,5 +465,5 @@ func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow
|
||||
if v.hc.HasHeader(header.Hash()) {
|
||||
return nil
|
||||
}
|
||||
return ValidateHeader(v.Pow, header, parent, checkPow, false)
|
||||
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
|
||||
}
|
||||
|
@ -16,12 +16,21 @@ var (
|
||||
big32 = big.NewInt(32)
|
||||
)
|
||||
|
||||
// StateProcessor is a basic Processor, which takes care of transitioning
|
||||
// state from one point to another.
|
||||
//
|
||||
// StateProcessor implements Processor.
|
||||
type StateProcessor struct {
|
||||
bc *BlockChain
|
||||
config *ChainConfig
|
||||
bc *BlockChain
|
||||
}
|
||||
|
||||
func NewStateProcessor(bc *BlockChain) *StateProcessor {
|
||||
return &StateProcessor{bc}
|
||||
// NewStateProcessor initialises a new StateProcessor.
|
||||
func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
|
||||
return &StateProcessor{
|
||||
config: config,
|
||||
bc: bc,
|
||||
}
|
||||
}
|
||||
|
||||
// Process processes the state changes according to the Ethereum rules by running
|
||||
@ -31,7 +40,7 @@ func NewStateProcessor(bc *BlockChain) *StateProcessor {
|
||||
// Process returns the receipts and logs accumulated during the process and
|
||||
// returns the amount of gas that was used in the process. If any of the
|
||||
// transactions failed to execute due to insufficient gas it will return an error.
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
totalUsedGas = big.NewInt(0)
|
||||
@ -43,7 +52,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
|
||||
for i, tx := range block.Transactions() {
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, totalUsedGas, err
|
||||
}
|
||||
@ -60,8 +69,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
//
|
||||
// ApplyTransactions returns the generated receipts and vm logs during the
|
||||
// execution of the state transition phase.
|
||||
func ApplyTransaction(bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg *vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, bc, tx, header, cfg), tx, gp)
|
||||
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ func (self *StateTransition) from() (vm.Account, error) {
|
||||
f common.Address
|
||||
err error
|
||||
)
|
||||
if params.IsHomestead(self.env.BlockNumber()) {
|
||||
if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
|
||||
f, err = self.msg.From()
|
||||
} else {
|
||||
f, err = self.msg.FromFrontier()
|
||||
@ -216,7 +216,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
|
||||
msg := self.msg
|
||||
sender, _ := self.from() // err checked in preCheck
|
||||
|
||||
homestead := params.IsHomestead(self.env.BlockNumber())
|
||||
homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
|
||||
contractCreation := MessageCreatesContract(msg)
|
||||
// Pay intrinsic gas
|
||||
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
|
||||
|
@ -30,7 +30,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -60,6 +59,7 @@ type stateFn func() (*state.StateDB, error)
|
||||
// current state) and future transactions. Transactions move between those
|
||||
// two states over time as they are received and processed.
|
||||
type TxPool struct {
|
||||
config *ChainConfig
|
||||
quit chan bool // Quitting channel
|
||||
currentState stateFn // The state function which will allow us to do some pre checks
|
||||
pendingState *state.ManagedState
|
||||
@ -75,8 +75,9 @@ type TxPool struct {
|
||||
homestead bool
|
||||
}
|
||||
|
||||
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
|
||||
func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
|
||||
pool := &TxPool{
|
||||
config: config,
|
||||
pending: make(map[common.Hash]*types.Transaction),
|
||||
queue: make(map[common.Address]map[common.Hash]*types.Transaction),
|
||||
quit: make(chan bool),
|
||||
@ -102,7 +103,7 @@ func (pool *TxPool) eventLoop() {
|
||||
switch ev := ev.Data.(type) {
|
||||
case ChainHeadEvent:
|
||||
pool.mu.Lock()
|
||||
if ev.Block != nil && params.IsHomestead(ev.Block.Number()) {
|
||||
if ev.Block != nil && pool.config.IsHomestead(ev.Block.Number()) {
|
||||
pool.homestead = true
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||
|
||||
var m event.TypeMux
|
||||
key, _ := crypto.GenerateKey()
|
||||
newPool := NewTxPool(&m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
newPool := NewTxPool(testChainConfig(), &m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
newPool.resetState()
|
||||
return newPool, key
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ type HeaderValidator interface {
|
||||
// of gas used in the process and return an error if any of the internal rules
|
||||
// failed.
|
||||
type Processor interface {
|
||||
Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
|
||||
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
|
||||
}
|
||||
|
||||
// Backend is an interface defining the basic functionality for an operable node
|
||||
|
@ -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{})
|
||||
}
|
||||
|
@ -41,30 +41,26 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
|
||||
}
|
||||
|
||||
type VMEnv struct {
|
||||
state *state.StateDB // State to use for executing
|
||||
evm *vm.EVM // The Ethereum Virtual Machine
|
||||
depth int // Current execution depth
|
||||
msg Message // Message appliod
|
||||
chainConfig *ChainConfig // Chain configuration
|
||||
state *state.StateDB // State to use for executing
|
||||
evm *vm.EVM // The Ethereum Virtual Machine
|
||||
depth int // Current execution depth
|
||||
msg Message // Message appliod
|
||||
|
||||
header *types.Header // Header information
|
||||
chain *BlockChain // Blockchain handle
|
||||
logs []vm.StructLog // Logs for the custom structured logger
|
||||
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
|
||||
|
||||
}
|
||||
|
||||
func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.Header, cfg *vm.Config) *VMEnv {
|
||||
func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
|
||||
env := &VMEnv{
|
||||
chain: chain,
|
||||
state: state,
|
||||
header: header,
|
||||
msg: msg,
|
||||
getHashFn: GetHashFn(header.ParentHash, chain),
|
||||
}
|
||||
|
||||
// initialise a default config if none present
|
||||
if cfg == nil {
|
||||
cfg = new(vm.Config)
|
||||
chainConfig: chainConfig,
|
||||
chain: chain,
|
||||
state: state,
|
||||
header: header,
|
||||
msg: msg,
|
||||
getHashFn: GetHashFn(header.ParentHash, chain),
|
||||
}
|
||||
|
||||
// if no log collector is present set self as the collector
|
||||
@ -76,6 +72,7 @@ func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.
|
||||
return env
|
||||
}
|
||||
|
||||
func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
|
||||
|
Reference in New Issue
Block a user