cmd,consensus,core,eth,params,tests: articulate EIP features

Refactors chain configuration and respective feature
implementations to use EIP definitions and methods,
instead of HardFork names, whenever possible. Doing so
attempts to address ambiguity and complexity in chain
configuration and feature implementation.

Signed-off-by: Isaac Ardis (isaac.ardis@gmail.com)
This commit is contained in:
ia 2019-01-04 08:36:55 -06:00
parent 56a3f6c03c
commit 717df21245
No known key found for this signature in database
GPG key ID: F36487257419D94C
19 changed files with 923 additions and 233 deletions

View file

@ -421,7 +421,7 @@ func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBu
} }
func (spec *parityChainSpec) setByzantium(num *big.Int) { func (spec *parityChainSpec) setByzantium(num *big.Int) {
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ByzantiumBlockReward) spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.EIP649FBlockReward)
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000) spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000)
n := hexutil.Uint64(num.Uint64()) n := hexutil.Uint64(num.Uint64())
spec.Engine.Ethash.Params.EIP100bTransition = n spec.Engine.Ethash.Params.EIP100bTransition = n
@ -432,7 +432,7 @@ func (spec *parityChainSpec) setByzantium(num *big.Int) {
} }
func (spec *parityChainSpec) setConstantinople(num *big.Int) { func (spec *parityChainSpec) setConstantinople(num *big.Int) {
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ConstantinopleBlockReward) spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.EIP1234FBlockReward)
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000) spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000)
n := hexutil.Uint64(num.Uint64()) n := hexutil.Uint64(num.Uint64())
spec.Params.EIP145Transition = n spec.Params.EIP145Transition = n

View file

@ -581,7 +581,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
// rewards given, and returns the final block. // rewards given, and returns the final block.
func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped // No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP161F(header.Number))
header.UncleHash = types.CalcUncleHash(nil) header.UncleHash = types.CalcUncleHash(nil)
// Assemble and return the final block for sealing // Assemble and return the final block for sealing

View file

@ -38,23 +38,25 @@ import (
// Ethash proof-of-work protocol constants. // Ethash proof-of-work protocol constants.
var ( var (
FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium EIP649FBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople EIP1234FBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople
maxUncles = 2 // Maximum number of uncles allowed in a single block maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks
// calcDifficultyConstantinople is the difficulty adjustment algorithm for Constantinople. // calcDifficultyEIP1234 is the difficulty adjustment algorithm for Constantinople.
// It returns the difficulty that a new block should have when created at time given the // It returns the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules, but with // parent block's time and difficulty. The calculation uses the Byzantium rules, but with
// bomb offset 5M. // bomb offset 5M.
// Specification EIP-1234: https://eips.ethereum.org/EIPS/eip-1234 // Specification EIP-1234: https://eips.ethereum.org/EIPS/eip-1234
calcDifficultyConstantinople = makeDifficultyCalculator(big.NewInt(5000000)) calcDifficultyEIP1234 = makeDifficultyCalculator(big.NewInt(5000000))
// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns // calcDifficultyByzantium is the difficulty adjustment algorithm for Byzantium. It returns
// the difficulty that a new block should have when created at time given the // the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules. // parent block's time and difficulty. The calculation uses the Byzantium rules.
// Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649 // Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649
// Related meta-ish EIP-669: https://github.com/ethereum/EIPs/pull/669
// Note that this calculator also includes the change from EIP100.
calcDifficultyByzantium = makeDifficultyCalculator(big.NewInt(3000000)) calcDifficultyByzantium = makeDifficultyCalculator(big.NewInt(3000000))
) )
@ -313,10 +315,16 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, p
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1) next := new(big.Int).Add(parent.Number, big1)
switch { switch {
case config.IsConstantinople(next): case config.IsEIP1234F(next):
return calcDifficultyConstantinople(time, parent) return calcDifficultyEIP1234(time, parent)
case config.IsByzantium(next): case config.IsByzantium(next) || (config.IsEIP649F(next) && config.IsEIP100F(next)):
return calcDifficultyByzantium(time, parent) return calcDifficultyByzantium(time, parent)
case config.IsEIP649F(next):
// TODO: calculator for only EIP649:difficulty bomb delay (without EIP100:mean time adjustment)
panic("not implemented")
case config.IsEIP100F(next):
// TODO: calculator for only EIP100:mean time adjustment (without EIP649:difficulty bomb delay)
panic("not implemented")
case config.IsHomestead(next): case config.IsHomestead(next):
return calcDifficultyHomestead(time, parent) return calcDifficultyHomestead(time, parent)
default: default:
@ -567,7 +575,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles) accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP161F(header.Number))
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts), nil return types.NewBlock(header, txs, uncles, receipts), nil
@ -608,11 +616,11 @@ var (
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
// Select the correct block reward based on chain progression // Select the correct block reward based on chain progression
blockReward := FrontierBlockReward blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) { if config.IsEIP649F(header.Number) {
blockReward = ByzantiumBlockReward blockReward = EIP649FBlockReward
} }
if config.IsConstantinople(header.Number) { if config.IsEIP1234F(header.Number) {
blockReward = ConstantinopleBlockReward blockReward = EIP1234FBlockReward
} }
// Accumulate the rewards for the miner and any included uncles // Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward) reward := new(big.Int).Set(blockReward)

View file

@ -95,7 +95,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
} }
// Validate the state root against the received state root and throw // Validate the state root against the received state root and throw
// an error if they don't match. // an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { if root := statedb.IntermediateRoot(v.config.IsEIP161F(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
} }
return nil return nil

View file

@ -43,7 +43,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
) )
var ( var (
@ -946,7 +946,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
} }
rawdb.WriteBlock(bc.db, block) rawdb.WriteBlock(bc.db, block)
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number())) root, err := state.Commit(bc.chainConfig.IsEIP161F(block.Number()))
if err != nil { if err != nil {
return NonStatTy, err return NonStatTy, err
} }

View file

