core, cmd/puppeth: implement constantinople fix, disable EIP-1283 (#18486)

This PR adds a new fork which disables EIP-1283. Internally it's called Petersburg,
but the genesis/config field is ConstantinopleFix.

The block numbers are:

    7280000 for Constantinople on Mainnet
    7280000 for ConstantinopleFix on Mainnet
    4939394 for ConstantinopleFix on Ropsten
    9999999 for ConstantinopleFix on Rinkeby (real number decided later)

This PR also defaults to using the same ConstantinopleFix number as whatever
Constantinople is set to. That is, it will default to mainnet behaviour if ConstantinopleFix
is not set.This means that for private networks which have already transitioned
to Constantinople, this PR will break the network unless ConstantinopleFix is
explicitly set!
This commit is contained in:
Martin Holst Swende 2019-01-24 11:36:30 +01:00 committed by Felix Lange
parent dc43ea8d03
commit ecb781297b
11 changed files with 134 additions and 66 deletions

View File

@ -245,6 +245,7 @@ type parityChainSpec struct {
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"` EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"` EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"` EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
EIP1283DisableTransition hexutil.Uint64 `json:"eip1283DisableTransition"`
} `json:"params"` } `json:"params"`
Genesis struct { Genesis struct {
@ -347,6 +348,11 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
if num := genesis.Config.ConstantinopleBlock; num != nil { if num := genesis.Config.ConstantinopleBlock; num != nil {
spec.setConstantinople(num) spec.setConstantinople(num)
} }
// ConstantinopleFix (remove eip-1283)
if num := genesis.Config.PetersburgBlock; num != nil {
spec.setConstantinopleFix(num)
}
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor) spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
@ -441,6 +447,10 @@ func (spec *parityChainSpec) setConstantinople(num *big.Int) {
spec.Params.EIP1283Transition = n spec.Params.EIP1283Transition = n
} }
func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) {
spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64())
}
// pyEthereumGenesisSpec represents the genesis specification format used by the // pyEthereumGenesisSpec represents the genesis specification format used by the
// Python Ethereum implementation. // Python Ethereum implementation.
type pyEthereumGenesisSpec struct { type pyEthereumGenesisSpec struct {

View File

@ -632,6 +632,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
"Spurious": conf.Genesis.Config.EIP155Block, "Spurious": conf.Genesis.Config.EIP155Block,
"Byzantium": conf.Genesis.Config.ByzantiumBlock, "Byzantium": conf.Genesis.Config.ByzantiumBlock,
"Constantinople": conf.Genesis.Config.ConstantinopleBlock, "Constantinople": conf.Genesis.Config.ConstantinopleBlock,
"ConstantinopleFix": conf.Genesis.Config.PetersburgBlock,
}) })
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes() files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()

View File

@ -223,6 +223,10 @@ func (w *wizard) manageGenesis() {
fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock) fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock) w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
fmt.Println()
fmt.Printf("Which block should Constantinople-Fix (remove EIP-1283) come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ") out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
fmt.Printf("Chain configuration updated:\n\n%s\n", out) fmt.Printf("Chain configuration updated:\n\n%s\n", out)

View File

