Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip/eip methods #8

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/puppeth/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,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.EIP649BlockReward)
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000)
n := hexutil.Uint64(num.Uint64())
spec.Engine.Ethash.Params.EIP100bTransition = n
Expand Down
38 changes: 23 additions & 15 deletions consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
EIP649BlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
EIP1234BlockReward = 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))
)

Expand Down Expand Up @@ -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.IsEIP1234(next):
return calcDifficultyEIP1234(time, parent)
case config.IsByzantium(next) || (config.IsEIP649(next) && config.IsEIP100(next)):
return calcDifficultyByzantium(time, parent)
case config.IsEIP649(next):
// TODO: calculator for only EIP649:difficulty bomb delay (without EIP100:mean time adjustment)
panic("not implemented")
case config.IsEIP100(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:
Expand Down Expand Up @@ -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.IsEIP649(header.Number) {
blockReward = EIP649BlockReward
}
if config.IsConstantinople(header.Number) {
blockReward = ConstantinopleBlockReward
if config.IsEIP1234(header.Number) {
blockReward = EIP1234BlockReward
}
// Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward)
Expand Down
2 changes: 1 addition & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ 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.IsEIP658(header.Number) {
statedb.Finalise(true)
} else {
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
Expand Down
35 changes: 23 additions & 12 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
Expand All @@ -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.IsEIP198(num)
case common.BytesToAddress([]byte{6}):
return config.IsEIP213(num)
case common.BytesToAddress([]byte{7}):
return config.IsEIP213(num)
case common.BytesToAddress([]byte{8}):
return config.IsEIP212(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)
Expand Down
96 changes: 94 additions & 2 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/params"

"github.com/ethereum/go-ethereum/common"
)

Expand Down Expand Up @@ -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 {
Expand All @@ -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")),
Expand Down
14 changes: 3 additions & 11 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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().IsEIP158(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)
Expand Down
2 changes: 1 addition & 1 deletion core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.IsEIP1283 {
// This checks for 3 scenario's and calculates gas accordingly:
//
// 1. From a zero-value address to a non-zero value (NEW VALUE)
Expand Down
50 changes: 36 additions & 14 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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.IsEIP214 {
if in.readOnly {
// If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
Expand All @@ -131,6 +123,36 @@ func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, st
}
}
}
switch op {
case DELEGATECALL:
if !in.evm.chainRules.IsEIP7 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case REVERT:
if !in.evm.chainRules.IsEIP140 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case STATICCALL:
if !in.evm.chainRules.IsEIP214 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case RETURNDATACOPY, RETURNDATASIZE:
if !in.evm.chainRules.IsEIP211 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case SHL, SHR, SAR:
if !in.evm.chainRules.IsEIP145 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case CREATE2:
if !in.evm.chainRules.IsEIP1014 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
case EXTCODEHASH:
if !in.evm.chainRules.IsEIP1052 {
return fmt.Errorf("invalid opcode 0x%x", int(op))
}
}
return nil
}

Expand Down Expand Up @@ -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
Expand Down
Loading