@ -200,7 +200,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
block, _ := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts) block, _ := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
// Write state changes to db // Write state changes to db
root, err := statedb.Commit(config.IsEIP158(b.header.Number)) root, err := statedb.Commit(config.IsEIP161F(b.header.Number))
if err != nil { if err != nil {
panic(fmt.Sprintf("state write error: %v", err)) panic(fmt.Sprintf("state write error: %v", err))
} }
@ -233,7 +233,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
} }
return &types.Header{ return &types.Header{
Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), Root: state.IntermediateRoot(chain.Config().IsEIP161F(parent.Number())),
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(), Coinbase: parent.Coinbase(),
Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{ Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{

View file

@ -102,10 +102,10 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
} }
// Update the state with pending changes // Update the state with pending changes
var root []byte var root []byte
if config.IsByzantium(header.Number) { if config.IsEIP658F(header.Number) {
statedb.Finalise(true) statedb.Finalise(true)
} else { } else {
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() root = statedb.IntermediateRoot(config.IsEIP161F(header.Number)).Bytes()
} }
*usedGas += gas *usedGas += gas

View file

@ -37,18 +37,8 @@ type PrecompiledContract interface {
Run(input []byte) ([]byte, error) // Run runs the precompiled contract Run(input []byte) ([]byte, error) // Run runs the precompiled contract
} }
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // AllPrecompiledContracts returns all possible precompiled contracts.
// contracts used in the Frontier and Homestead releases. var AllPrecompiledContracts = map[common.Address]PrecompiledContract{
var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
}
// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
// contracts used in the Byzantium release.
var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{}, common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{},
@ -59,6 +49,27 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{8}): &bn256Pairing{}, common.BytesToAddress([]byte{8}): &bn256Pairing{},
} }
// IsPrecompiledContractEnabled checks whether a given precompiled contract is enabled for a chain config at a given block.
func IsPrecompiledContractEnabled(config *params.ChainConfig, num *big.Int, codeAddr common.Address) bool {
switch codeAddr {
case common.BytesToAddress([]byte{1}),
common.BytesToAddress([]byte{2}),
common.BytesToAddress([]byte{3}),
common.BytesToAddress([]byte{4}):
return true
case common.BytesToAddress([]byte{5}):
return config.IsEIP198F(num)
case common.BytesToAddress([]byte{6}):
return config.IsEIP213F(num)
case common.BytesToAddress([]byte{7}):
return config.IsEIP213F(num)
case common.BytesToAddress([]byte{8}):
return config.IsEIP212F(num)
default:
return false
}
}
// RunPrecompiledContract runs and evaluates the output of a precompiled contract. // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input) gas := p.RequiredGas(input)

View file

@ -21,6 +21,8 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -337,9 +339,10 @@ var bn256PairingTests = []precompiledTest{
} }
func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
p := PrecompiledContractsByzantium[common.HexToAddress(addr)] p := AllPrecompiledContracts[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in)) nil, new(big.Int), p.RequiredGas(in))
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
if res, err := RunPrecompiledContract(p, in, contract); err != nil { if res, err := RunPrecompiledContract(p, in, contract); err != nil {
@ -350,11 +353,100 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
}) })
} }
func TestIsPrecompiledContractEnabled(t *testing.T) {
var homeCts = []common.Address{
common.BytesToAddress([]byte{1}),
common.BytesToAddress([]byte{2}),
common.BytesToAddress([]byte{3}),
common.BytesToAddress([]byte{4}),
}
var byzUniqCts = []common.Address{
common.BytesToAddress([]byte{5}),
common.BytesToAddress([]byte{6}),
common.BytesToAddress([]byte{7}),
common.BytesToAddress([]byte{8}),
}
var byzCts = append(homeCts, byzUniqCts...)
var nonCts = []common.Address{
common.Address{},
common.BytesToAddress([]byte{42}),
common.HexToAddress("0xdeadbeef"),
}
type c struct {
addr common.Address
config *params.ChainConfig
blockNum *big.Int
want bool
}
cases := []c{}
addCaseWhere := func(config *params.ChainConfig, addr common.Address, bn *big.Int, want bool) {
cases = append(cases, c{
addr: addr,
config: config,
blockNum: bn,
want: want,
})
}
for _, a := range homeCts {
addCaseWhere(params.AllEthashProtocolChanges, a, big.NewInt(0), true)
addCaseWhere(params.MainnetChainConfig, a, big.NewInt(0), true)
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Sub(params.MainnetChainConfig.ByzantiumBlock, common.Big1), true)
addCaseWhere(params.MainnetChainConfig, a, params.MainnetChainConfig.ByzantiumBlock, true)
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Add(params.MainnetChainConfig.ByzantiumBlock, common.Big1), true)
}
for _, a := range byzUniqCts {
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Sub(params.MainnetChainConfig.ByzantiumBlock, common.Big1), false)
addCaseWhere(params.MainnetChainConfig, a, params.MainnetChainConfig.ByzantiumBlock, true)
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Add(params.MainnetChainConfig.ByzantiumBlock, common.Big1), true)
}
for _, a := range byzCts {
addCaseWhere(params.AllEthashProtocolChanges, a, big.NewInt(0), true)
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Add(params.MainnetChainConfig.ByzantiumBlock, common.Big1), true)
}
for _, a := range nonCts {
addCaseWhere(params.AllEthashProtocolChanges, a, big.NewInt(0), false)
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Sub(params.MainnetChainConfig.ByzantiumBlock, common.Big1), false)
addCaseWhere(params.MainnetChainConfig, a, new(big.Int).Add(params.MainnetChainConfig.ByzantiumBlock, common.Big1), false)
}
for i, c := range cases {
got := IsPrecompiledContractEnabled(c.config, c.blockNum, c.addr)
if c.want != got {
t.Errorf("test: %d, address: %x, want: %v, got: %v", i, c.addr, c.want, got)
}
// test 1:1 with pre-existing hard-fork implementation style in *evm#Call
precomps := map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
}
if c.config.IsByzantium(c.blockNum) {
precomps = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256Add{},
common.BytesToAddress([]byte{7}): &bn256ScalarMul{},
common.BytesToAddress([]byte{8}): &bn256Pairing{},
}
}
expect := precomps[c.addr] == nil
got = !IsPrecompiledContractEnabled(c.config, c.blockNum, c.addr)
if got != expect {
t.Errorf("addr: %x, bn: %v, want: %v, got: %v", c.addr, c.blockNum, c.want, got)
}
}
}
func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
if test.noBenchmark { if test.noBenchmark {
return return
} }
p := PrecompiledContractsByzantium[common.HexToAddress(addr)] p := AllPrecompiledContracts[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
reqGas := p.RequiredGas(in) reqGas := p.RequiredGas(in)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),

View file

