diff --git a/cmd/puppeth/genesis.go b/cmd/puppeth/genesis.go index c95c81a6dd..9db56e7e08 100644 --- a/cmd/puppeth/genesis.go +++ b/cmd/puppeth/genesis.go @@ -421,7 +421,7 @@ func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBu } 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) n := hexutil.Uint64(num.Uint64()) spec.Engine.Ethash.Params.EIP100bTransition = n @@ -432,7 +432,7 @@ func (spec *parityChainSpec) setByzantium(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) n := hexutil.Uint64(num.Uint64()) spec.Params.EIP145Transition = n diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index c79c30caed..6699e33906 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -581,7 +581,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro // 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) { // 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) // Assemble and return the final block for sealing diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 62e3f8fca9..7705dfbbd8 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -38,23 +38,25 @@ import ( // Ethash proof-of-work protocol constants. var ( - 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 - ConstantinopleBlockReward = 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 - allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks + FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block + EIP649FBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium + 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 + 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 // parent block's time and difficulty. The calculation uses the Byzantium rules, but with // bomb offset 5M. // 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 // parent block's time and difficulty. The calculation uses the Byzantium rules. // 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)) ) @@ -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 { next := new(big.Int).Add(parent.Number, big1) switch { - case config.IsConstantinople(next): - return calcDifficultyConstantinople(time, parent) - case config.IsByzantium(next): + case config.IsEIP1234F(next): + return calcDifficultyEIP1234(time, parent) + case config.IsByzantium(next) || (config.IsEIP649F(next) && config.IsEIP100F(next)): 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): return calcDifficultyHomestead(time, parent) 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) { // Accumulate any block and uncle rewards and commit the final state root 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 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) { // Select the correct block reward based on chain progression blockReward := FrontierBlockReward - if config.IsByzantium(header.Number) { - blockReward = ByzantiumBlockReward + if config.IsEIP649F(header.Number) { + blockReward = EIP649FBlockReward } - if config.IsConstantinople(header.Number) { - blockReward = ConstantinopleBlockReward + if config.IsEIP1234F(header.Number) { + blockReward = EIP1234FBlockReward } // Accumulate the rewards for the miner and any included uncles reward := new(big.Int).Set(blockReward) diff --git a/core/block_validator.go b/core/block_validator.go index 3b9496fece..ecf9c039fa 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -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 // 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 nil diff --git a/core/blockchain.go b/core/blockchain.go index c29063a73b..efaaded730 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -43,7 +43,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru" ) var ( @@ -946,7 +946,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. } 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 { return NonStatTy, err } diff --git a/core/chain_makers.go b/core/chain_makers.go index e3a5537a40..c61ed29388 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -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) // 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 { 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{ - Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), + Root: state.IntermediateRoot(chain.Config().IsEIP161F(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{ diff --git a/core/state_processor.go b/core/state_processor.go index 503a35d16a..69e514bedf 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -102,10 +102,10 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo } // Update the state with pending changes var root []byte - if config.IsByzantium(header.Number) { + if config.IsEIP658F(header.Number) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + root = statedb.IntermediateRoot(config.IsEIP161F(header.Number)).Bytes() } *usedGas += gas diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 20b741f8f1..71334413dc 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -37,18 +37,8 @@ type PrecompiledContract interface { Run(input []byte) ([]byte, error) // Run runs the precompiled contract } -// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum -// contracts used in the Frontier and Homestead releases. -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{ +// AllPrecompiledContracts returns all possible precompiled contracts. +var AllPrecompiledContracts = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{1}): &ecrecover{}, common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, @@ -59,6 +49,27 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ 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. func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { gas := p.RequiredGas(input) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 96083337c9..40d654d40a 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -21,6 +21,8 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/common" ) @@ -337,9 +339,10 @@ var bn256PairingTests = []precompiledTest{ } func testPrecompiled(addr string, test precompiledTest, t *testing.T) { - p := PrecompiledContractsByzantium[common.HexToAddress(addr)] + p := AllPrecompiledContracts[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) contract := NewContract(AccountRef(common.HexToAddress("1337")), + nil, new(big.Int), p.RequiredGas(in)) t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { 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) { if test.noBenchmark { return } - p := PrecompiledContractsByzantium[common.HexToAddress(addr)] + p := AllPrecompiledContracts[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) reqGas := p.RequiredGas(in) contract := NewContract(AccountRef(common.HexToAddress("1337")), diff --git a/core/vm/evm.go b/core/vm/evm.go index ba4d1e9eb8..01a196cd4d 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -43,12 +43,8 @@ type ( // 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) { if contract.CodeAddr != nil { - precompiles := PrecompiledContractsHomestead - if evm.ChainConfig().IsByzantium(evm.BlockNumber) { - precompiles = PrecompiledContractsByzantium - } - if p := precompiles[*contract.CodeAddr]; p != nil { - return RunPrecompiledContract(p, input, contract) + if IsPrecompiledContractEnabled(evm.ChainConfig(), evm.BlockNumber, *contract.CodeAddr) { + return RunPrecompiledContract(AllPrecompiledContracts[*contract.CodeAddr], input, contract) } } 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() ) if !evm.StateDB.Exist(addr) { - precompiles := PrecompiledContractsHomestead - if evm.ChainConfig().IsByzantium(evm.BlockNumber) { - precompiles = PrecompiledContractsByzantium - } - if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { + if !IsPrecompiledContractEnabled(evm.ChainConfig(), evm.BlockNumber, addr) && evm.ChainConfig().IsEIP161F(evm.BlockNumber) && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug && evm.depth == 0 { 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 snapshot := evm.StateDB.Snapshot() evm.StateDB.CreateAccount(address) - if evm.ChainConfig().IsEIP158(evm.BlockNumber) { + if evm.ChainConfig().IsEIP161F(evm.BlockNumber) { evm.StateDB.SetNonce(address, 1) } 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) // 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 // 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 diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index df79f86eca..0899d6fc8c 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -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)) ) // 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: // // 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 transfersValue = stack.Back(2).Sign() != 0 address = common.BigToAddress(stack.Back(1)) - eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + eip158 = evm.ChainConfig().IsEIP161F(evm.BlockNumber) ) if eip158 { if transfersValue && evm.StateDB.Empty(address) { @@ -461,7 +461,7 @@ func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, gas = gt.Suicide var ( address = common.BigToAddress(stack.Back(0)) - eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + eip158 = evm.ChainConfig().IsEIP161F(evm.BlockNumber) ) if eip158 { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 952d96dd4d..6cb411580c 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -99,16 +99,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // the jump table was initialised. If it was not // we'll set the default jump table. if !cfg.JumpTable[STOP].valid { - switch { - 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 - } + cfg.JumpTable = baseInstructionSet } return &EVMInterpreter{ @@ -119,7 +110,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { } 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 the interpreter is operating in readonly mode, make sure no // 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 } @@ -216,13 +238,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if !operation.valid { 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 err := in.enforceRestrictions(op, operation, stack); err != nil { return nil, err } + if err := operation.validateStack(stack); err != nil { + return nil, err + } var memorySize uint64 // calculate the new memory size and expand the memory to fit diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index deedf70cdb..f30a31bb30 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -48,112 +48,13 @@ type operation struct { valid bool // indication whether the retrieved operation is valid and known reverts bool // determines whether the operation reverts state (implicitly halts) returns bool // determines whether the operations sets the return data content + } -var ( - frontierInstructionSet = newFrontierInstructionSet() - homesteadInstructionSet = newHomesteadInstructionSet() - byzantiumInstructionSet = newByzantiumInstructionSet() - constantinopleInstructionSet = newConstantinopleInstructionSet() -) +var baseInstructionSet = newInstructionSet() -// NewConstantinopleInstructionSet returns the frontier, homestead -// byzantium and contantinople instructions. -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 { +// newInstructionSet returns all available instructions. +func newInstructionSet() [256]operation { return [256]operation{ STOP: { execute: opStop, @@ -962,5 +863,92 @@ func newFrontierInstructionSet() [256]operation { valid: 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, + }, } } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 0b8f8aa00b..7ac11c7d2f 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -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) } // 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 { return nil, err } diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 9d6701868c..1c1d9ee45d 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -390,7 +390,7 @@ func New(code string) (*Tracer, error) { return 1 }) 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) return 1 }) diff --git a/params/config.go b/params/config.go index 2935ef1f3f..b39f4ed405 100644 --- a/params/config.go +++ b/params/config.go @@ -111,17 +111,137 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // 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 // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // 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} - TestRules = TestChainConfig.Rules(new(big.Int)) + 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 + 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 @@ -144,21 +264,93 @@ type TrustedCheckpoint struct { type ChainConfig struct { 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) + // 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) 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) 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) + // HF: Spurious Dragon 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) - 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 Ethash *EthashConfig `json:"ethash,omitempty"` @@ -214,6 +406,11 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool { 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. func (c *ChainConfig) IsDAOFork(num *big.Int) bool { return isForked(c.DAOForkBlock, num) @@ -229,19 +426,167 @@ func (c *ChainConfig) IsEIP155(num *big.Int) bool { return isForked(c.EIP155Block, num) } -// IsEIP158 returns whether num is either equal to the EIP158 fork block or greater. -func (c *ChainConfig) IsEIP158(num *big.Int) bool { - return isForked(c.EIP158Block, num) +// EIP158HFFBlocks returns the canonical EIP blocks configured for the implemented EIP158HF fork, +// a subset of features introduced at the Spurious Dragon fork. +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 { - 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 { - 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 @@ -249,7 +594,7 @@ func (c *ChainConfig) IsEWASM(num *big.Int) bool { 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. func (c *ChainConfig) GasTable(num *big.Int) GasTable { @@ -257,10 +602,10 @@ func (c *ChainConfig) GasTable(num *big.Int) GasTable { return GasTableHomestead } switch { - case c.IsConstantinople(num): - return GasTableConstantinople - case c.IsEIP158(num): - return GasTableEIP158 + case c.IsEIP1052F(num): + return GasTableEIP1052 + case c.IsEIP160F(num): + return GasTableEIP160 case c.IsEIP150(num): return GasTableEIP150 default: @@ -287,36 +632,66 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi } func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { - if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) { - return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) - } - if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) { - return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) + for _, ch := range []struct { + name string + c1, c2 *big.Int + }{ + {"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 { return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) } - if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) { - return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) + if c.IsEIP155(head) && !configNumEqual(c.ChainID, newcfg.ChainID) { + return newCompatError("EIP155 chain ID", c.EIP155Block, newcfg.EIP155Block) } - if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) { - return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) - } - if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) { - return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) - } - if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) { - return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) - } - if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) { - 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) + // Either Byzantium block must be set OR EIP100 and EIP649 must be equivalent + if newcfg.ByzantiumBlock == nil { + if !configNumEqual(newcfg.EIP100FBlock, newcfg.EIP649FBlock) { + return newCompatError("EIP100F/EIP649F not equal", newcfg.EIP100FBlock, newcfg.EIP649FBlock) + } + if isForkIncompatible(c.EIP100FBlock, newcfg.EIP649FBlock, head) { + return newCompatError("EIP100F/EIP649F fork block", c.EIP100FBlock, newcfg.EIP649FBlock) + } + if isForkIncompatible(c.EIP649FBlock, newcfg.EIP100FBlock, head) { + return newCompatError("EIP649F/EIP100F fork block", c.EIP649FBlock, newcfg.EIP100FBlock) + } } + 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 // phases. type Rules struct { - ChainID *big.Int - IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool - IsByzantium, IsConstantinople bool + ChainID *big.Int + IsHomestead, IsEIP7F 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. @@ -393,12 +772,34 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { chainID = new(big.Int) } return Rules{ - ChainID: new(big.Int).Set(chainID), - IsHomestead: c.IsHomestead(num), - IsEIP150: c.IsEIP150(num), - IsEIP155: c.IsEIP155(num), - IsEIP158: c.IsEIP158(num), - IsByzantium: c.IsByzantium(num), + ChainID: new(big.Int).Set(chainID), + + IsHomestead: c.IsHomestead(num), + IsEIP7F: c.IsEIP7F(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), + IsEIP145F: c.IsEIP145F(num), + IsEIP1014F: c.IsEIP1014F(num), + IsEIP1052F: c.IsEIP1052F(num), + IsEIP1234F: c.IsEIP1234F(num), + IsEIP1283F: c.IsEIP1283F(num), } } diff --git a/params/config_test.go b/params/config_test.go index 02c5fe2917..ffa5e63c38 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -20,8 +20,107 @@ import ( "math/big" "reflect" "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 EIPF 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 EIPF 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 EIPF 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) { type test struct { stored, new *ChainConfig @@ -70,6 +169,83 @@ func TestCheckCompatible(t *testing.T) { 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 { diff --git a/params/gas_table.go b/params/gas_table.go index 6c4a38269c..f472144768 100644 --- a/params/gas_table.go +++ b/params/gas_table.go @@ -63,9 +63,9 @@ var ( CreateBySuicide: 25000, } - // GasTableEIP158 contain the gas re-prices for + // GasTableEIP160 contain the gas re-prices for // the EIP155/EIP158 phase. - GasTableEIP158 = GasTable{ + GasTableEIP160 = GasTable{ ExtcodeSize: 700, ExtcodeCopy: 700, Balance: 400, @@ -76,9 +76,9 @@ var ( CreateBySuicide: 25000, } - // GasTableConstantinople contain the gas re-prices for + // GasTableEIP1052 contain the gas re-prices for // the constantinople phase. - GasTableConstantinople = GasTable{ + GasTableEIP1052 = GasTable{ ExtcodeSize: 700, ExtcodeCopy: 700, ExtcodeHash: 400, diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 436284196d..2ca5783118 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -144,7 +144,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD statedb.RevertToSnapshot(snapshot) } // 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 // where // - 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 statedb.AddBalance(block.Coinbase(), new(big.Int)) // 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 // of suicides, and we need to touch the coinbase _after_ it has potentially suicided. if root != common.Hash(post.Root) {