@ -183,6 +183,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constant
newcfg := genesis.configOrDefault(stored) newcfg := genesis.configOrDefault(stored)
if constantinopleOverride != nil { if constantinopleOverride != nil {
newcfg.ConstantinopleBlock = constantinopleOverride newcfg.ConstantinopleBlock = constantinopleOverride
newcfg.PetersburgBlock = constantinopleOverride
} }
storedcfg := rawdb.ReadChainConfig(db, stored) storedcfg := rawdb.ReadChainConfig(db, stored)
if storedcfg == nil { if storedcfg == nil {

View File

@ -121,7 +121,9 @@ 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 { // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
// OR Constantinople is not active
if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople {
// 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)

View File

@ -34,7 +34,11 @@ type JSONLogger struct {
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream. // into the provided stream.
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
return &JSONLogger{json.NewEncoder(writer), cfg} l := &JSONLogger{json.NewEncoder(writer), cfg}
if l.cfg == nil {
l.cfg = &LogConfig{}
}
return l
} }
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {

View File

@ -42,7 +42,8 @@ var (
EIP155Block: big.NewInt(2675000), EIP155Block: big.NewInt(2675000),
EIP158Block: big.NewInt(2675000), EIP158Block: big.NewInt(2675000),
ByzantiumBlock: big.NewInt(4370000), ByzantiumBlock: big.NewInt(4370000),
ConstantinopleBlock: nil, ConstantinopleBlock: big.NewInt(7280000),
PetersburgBlock: big.NewInt(7280000),
Ethash: new(EthashConfig), Ethash: new(EthashConfig),
} }
@ -67,6 +68,7 @@ var (
EIP158Block: big.NewInt(10), EIP158Block: big.NewInt(10),
ByzantiumBlock: big.NewInt(1700000), ByzantiumBlock: big.NewInt(1700000),
ConstantinopleBlock: big.NewInt(4230000), ConstantinopleBlock: big.NewInt(4230000),
PetersburgBlock: big.NewInt(4939394),
Ethash: new(EthashConfig), Ethash: new(EthashConfig),
} }
@ -91,6 +93,7 @@ var (
EIP158Block: big.NewInt(3), EIP158Block: big.NewInt(3),
ByzantiumBlock: big.NewInt(1035301), ByzantiumBlock: big.NewInt(1035301),
ConstantinopleBlock: big.NewInt(3660663), ConstantinopleBlock: big.NewInt(3660663),
PetersburgBlock: big.NewInt(9999999), //TODO! Insert Rinkeby block number
Clique: &CliqueConfig{ Clique: &CliqueConfig{
Period: 15, Period: 15,
Epoch: 30000, Epoch: 30000,
@ -111,16 +114,16 @@ 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), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
// 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), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
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} 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), big.NewInt(0), nil, new(EthashConfig), nil}
TestRules = TestChainConfig.Rules(new(big.Int)) TestRules = TestChainConfig.Rules(new(big.Int))
) )
@ -158,6 +161,7 @@ type ChainConfig struct {
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
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)
PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople)
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
// Various consensus engines // Various consensus engines
@ -195,7 +199,7 @@ func (c *ChainConfig) String() string {
default: default:
engine = "unknown" engine = "unknown"
} }
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Engine: %v}", return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v ConstantinopleFix: %v Engine: %v}",
c.ChainID, c.ChainID,
c.HomesteadBlock, c.HomesteadBlock,
c.DAOForkBlock, c.DAOForkBlock,
@ -205,6 +209,7 @@ func (c *ChainConfig) String() string {
c.EIP158Block, c.EIP158Block,
c.ByzantiumBlock, c.ByzantiumBlock,
c.ConstantinopleBlock, c.ConstantinopleBlock,
c.PetersburgBlock,
engine, engine,
) )
} }
@ -244,6 +249,13 @@ func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
return isForked(c.ConstantinopleBlock, num) return isForked(c.ConstantinopleBlock, num)
} }
// IsPetersburg returns whether num is either
// - equal to or greater than the PetersburgBlock fork block,
// - OR is nil, and Constantinople is active
func (c *ChainConfig) IsPetersburg(num *big.Int) bool {
return isForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isForked(c.ConstantinopleBlock, 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
func (c *ChainConfig) IsEWASM(num *big.Int) bool { func (c *ChainConfig) IsEWASM(num *big.Int) bool {
return isForked(c.EWASMBlock, num) return isForked(c.EWASMBlock, num)
@ -314,6 +326,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
} }
if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, head) {
return newCompatError("ConstantinopleFix fork block", c.PetersburgBlock, newcfg.PetersburgBlock)
}
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) { if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock) return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
} }
@ -383,7 +398,7 @@ func (err *ConfigCompatError) Error() string {
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople bool IsByzantium, IsConstantinople, IsPetersburg bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -400,5 +415,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
IsEIP158: c.IsEIP158(num), IsEIP158: c.IsEIP158(num),
IsByzantium: c.IsByzantium(num), IsByzantium: c.IsByzantium(num),
IsConstantinople: c.IsConstantinople(num), IsConstantinople: c.IsConstantinople(num),
IsPetersburg: c.IsPetersburg(num),
} }
} }