@ -43,12 +43,8 @@ type (
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
if contract.CodeAddr != nil { if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead if IsPrecompiledContractEnabled(evm.ChainConfig(), evm.BlockNumber, *contract.CodeAddr) {
if evm.ChainConfig().IsByzantium(evm.BlockNumber) { return RunPrecompiledContract(AllPrecompiledContracts[*contract.CodeAddr], input, contract)
precompiles = PrecompiledContractsByzantium
}
if p := precompiles[*contract.CodeAddr]; p != nil {
return RunPrecompiledContract(p, input, contract)
} }
} }
for _, interpreter := range evm.interpreters { for _, interpreter := range evm.interpreters {
@ -197,11 +193,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
snapshot = evm.StateDB.Snapshot() snapshot = evm.StateDB.Snapshot()
) )
if !evm.StateDB.Exist(addr) { if !evm.StateDB.Exist(addr) {
precompiles := PrecompiledContractsHomestead if !IsPrecompiledContractEnabled(evm.ChainConfig(), evm.BlockNumber, addr) && evm.ChainConfig().IsEIP161F(evm.BlockNumber) && value.Sign() == 0 {
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
precompiles = PrecompiledContractsByzantium
}
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer // Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 { if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
@ -391,7 +383,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Create a new account on the state // Create a new account on the state
snapshot := evm.StateDB.Snapshot() snapshot := evm.StateDB.Snapshot()
evm.StateDB.CreateAccount(address) evm.StateDB.CreateAccount(address)
if evm.ChainConfig().IsEIP158(evm.BlockNumber) { if evm.ChainConfig().IsEIP161F(evm.BlockNumber) {
evm.StateDB.SetNonce(address, 1) evm.StateDB.SetNonce(address, 1)
} }
evm.Transfer(evm.StateDB, caller.Address(), address, value) evm.Transfer(evm.StateDB, caller.Address(), address, value)
@ -414,7 +406,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
ret, err := run(evm, contract, nil, false) ret, err := run(evm, contract, nil, false)
// 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().IsEIP170F(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
// calculate the gas required to store the code. If the code could not // calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled // be stored due to not enough gas set an error and let it be handled

View file

@ -121,7 +121,7 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
) )
// The legacy gas metering only takes into consideration the current state // The legacy gas metering only takes into consideration the current state
if !evm.chainRules.IsConstantinople { if !evm.chainRules.IsEIP1283F {
// This checks for 3 scenario's and calculates gas accordingly: // This checks for 3 scenario's and calculates gas accordingly:
// //
// 1. From a zero-value address to a non-zero value (NEW VALUE) // 1. From a zero-value address to a non-zero value (NEW VALUE)
@ -391,7 +391,7 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
gas = gt.Calls gas = gt.Calls
transfersValue = stack.Back(2).Sign() != 0 transfersValue = stack.Back(2).Sign() != 0
address = common.BigToAddress(stack.Back(1)) address = common.BigToAddress(stack.Back(1))
eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) eip158 = evm.ChainConfig().IsEIP161F(evm.BlockNumber)
) )
if eip158 { if eip158 {
if transfersValue && evm.StateDB.Empty(address) { if transfersValue && evm.StateDB.Empty(address) {
@ -461,7 +461,7 @@ func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
gas = gt.Suicide gas = gt.Suicide
var ( var (
address = common.BigToAddress(stack.Back(0)) address = common.BigToAddress(stack.Back(0))
eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) eip158 = evm.ChainConfig().IsEIP161F(evm.BlockNumber)
) )
if eip158 { if eip158 {

View file

@ -99,16 +99,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// the jump table was initialised. If it was not // the jump table was initialised. If it was not
// we'll set the default jump table. // we'll set the default jump table.
if !cfg.JumpTable[STOP].valid { if !cfg.JumpTable[STOP].valid {
switch { cfg.JumpTable = baseInstructionSet
case evm.ChainConfig().IsConstantinople(evm.BlockNumber):
cfg.JumpTable = constantinopleInstructionSet
case evm.ChainConfig().IsByzantium(evm.BlockNumber):
cfg.JumpTable = byzantiumInstructionSet
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
cfg.JumpTable = homesteadInstructionSet
default:
cfg.JumpTable = frontierInstructionSet
}
} }
return &EVMInterpreter{ return &EVMInterpreter{
@ -119,7 +110,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
} }
func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
if in.evm.chainRules.IsByzantium { // STATICCALL
if in.evm.chainRules.IsEIP214F {
if in.readOnly { if in.readOnly {
// If the interpreter is operating in readonly mode, make sure no // If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item // state-modifying operation is performed. The 3rd stack item
@ -131,6 +123,36 @@ func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, st
} }
} }
} }
switch op {
case DELEGATECALL:
if !in.evm.chainRules.IsEIP7F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case REVERT:
if !in.evm.chainRules.IsEIP140F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case STATICCALL:
if !in.evm.chainRules.IsEIP214F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case RETURNDATACOPY, RETURNDATASIZE:
if !in.evm.chainRules.IsEIP211F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case SHL, SHR, SAR:
if !in.evm.chainRules.IsEIP145F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case CREATE2:
if !in.evm.chainRules.IsEIP1014F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case EXTCODEHASH:
if !in.evm.chainRules.IsEIP1052F {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
}
return nil return nil
} }
@ -216,13 +238,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
if !operation.valid { if !operation.valid {
return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
} }
if err := operation.validateStack(stack); err != nil {
return nil, err
}
// If the operation is valid, enforce and write restrictions // If the operation is valid, enforce and write restrictions
if err := in.enforceRestrictions(op, operation, stack); err != nil { if err := in.enforceRestrictions(op, operation, stack); err != nil {
return nil, err return nil, err
} }
if err := operation.validateStack(stack); err != nil {
return nil, err
}
var memorySize uint64 var memorySize uint64
// calculate the new memory size and expand the memory to fit // calculate the new memory size and expand the memory to fit

View file

@ -48,112 +48,13 @@ type operation struct {
valid bool // indication whether the retrieved operation is valid and known valid bool // indication whether the retrieved operation is valid and known
reverts bool // determines whether the operation reverts state (implicitly halts) reverts bool // determines whether the operation reverts state (implicitly halts)
returns bool // determines whether the operations sets the return data content returns bool // determines whether the operations sets the return data content
} }
var ( var baseInstructionSet = newInstructionSet()
frontierInstructionSet = newFrontierInstructionSet()
homesteadInstructionSet = newHomesteadInstructionSet()
byzantiumInstructionSet = newByzantiumInstructionSet()
constantinopleInstructionSet = newConstantinopleInstructionSet()
)
// NewConstantinopleInstructionSet returns the frontier, homestead // newInstructionSet returns all available instructions.
// byzantium and contantinople instructions. func newInstructionSet() [256]operation {
func newConstantinopleInstructionSet() [256]operation {
// instructions that can be executed during the byzantium phase.
instructionSet := newByzantiumInstructionSet()
instructionSet[SHL] = operation{
execute: opSHL,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
}
instructionSet[SHR] = operation{
execute: opSHR,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
}
instructionSet[SAR] = operation{
execute: opSAR,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
}
instructionSet[EXTCODEHASH] = operation{
execute: opExtCodeHash,
gasCost: gasExtCodeHash,
validateStack: makeStackFunc(1, 1),
valid: true,
}
instructionSet[CREATE2] = operation{
execute: opCreate2,
gasCost: gasCreate2,
validateStack: makeStackFunc(4, 1),
memorySize: memoryCreate2,
valid: true,
writes: true,
returns: true,
}
return instructionSet
}
// NewByzantiumInstructionSet returns the frontier, homestead and
// byzantium instructions.
func newByzantiumInstructionSet() [256]operation {
// instructions that can be executed during the homestead phase.
instructionSet := newHomesteadInstructionSet()
instructionSet[STATICCALL] = operation{
execute: opStaticCall,
gasCost: gasStaticCall,
validateStack: makeStackFunc(6, 1),
memorySize: memoryStaticCall,
valid: true,
returns: true,
}
instructionSet[RETURNDATASIZE] = operation{
execute: opReturnDataSize,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
}
instructionSet[RETURNDATACOPY] = operation{
execute: opReturnDataCopy,
gasCost: gasReturnDataCopy,
validateStack: makeStackFunc(3, 0),
memorySize: memoryReturnDataCopy,
valid: true,
}
instructionSet[REVERT] = operation{
execute: opRevert,
gasCost: gasRevert,
validateStack: makeStackFunc(2, 0),
memorySize: memoryRevert,
valid: true,
reverts: true,
returns: true,
}
return instructionSet
}
// NewHomesteadInstructionSet returns the frontier and homestead
// instructions that can be executed during the homestead phase.
func newHomesteadInstructionSet() [256]operation {
instructionSet := newFrontierInstructionSet()
instructionSet[DELEGATECALL] = operation{
execute: opDelegateCall,
gasCost: gasDelegateCall,
validateStack: makeStackFunc(6, 1),
memorySize: memoryDelegateCall,
valid: true,
returns: true,
}
return instructionSet
}
// NewFrontierInstructionSet returns the frontier instructions
// that can be executed during the frontier phase.
func newFrontierInstructionSet() [256]operation {
return [256]operation{ return [256]operation{
STOP: { STOP: {
execute: opStop, execute: opStop,
@ -962,5 +863,92 @@ func newFrontierInstructionSet() [256]operation {
valid: true, valid: true,
writes: true, writes: true,
}, },
// Homestead
// EIP7
DELEGATECALL: {
execute: opDelegateCall,
gasCost: gasDelegateCall,
validateStack: makeStackFunc(6, 1),
memorySize: memoryDelegateCall,
valid: true,
returns: true,
},
// Byzantium
// EIP140
REVERT: {
execute: opRevert,
gasCost: gasRevert,
validateStack: makeStackFunc(2, 0),
memorySize: memoryRevert,
valid: true,
reverts: true,
returns: true,
},
// EIP214
STATICCALL: {
execute: opStaticCall,
gasCost: gasStaticCall,
validateStack: makeStackFunc(6, 1),
memorySize: memoryStaticCall,
valid: true,
returns: true,
},
// EIP211
RETURNDATASIZE: {
execute: opReturnDataSize,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
// EIP211
RETURNDATACOPY: {
execute: opReturnDataCopy,
gasCost: gasReturnDataCopy,
validateStack: makeStackFunc(3, 0),
memorySize: memoryReturnDataCopy,
valid: true,
},
// Constantinople
// EIP145
SHL: {
execute: opSHL,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
// EIP145
SHR: {
execute: opSHR,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
// EIP145
SAR: {
execute: opSAR,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
// EIP1014
CREATE2: {
execute: opCreate2,
gasCost: gasCreate2,
validateStack: makeStackFunc(4, 1),
memorySize: memoryCreate2,
valid: true,
writes: true,
returns: true,
},
// EIP1052
EXTCODEHASH: {
execute: opExtCodeHash,
gasCost: gasExtCodeHash,
validateStack: makeStackFunc(1, 1),
valid: true,
},
} }
} }

View file

@ -669,7 +669,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err) return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
} }
// Finalize the state so any modifications are written to the trie // Finalize the state so any modifications are written to the trie
root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number())) root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP161F(block.Number()))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -390,7 +390,7 @@ func New(code string) (*Tracer, error) {
return 1 return 1
}) })
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int { tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))] _, ok := vm.AllPrecompiledContracts[common.BytesToAddress(popSlice(ctx))]
ctx.PushBoolean(ok) ctx.PushBoolean(ok)
return 1 return 1
}) })

View file

@ -111,17 +111,137 @@ var (
// //
// This configuration is intentionally not using keyed fields to force anyone // This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields. // adding flags to the config to also have to set these fields.
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} AllEthashProtocolChanges = &ChainConfig{
big.NewInt(1337), // ChainID
big.NewInt(0), // HomesteadBlock
nil, // EIP7FBlock
nil, // DAOForkBlock
false, // DAOForkSupport
big.NewInt(0), // EIP150Block
common.Hash{}, // EIP150Hash
big.NewInt(0), // EIP155Block
big.NewInt(0), // EIP158Block
nil, // EIP160FBlock
nil, // EIP161FBlock
nil, // EIP170FBlock
big.NewInt(0), // ByzantiumBlock
nil, // EIP100FBlock
nil, // EIP140FBlock
nil, // EIP198FBlock
nil, // EIP211FBlock
nil, // EIP212FBlock
nil, // EIP213FBlock
nil, // EIP214FBlock
nil, // EIP649FBlock
nil, // EIP658FBlock
big.NewInt(0), // ConstantinopleBlock
nil, // EIP145FBlock
nil, // EIP1014FBlock
nil, // EIP1052FBlock
nil, // EIP1234FBlock
nil, // EIP1283FBlock
nil, // EWASMBlock
new(EthashConfig), // Ethash
nil, // Clique
}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus. // and accepted by the Ethereum core developers into the Clique consensus.
// //
// This configuration is intentionally not using keyed fields to force anyone // This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields. // adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} AllCliqueProtocolChanges = &ChainConfig{
big.NewInt(1337), // ChainID
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} big.NewInt(0), // HomesteadBlock
TestRules = TestChainConfig.Rules(new(big.Int)) nil, // EIP7FBlock
nil, // DAOForkBlock
false, // DAOForkSupport
big.NewInt(0), // EIP150Block
common.Hash{}, // EIP150Hash
big.NewInt(0), // EIP155Block
big.NewInt(0), // EIP158Block
nil, // EIP160FBlock
nil, // EIP161FBlock
nil, // EIP170FBlock
big.NewInt(0), // ByzantiumBlock
nil, // EIP100FBlock
nil, // EIP140FBlock
nil, // EIP198FBlock
nil, // EIP211FBlock
nil, // EIP212FBlock
nil, // EIP213FBlock
nil, // EIP214FBlock
nil, // EIP649FBlock
nil, // EIP658FBlock
big.NewInt(0), // ConstantinopleBlock
nil, // EIP145FBlock
nil, // EIP1014FBlock
nil, // EIP1052FBlock
nil, // EIP1234FBlock
nil, // EIP1283FBlock
nil, // EWASMBlock
nil, // Ethash
&CliqueConfig{
Period: 0,
Epoch: 30000,
},
}
// TestChainConfig is used for tests.
TestChainConfig = &ChainConfig{
big.NewInt(1), // ChainID
big.NewInt(0), // HomesteadBlock
nil, // EIP7FBlock
nil, // DAOForkBlock
false, // DAOForkSupport
big.NewInt(0), // EIP150Block
common.Hash{}, // EIP150Hash
big.NewInt(0), // EIP155Block
big.NewInt(0), // EIP158Block
nil, // EIP160FBlock
nil, // EIP161FBlock
nil, // EIP170FBlock
big.NewInt(0), // ByzantiumBlock
nil, // EIP100FBlock
nil, // EIP140FBlock
nil, // EIP198FBlock
nil, // EIP211FBlock
nil, // EIP212FBlock
nil, // EIP213FBlock
nil, // EIP214FBlock
nil, // EIP649FBlock
nil, // EIP658FBlock
big.NewInt(0), // ConstantinopleBlock
nil, // EIP145FBlock
nil, // EIP1014FBlock
nil, // EIP1052FBlock
nil, // EIP1234FBlock
nil, // EIP1283FBlock
nil, // EWASMBlock
new(EthashConfig), // Ethash
nil, // Clique
}
// TestRules are all rules from TestChainConfig initialized at 0.
TestRules = TestChainConfig.Rules(new(big.Int))
) )
// TrustedCheckpoint represents a set of post-processed trie roots (CHT and // TrustedCheckpoint represents a set of post-processed trie roots (CHT and
@ -144,21 +264,93 @@ type TrustedCheckpoint struct {
type ChainConfig struct { type ChainConfig struct {
ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection
// HF: Homestead
HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead) HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead)
// Note: EIPs 2 and 8 were also included in this fork, but have not been distinguished individually in the code.
//
// DELEGATECALL
// https://eips.ethereum.org/EIPS/eip-7
EIP7FBlock *big.Int `json:"eip7FBlock,omitempy"`
// HF: DAO
DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // TheDAO hard-fork switch block (nil = no fork) DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // TheDAO hard-fork switch block (nil = no fork)
DAOForkSupport bool `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork DAOForkSupport bool `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork
// HF: Tangerine Whistle
// EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork) EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork)
EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed)
// HF: Spurious Dragon
EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block
EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block, includes implementations of 158/161, 160, and 170
//
// EXP cost increase
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-160.md
EIP160FBlock *big.Int `json:"eip160FBlock,omitempty"`
// State trie clearing (== EIP158 proper)
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
EIP161FBlock *big.Int `json:"eip161FBlock,omitempty"`
// Contract code size limit
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md
EIP170FBlock *big.Int `json:"eip170FBlock,omitempty"`
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) // HF: Byzantium
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
//
// Difficulty adjustment to target mean block time including uncles
// https://github.com/ethereum/EIPs/issues/100
EIP100FBlock *big.Int `json:"eip100FBlock,omitempty"`
// Opcode REVERT
// https://eips.ethereum.org/EIPS/eip-140
EIP140FBlock *big.Int `json:"eip140FBlock,omitempty"`
// Precompiled contract for bigint_modexp
// https://github.com/ethereum/EIPs/issues/198
EIP198FBlock *big.Int `json:"eip198FBlock,omitempty"`
// Opcodes RETURNDATACOPY, RETURNDATASIZE
// https://github.com/ethereum/EIPs/issues/211
EIP211FBlock *big.Int `json:"eip211FBlock,omitempty"`
// Precompiled contract for pairing check
// https://github.com/ethereum/EIPs/issues/212
EIP212FBlock *big.Int `json:"eip212FBlock,omitempty"`
// Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128
// https://github.com/ethereum/EIPs/issues/213
EIP213FBlock *big.Int `json:"eip213FBlock,omitempty"`
// Opcode STATICCALL
// https://github.com/ethereum/EIPs/issues/214
EIP214FBlock *big.Int `json:"eip214FBlock,omitempty"`
// Metropolis diff bomb delay and reducing block reward
// https://github.com/ethereum/EIPs/issues/649
// note that this is closely related to EIP100.
// In fact, EIP100 is bundled in
EIP649FBlock *big.Int `json:"eip649FBlock,omitempty"`
// Transaction receipt status
// https://github.com/ethereum/EIPs/issues/658
EIP658FBlock *big.Int `json:"eip658FBlock,omitempty"`
// NOT CONFIGURABLE: prevent overwriting contracts
// https://github.com/ethereum/EIPs/issues/684
// EIP684FBlock *big.Int `json:"eip684BFlock,omitempty"`
// HF: Constantinople
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) //
// Opcodes SHR, SHL, SAR
// https://eips.ethereum.org/EIPS/eip-145
EIP145FBlock *big.Int `json:"eip145FBlock,omitempty"`
// Opcode CREATE2
// https://eips.ethereum.org/EIPS/eip-1014
EIP1014FBlock *big.Int `json:"eip1014FBlock,omitempty"`
// Opcode EXTCODEHASH
// https://eips.ethereum.org/EIPS/eip-1052
EIP1052FBlock *big.Int `json:"eip1052FBlock,omitempty"`
// Constantinople difficulty bomb delay and block reward adjustment
// https://eips.ethereum.org/EIPS/eip-1234
EIP1234FBlock *big.Int `json:"eip1234FBlock,omitempty"`
// Net gas metering
// https://eips.ethereum.org/EIPS/eip-1283
EIP1283FBlock *big.Int `json:"eip1283FBlock,omitempty"`
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
// Various consensus engines // Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"` Ethash *EthashConfig `json:"ethash,omitempty"`
@ -214,6 +406,11 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool {
return isForked(c.HomesteadBlock, num) return isForked(c.HomesteadBlock, num)
} }
// IsEIP7F returns whether num is equal to or greater than the Homestead or EIP7 block.
func (c *ChainConfig) IsEIP7F(num *big.Int) bool {
return c.IsHomestead(num) || isForked(c.EIP7FBlock, num)
}
// IsDAOFork returns whether num is either equal to the DAO fork block or greater. // IsDAOFork returns whether num is either equal to the DAO fork block or greater.
func (c *ChainConfig) IsDAOFork(num *big.Int) bool { func (c *ChainConfig) IsDAOFork(num *big.Int) bool {
return isForked(c.DAOForkBlock, num) return isForked(c.DAOForkBlock, num)
@ -229,19 +426,167 @@ func (c *ChainConfig) IsEIP155(num *big.Int) bool {
return isForked(c.EIP155Block, num) return isForked(c.EIP155Block, num)
} }
// IsEIP158 returns whether num is either equal to the EIP158 fork block or greater. // EIP158HFFBlocks returns the canonical EIP blocks configured for the implemented EIP158HF fork,
func (c *ChainConfig) IsEIP158(num *big.Int) bool { // a subset of features introduced at the Spurious Dragon fork.
return isForked(c.EIP158Block, num) func (c *ChainConfig) EIP158HFFBlocks() []*big.Int {
return []*big.Int{
c.EIP160FBlock,
c.EIP161FBlock,
c.EIP170FBlock,
}
} }
// IsByzantium returns whether num is either equal to the Byzantium fork block or greater. // IsEIP158HF returns whether num is either equal to the "EIP158 Hardfork"
// (an implemented-in-code subset of the Spurious Dragon hard-fork) block or greater.
func (c *ChainConfig) IsEIP158HF(num *big.Int) bool {
return isForked(c.EIP158Block, num) || func(n *big.Int) bool {
blocks := c.EIP158HFFBlocks()
for i := range blocks {
if !isForked(blocks[i], n) {
return false
}
}
return true
}(num)
}
// IsEIP160F returns whether num is either equal to or greater than the "EIP158HF" Block or EIP160 block.
func (c *ChainConfig) IsEIP160F(num *big.Int) bool {
return c.IsEIP158HF(num) || isForked(c.EIP160FBlock, num)
}
// IsEIP161F returns whether num is either equal to or greater than the "EIP158HF" Block or EIP161 block.
func (c *ChainConfig) IsEIP161F(num *big.Int) bool {
return c.IsEIP158HF(num) || isForked(c.EIP161FBlock, num)
}
// IsEIP170F returns whether num is either equal to or greater than the "EIP158HF" Block or EIP170 block.
func (c *ChainConfig) IsEIP170F(num *big.Int) bool {
return c.IsEIP158HF(num) || isForked(c.EIP170FBlock, num)
}
//ByzantiumEIPFBlocks returns the canonical EIP blocks configured for the Byzantium Fork.
func (c *ChainConfig) ByzantiumEIPFBlocks() []*big.Int {
return []*big.Int{
c.EIP100FBlock,
c.EIP140FBlock,
c.EIP198FBlock,
c.EIP211FBlock,
c.EIP212FBlock,
c.EIP213FBlock,
c.EIP214FBlock,
c.EIP649FBlock,
c.EIP658FBlock,
}
}
// IsByzantium returns whether num is either equal to the Byzantium fork block or greater,
// or whether the configured params satisfy all requirements fulfilling the Byzantium fork.
func (c *ChainConfig) IsByzantium(num *big.Int) bool { func (c *ChainConfig) IsByzantium(num *big.Int) bool {
return isForked(c.ByzantiumBlock, num) return isForked(c.ByzantiumBlock, num) || func(n *big.Int) bool {
blocks := c.ByzantiumEIPFBlocks()
for i := range blocks {
if !isForked(blocks[i], n) {
return false
}
}
return true
}(num)
} }
// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater. // IsEIP100F returns whether num is equal to or greater than the Byzantium or EIP100 block.
func (c *ChainConfig) IsEIP100F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP100FBlock, num)
}
// IsEIP140F returns whether num is equal to or greater than the Byzantium or EIP140 block.
func (c *ChainConfig) IsEIP140F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP140FBlock, num)
}
// IsEIP198F returns whether num is equal to or greater than the Byzantium or EIP198 block.
func (c *ChainConfig) IsEIP198F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP198FBlock, num)
}
// IsEIP211F returns whether num is equal to or greater than the Byzantium or EIP211 block.
func (c *ChainConfig) IsEIP211F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP211FBlock, num)
}
// IsEIP212F returns whether num is equal to or greater than the Byzantium or EIP212 block.
func (c *ChainConfig) IsEIP212F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP212FBlock, num)
}
// IsEIP213F returns whether num is equal to or greater than the Byzantium or EIP213 block.
func (c *ChainConfig) IsEIP213F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP213FBlock, num)
}
// IsEIP214F returns whether num is equal to or greater than the Byzantium or EIP214 block.
func (c *ChainConfig) IsEIP214F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP214FBlock, num)
}
// IsEIP649F returns whether num is equal to or greater than the Byzantium or EIP649 block.
func (c *ChainConfig) IsEIP649F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP649FBlock, num)
}
// IsEIP658F returns whether num is equal to or greater than the Byzantium or EIP658 block.
func (c *ChainConfig) IsEIP658F(num *big.Int) bool {
return c.IsByzantium(num) || isForked(c.EIP658FBlock, num)
}
// ConstantinopleEIPFBlocks returns the canonical blocks configured for the Constantinople Fork.
func (c *ChainConfig) ConstantinopleEIPFBlocks() []*big.Int {
return []*big.Int{
c.EIP145FBlock,
c.EIP1014FBlock,
c.EIP1052FBlock,
c.EIP1234FBlock,
c.EIP1283FBlock,
}
}
// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater,
// or whether configured params satisfy all requirements fulfilling the Constantinople fork.
func (c *ChainConfig) IsConstantinople(num *big.Int) bool { func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
return isForked(c.ConstantinopleBlock, num) return isForked(c.ConstantinopleBlock, num) || func(n *big.Int) bool {
blocks := c.ConstantinopleEIPFBlocks()
for i := range blocks {
if !isForked(blocks[i], n) {
return false
}
}
return true
}(num)
}
// IsEIP145F returns whether num is equal to or greater than the Constantinople or EIP145 block.
func (c *ChainConfig) IsEIP145F(num *big.Int) bool {
return c.IsConstantinople(num) || isForked(c.EIP145FBlock, num)
}
// IsEIP1014F returns whether num is equal to or greater than the Constantinople or EIP1014 block.
func (c *ChainConfig) IsEIP1014F(num *big.Int) bool {
return c.IsConstantinople(num) || isForked(c.EIP1014FBlock, num)
}
// IsEIP1052F returns whether num is equal to or greater than the Constantinople or EIP1052 block.
func (c *ChainConfig) IsEIP1052F(num *big.Int) bool {
return c.IsConstantinople(num) || isForked(c.EIP1052FBlock, num)
}
// IsEIP1234F returns whether num is equal to or greater than the Constantinople or EIP1234 block.
func (c *ChainConfig) IsEIP1234F(num *big.Int) bool {
return c.IsConstantinople(num) || isForked(c.EIP1234FBlock, num)
}
// IsEIP1283F returns whether num is equal to or greater than the Constantinople or EIP1283 block.
func (c *ChainConfig) IsEIP1283F(num *big.Int) bool {
return c.IsConstantinople(num) || isForked(c.EIP1283FBlock, num)
} }
// IsEWASM returns whether num represents a block number after the EWASM fork // IsEWASM returns whether num represents a block number after the EWASM fork
@ -249,7 +594,7 @@ func (c *ChainConfig) IsEWASM(num *big.Int) bool {
return isForked(c.EWASMBlock, num) return isForked(c.EWASMBlock, num)
} }
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). // GasTable returns the gas table corresponding to the current phase.
// //
// The returned GasTable's fields shouldn't, under any circumstances, be changed. // The returned GasTable's fields shouldn't, under any circumstances, be changed.
func (c *ChainConfig) GasTable(num *big.Int) GasTable { func (c *ChainConfig) GasTable(num *big.Int) GasTable {
@ -257,10 +602,10 @@ func (c *ChainConfig) GasTable(num *big.Int) GasTable {
return GasTableHomestead return GasTableHomestead
} }
switch { switch {
case c.IsConstantinople(num): case c.IsEIP1052F(num):
return GasTableConstantinople return GasTableEIP1052
case c.IsEIP158(num): case c.IsEIP160F(num):
return GasTableEIP158 return GasTableEIP160
case c.IsEIP150(num): case c.IsEIP150(num):
return GasTableEIP150 return GasTableEIP150
default: default:
@ -287,36 +632,66 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi
} }
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError {
if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) { for _, ch := range []struct {
return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) name string
} c1, c2 *big.Int
if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) { }{
return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) {"Homestead", c.HomesteadBlock, newcfg.HomesteadBlock},
{"EIP7F", c.EIP7FBlock, newcfg.EIP7FBlock},
{"DAO", c.DAOForkBlock, newcfg.DAOForkBlock},
{"EIP150", c.EIP150Block, newcfg.EIP150Block},
{"EIP155", c.EIP155Block, newcfg.EIP155Block},
{"EIP158", c.EIP158Block, newcfg.EIP158Block},
{"EIP160F", c.EIP160FBlock, newcfg.EIP160FBlock},
{"EIP161F", c.EIP161FBlock, newcfg.EIP161FBlock},
{"EIP170F", c.EIP170FBlock, newcfg.EIP170FBlock},
{"Byzantium", c.ByzantiumBlock, newcfg.ByzantiumBlock},
{"EIP100F", c.EIP100FBlock, newcfg.EIP100FBlock},
{"EIP140F", c.EIP140FBlock, newcfg.EIP140FBlock},
{"EIP198F", c.EIP198FBlock, newcfg.EIP198FBlock},
{"EIP211F", c.EIP211FBlock, newcfg.EIP211FBlock},
{"EIP212F", c.EIP212FBlock, newcfg.EIP212FBlock},
{"EIP213F", c.EIP213FBlock, newcfg.EIP213FBlock},
{"EIP214F", c.EIP214FBlock, newcfg.EIP214FBlock},
{"EIP649F", c.EIP649FBlock, newcfg.EIP649FBlock},
{"EIP658F", c.EIP658FBlock, newcfg.EIP658FBlock},
{"Constantinople", c.ConstantinopleBlock, newcfg.ConstantinopleBlock},
{"EIP145F", c.EIP145FBlock, newcfg.EIP145FBlock},
{"EIP1014F", c.EIP1014FBlock, newcfg.EIP1014FBlock},
{"EIP1052F", c.EIP1052FBlock, newcfg.EIP1052FBlock},
{"EIP1234F", c.EIP1234FBlock, newcfg.EIP1234FBlock},
{"EIP1283F", c.EIP1283FBlock, newcfg.EIP1283FBlock},
{"EWASM", c.EWASMBlock, newcfg.EWASMBlock},
} {
if err := func(c1, c2, head *big.Int) *ConfigCompatError {
if isForkIncompatible(ch.c1, ch.c2, head) {
return newCompatError(ch.name+" fork block", ch.c1, ch.c2)
}
return nil
}(ch.c1, ch.c2, head); err != nil {
return err
}
} }
if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport { if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport {
return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock)
} }
if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) { if c.IsEIP155(head) && !configNumEqual(c.ChainID, newcfg.ChainID) {
return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) return newCompatError("EIP155 chain ID", c.EIP155Block, newcfg.EIP155Block)
} }
if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) { // Either Byzantium block must be set OR EIP100 and EIP649 must be equivalent
return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) if newcfg.ByzantiumBlock == nil {
} if !configNumEqual(newcfg.EIP100FBlock, newcfg.EIP649FBlock) {
if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) { return newCompatError("EIP100F/EIP649F not equal", newcfg.EIP100FBlock, newcfg.EIP649FBlock)
return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) }
} if isForkIncompatible(c.EIP100FBlock, newcfg.EIP649FBlock, head) {
if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) { return newCompatError("EIP100F/EIP649F fork block", c.EIP100FBlock, newcfg.EIP649FBlock)
return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) }
} if isForkIncompatible(c.EIP649FBlock, newcfg.EIP100FBlock, head) {
if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) { return newCompatError("EIP649F/EIP100F fork block", c.EIP649FBlock, newcfg.EIP100FBlock)
return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock) }
}
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
}
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
} }
return nil return nil
} }
@ -381,9 +756,13 @@ func (err *ConfigCompatError) Error() string {
// Rules is a one time interface meaning that it shouldn't be used in between transition // Rules is a one time interface meaning that it shouldn't be used in between transition
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP7F bool
IsByzantium, IsConstantinople bool IsEIP150 bool
IsEIP155 bool
IsEIP158HF, IsEIP160F, IsEIP161F, IsEIP170F bool
IsByzantium, IsEIP100F, IsEIP140F, IsEIP198F, IsEIP211F, IsEIP212F, IsEIP213F, IsEIP214F, IsEIP649F, IsEIP658F bool
IsConstantinople, IsEIP145F, IsEIP1014F, IsEIP1052F, IsEIP1283F, IsEIP1234F bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -393,12 +772,34 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
chainID = new(big.Int) chainID = new(big.Int)
} }
return Rules{ return Rules{
ChainID: new(big.Int).Set(chainID), ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num),
IsEIP150: c.IsEIP150(num), IsHomestead: c.IsHomestead(num),
IsEIP155: c.IsEIP155(num), IsEIP7F: c.IsEIP7F(num),
IsEIP158: c.IsEIP158(num),
IsByzantium: c.IsByzantium(num), IsEIP150: c.IsEIP150(num),
IsEIP155: c.IsEIP155(num),
IsEIP158HF: c.IsEIP158HF(num),
IsEIP160F: c.IsEIP160F(num),
IsEIP161F: c.IsEIP161F(num),
IsEIP170F: c.IsEIP170F(num),
IsByzantium: c.IsByzantium(num),
IsEIP100F: c.IsEIP100F(num),
IsEIP140F: c.IsEIP140F(num),
IsEIP198F: c.IsEIP198F(num),
IsEIP211F: c.IsEIP211F(num),
IsEIP212F: c.IsEIP212F(num),
IsEIP213F: c.IsEIP213F(num),
IsEIP214F: c.IsEIP214F(num),
IsEIP649F: c.IsEIP649F(num),
IsEIP658F: c.IsEIP658F(num),
IsConstantinople: c.IsConstantinople(num), IsConstantinople: c.IsConstantinople(num),
IsEIP145F: c.IsEIP145F(num),
IsEIP1014F: c.IsEIP1014F(num),
IsEIP1052F: c.IsEIP1052F(num),
IsEIP1234F: c.IsEIP1234F(num),
IsEIP1283F: c.IsEIP1283F(num),
} }
} }