View File

@ -62,6 +62,18 @@ var Forks = map[string]*params.ChainConfig{
DAOForkBlock: big.NewInt(0), DAOForkBlock: big.NewInt(0),
ByzantiumBlock: big.NewInt(0), ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(10000000),
},
"ConstantinopleFix": {
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
}, },
"FrontierToHomesteadAt5": { "FrontierToHomesteadAt5": {
ChainID: big.NewInt(1), ChainID: big.NewInt(1),

View File

@ -42,9 +42,22 @@ type RLPTest struct {
Out string Out string
} }
// FromHex returns the bytes represented by the hexadecimal string s.
// s may be prefixed with "0x".
// This is copy-pasted from bytes.go, which does not return the error
func FromHex(s string) ([]byte, error) {
if len(s) > 1 && (s[0:2] == "0x" || s[0:2] == "0X") {
s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
}
return hex.DecodeString(s)
}
// Run executes the test. // Run executes the test.
func (t *RLPTest) Run() error { func (t *RLPTest) Run() error {
outb, err := hex.DecodeString(t.Out) outb, err := FromHex(t.Out)
if err != nil { if err != nil {
return fmt.Errorf("invalid hex in Out") return fmt.Errorf("invalid hex in Out")
} }

View File

@ -17,6 +17,7 @@
package tests package tests
import ( import (
"bufio"
"bytes" "bytes"
"flag" "flag"
"fmt" "fmt"
@ -45,9 +46,12 @@ func TestState(t *testing.T) {
st.skipLoad(`^stTransactionTest/OverflowGasRequire\.json`) // gasLimit > 256 bits st.skipLoad(`^stTransactionTest/OverflowGasRequire\.json`) // gasLimit > 256 bits
st.skipLoad(`^stTransactionTest/zeroSigTransa[^/]*\.json`) // EIP-86 is not supported yet st.skipLoad(`^stTransactionTest/zeroSigTransa[^/]*\.json`) // EIP-86 is not supported yet
// Expected failures: // Expected failures:
st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`, "bug in test") st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/0`, "bug in test")
st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/Byzantium`, "bug in test") st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/3`, "bug in test")
st.fails(`^stRevertTest/RevertPrecompiledTouch.json/Constantinople`, "bug in test") st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Constantinople/0`, "bug in test")
st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Constantinople/3`, "bug in test")
st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/ConstantinopleFix/0`, "bug in test")
st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/ConstantinopleFix/3`, "bug in test")
st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) { st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) {
for _, subtest := range test.Subtests() { for _, subtest := range test.Subtests() {
@ -86,18 +90,19 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
t.Log("gas limit too high for EVM trace") t.Log("gas limit too high for EVM trace")
return return
} }
tracer := vm.NewStructLogger(nil) buf := new(bytes.Buffer)
w := bufio.NewWriter(buf)
tracer := vm.NewJSONLogger(&vm.LogConfig{DisableMemory: true}, w)
err2 := test(vm.Config{Debug: true, Tracer: tracer}) err2 := test(vm.Config{Debug: true, Tracer: tracer})
if !reflect.DeepEqual(err, err2) { if !reflect.DeepEqual(err, err2) {
t.Errorf("different error for second run: %v", err2) t.Errorf("different error for second run: %v", err2)
} }
buf := new(bytes.Buffer) w.Flush()
vm.WriteTrace(buf, tracer.StructLogs())
if buf.Len() == 0 { if buf.Len() == 0 {
t.Log("no EVM operation logs generated") t.Log("no EVM operation logs generated")
} else { } else {
t.Log("EVM operation log:\n" + buf.String()) t.Log("EVM operation log:\n" + buf.String())
} }
t.Logf("EVM output: 0x%x", tracer.Output()) //t.Logf("EVM output: 0x%x", tracer.Output())
t.Logf("EVM error: %v", tracer.Error()) //t.Logf("EVM error: %v", tracer.Error())
} }

@ -1 +1 @@
Subproject commit c02a2a17c0288a255572b37dc7ec1fcb838b9dbf Subproject commit 6b85703b568f4456582a00665d8a3e5c3b20b484