View file

@ -20,8 +20,107 @@ import (
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
) )
// Test HF::EIPs boolean logic
func TestIsByzantiumAndAssociatedEIPFFns(t *testing.T) {
blocksWantsAroundFork := func(forkBlock *big.Int) (blocks []*big.Int, wants []bool) {
blocks, wants = append(blocks, forkBlock), append(wants, forkBlock != nil)
if forkBlock == nil {
blocks, wants = append(blocks, big.NewInt(0)), append(wants, false)
blocks, wants = append(blocks, big.NewInt(42)), append(wants, false)
return
}
blocks, wants = append(blocks, new(big.Int).Sub(forkBlock, common.Big1)), append(wants, false)
blocks, wants = append(blocks, new(big.Int).Add(forkBlock, common.Big1)), append(wants, true)
return
}
c := &ChainConfig{}
*c = *MainnetChainConfig
blocks, wants := blocksWantsAroundFork(c.ByzantiumBlock)
for i, b := range blocks {
if c.IsByzantium(b) != wants[i] {
t.Errorf("i: %d, b: %v, got: %v, want: %v", i, b, c.IsByzantium(b), wants[i])
}
// Show that Byzantium's EIP<N>F block methods imply Byzantium block presence
for j, fn := range []func(*big.Int) bool{
c.IsEIP100F,
c.IsEIP140F,
c.IsEIP198F,
c.IsEIP211F,
c.IsEIP212F,
c.IsEIP213F,
c.IsEIP214F,
c.IsEIP649F,
c.IsEIP658F,
} {
if fn(b) != c.IsByzantium(b) {
t.Errorf("j: %d, b: %v, got: %v, want: %v", j, b, fn(b), c.IsByzantium(b))
}
}
}
// Show that presence of all Byzantium's EIP<N>F blocks alone satisfy IsByzantium fn
c.EIP100FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP140FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP198FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP211FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP212FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP213FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP214FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP649FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.EIP658FBlock = new(big.Int).Set(c.ByzantiumBlock)
c.ByzantiumBlock = nil
for i, b := range blocks {
if c.IsByzantium(b) != wants[i] {
t.Errorf("i: %d, b: %v, got: %v, want: %v", i, b, c.IsByzantium(b), wants[i])
}
for j, fn := range []func(*big.Int) bool{
c.IsEIP100F,
c.IsEIP140F,
c.IsEIP198F,
c.IsEIP211F,
c.IsEIP212F,
c.IsEIP213F,
c.IsEIP214F,
c.IsEIP649F,
c.IsEIP658F,
} {
if fn(b) != c.IsByzantium(b) {
t.Errorf("j: %d, b: %v, got: %v, want: %v", j, b, fn(b), c.IsByzantium(b))
}
}
}
// Show that ALL EIP<N>F blocks must be set in order to be sufficiently "Byzantium"
c.EIP658FBlock = nil
for i, b := range blocks {
if c.IsByzantium(b) {
t.Errorf("i: %d, b: %v, got: %v, want: %v", i, b, c.IsByzantium(b), wants[i])
}
for j, fn := range []func(*big.Int) bool{
c.IsEIP100F,
c.IsEIP140F,
c.IsEIP198F,
c.IsEIP211F,
c.IsEIP212F,
c.IsEIP213F,
c.IsEIP214F,
c.IsEIP649F,
} {
if fn(b) != wants[i] {
t.Errorf("j: %d, b: %v, got: %v, want: %v", j, b, fn(b), wants[i])
}
}
if c.IsEIP658F(b) {
t.Errorf("got: %v, want: %v", c.IsEIP658F(b), false)
}
}
}
func TestCheckCompatible(t *testing.T) { func TestCheckCompatible(t *testing.T) {
type test struct { type test struct {
stored, new *ChainConfig stored, new *ChainConfig
@ -70,6 +169,83 @@ func TestCheckCompatible(t *testing.T) {
RewindTo: 9, RewindTo: 9,
}, },
}, },
{
stored: &ChainConfig{EIP100FBlock: big.NewInt(30), EIP649FBlock: big.NewInt(31)},
new: &ChainConfig{EIP100FBlock: big.NewInt(30), EIP649FBlock: big.NewInt(31)},
head: 25,
wantErr: &ConfigCompatError{
What: "EIP100F/EIP649F not equal",
StoredConfig: big.NewInt(30),
NewConfig: big.NewInt(31),
RewindTo: 29,
},
},
{
stored: &ChainConfig{EIP100FBlock: big.NewInt(30), EIP649FBlock: big.NewInt(30)},
new: &ChainConfig{EIP100FBlock: big.NewInt(24), EIP649FBlock: big.NewInt(24)},
head: 25,
wantErr: &ConfigCompatError{
What: "EIP100F fork block",
StoredConfig: big.NewInt(30),
NewConfig: big.NewInt(24),
RewindTo: 23,
},
},
{
stored: &ChainConfig{ByzantiumBlock: big.NewInt(30)},
new: &ChainConfig{EIP211FBlock: big.NewInt(26)},
head: 25,
wantErr: nil,
},
{
stored: &ChainConfig{ByzantiumBlock: big.NewInt(30)},
new: &ChainConfig{EIP100FBlock: big.NewInt(26)}, // err: EIP649 must also be set
head: 25,
wantErr: &ConfigCompatError{
What: "EIP100F/EIP649F not equal",
StoredConfig: big.NewInt(26), // this yields a weird-looking error (correctly, though), b/c ConfigCompatError not set up for these kinds of strange cases
NewConfig: nil,
RewindTo: 25,
},
},
{
stored: &ChainConfig{ByzantiumBlock: big.NewInt(30)},
new: &ChainConfig{EIP100FBlock: big.NewInt(26), EIP649FBlock: big.NewInt(26)},
head: 25,
wantErr: nil,
},
{
stored: MainnetChainConfig,
new: func() *ChainConfig {
c := &ChainConfig{}
*c = *MainnetChainConfig
c.DAOForkSupport = !MainnetChainConfig.DAOForkSupport
return c
}(),
head: MainnetChainConfig.DAOForkBlock.Uint64(),
wantErr: &ConfigCompatError{
What: "DAO fork support flag",
StoredConfig: MainnetChainConfig.DAOForkBlock,
NewConfig: MainnetChainConfig.DAOForkBlock,
RewindTo: new(big.Int).Sub(MainnetChainConfig.DAOForkBlock, common.Big1).Uint64(),
},
},
{
stored: MainnetChainConfig,
new: func() *ChainConfig {
c := &ChainConfig{}
*c = *MainnetChainConfig
c.ChainID = new(big.Int).Sub(MainnetChainConfig.EIP155Block, common.Big1)
return c
}(),
head: MainnetChainConfig.EIP158Block.Uint64(),
wantErr: &ConfigCompatError{
What: "EIP155 chain ID",
StoredConfig: MainnetChainConfig.EIP155Block,
NewConfig: MainnetChainConfig.EIP155Block,
RewindTo: new(big.Int).Sub(MainnetChainConfig.EIP158Block, common.Big1).Uint64(),
},
},
} }
for _, test := range tests { for _, test := range tests {

View file

@ -63,9 +63,9 @@ var (
CreateBySuicide: 25000, CreateBySuicide: 25000,
} }
// GasTableEIP158 contain the gas re-prices for // GasTableEIP160 contain the gas re-prices for
// the EIP155/EIP158 phase. // the EIP155/EIP158 phase.
GasTableEIP158 = GasTable{ GasTableEIP160 = GasTable{
ExtcodeSize: 700, ExtcodeSize: 700,
ExtcodeCopy: 700, ExtcodeCopy: 700,
Balance: 400, Balance: 400,
@ -76,9 +76,9 @@ var (
CreateBySuicide: 25000, CreateBySuicide: 25000,
} }
// GasTableConstantinople contain the gas re-prices for // GasTableEIP1052 contain the gas re-prices for
// the constantinople phase. // the constantinople phase.
GasTableConstantinople = GasTable{ GasTableEIP1052 = GasTable{
ExtcodeSize: 700, ExtcodeSize: 700,
ExtcodeCopy: 700, ExtcodeCopy: 700,
ExtcodeHash: 400, ExtcodeHash: 400,

View file

@ -144,7 +144,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
} }
// Commit block // Commit block
statedb.Commit(config.IsEIP158(block.Number())) statedb.Commit(config.IsEIP161F(block.Number()))
// Add 0-value mining reward. This only makes a difference in the cases // Add 0-value mining reward. This only makes a difference in the cases
// where // where
// - the coinbase suicided, or // - the coinbase suicided, or
@ -152,7 +152,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
// the coinbase gets no txfee, so isn't created, and thus needs to be touched // the coinbase gets no txfee, so isn't created, and thus needs to be touched
statedb.AddBalance(block.Coinbase(), new(big.Int)) statedb.AddBalance(block.Coinbase(), new(big.Int))
// And _now_ get the state root // And _now_ get the state root
root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) root := statedb.IntermediateRoot(config.IsEIP161F(block.Number()))
// N.B: We need to do this in a two-step process, because the first Commit takes care // N.B: We need to do this in a two-step process, because the first Commit takes care
// of suicides, and we need to touch the coinbase _after_ it has potentially suicided. // of suicides, and we need to touch the coinbase _after_ it has potentially suicided.
if root != common.Hash(post.Root) { if root != common.Hash(post.Root) {