diff --git a/params/config.go b/params/config.go index 470feb3fc8..c8f755ae6f 100644 --- a/params/config.go +++ b/params/config.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" ) // Genesis hashes to enforce below configs on. @@ -430,6 +431,7 @@ type Transition struct { EpochLength uint64 `json:"epochlength,omitempty"` // Number of blocks that should pass before pending validator votes are reset BlockPeriodSeconds uint64 `json:"blockperiodseconds,omitempty"` // Minimum time between two consecutive IBFT or QBFT blocks’ timestamps in seconds RequestTimeoutSeconds uint64 `json:"requesttimeoutseconds,omitempty"` // Minimum request timeout for each IBFT or QBFT round in milliseconds + ContractSizeLimit uint64 `json:"contractsizelimit,omitempty"` // Maximum smart contract code size } // String implements the fmt.Stringer interface. @@ -565,6 +567,7 @@ func (c *ChainConfig) GetMaxCodeSize(num *big.Int) int { maxCodeSize := MaxCodeSize if len(c.MaxCodeSizeConfig) > 0 { + log.Warn("WARNING: The attribute config.maxCodeSizeConfig is deprecated and will be removed in the future, please use config.transitions.contractsizelimit on genesis file") for _, data := range c.MaxCodeSizeConfig { if data.Block.Cmp(num) > 0 { break @@ -580,6 +583,13 @@ func (c *ChainConfig) GetMaxCodeSize(num *big.Int) int { maxCodeSize = int(c.MaxCodeSize) * 1024 } } + if len(c.Transitions) > 0 { + for i := 0; i < len(c.Transitions) && c.Transitions[i].Block.Cmp(num) <= 0; i++ { + if c.Transitions[i].ContractSizeLimit != 0 { + maxCodeSize = int(c.Transitions[i].ContractSizeLimit) * 1024 + } + } + } return maxCodeSize } @@ -623,6 +633,9 @@ func (c *ChainConfig) CheckTransitionsData() error { if c.Istanbul != nil && c.Istanbul.TestQBFTBlock != nil && (transition.Algorithm == IBFT || transition.Algorithm == QBFT) { return ErrTestQBFTBlockAndTransitions } + if len(c.MaxCodeSizeConfig) > 0 && transition.ContractSizeLimit != 0 { + return ErrMaxCodeSizeConfigAndTransitions + } if transition.Algorithm == QBFT { isQBFT = true } @@ -635,6 +648,9 @@ func (c *ChainConfig) CheckTransitionsData() error { if transition.Algorithm == IBFT && isQBFT { return ErrTransition } + if transition.ContractSizeLimit != 0 && (transition.ContractSizeLimit < 24 || transition.ContractSizeLimit > 128) { + return ErrContractSizeLimit + } prevBlock = transition.Block } return nil @@ -754,6 +770,9 @@ func isTransitionsConfigCompatible(c1, c2 *ChainConfig, head *big.Int) (error, * if isSameBlock || c1.Transitions[i].EpochLength != c2.Transitions[i].EpochLength { return ErrTransitionIncompatible("EpochLength"), head, head } + if isSameBlock || c1.Transitions[i].ContractSizeLimit != c2.Transitions[i].ContractSizeLimit { + return ErrTransitionIncompatible("ContractSizeLimit"), head, head + } } return nil, big.NewInt(0), big.NewInt(0) diff --git a/params/config_test.go b/params/config_test.go index ac161509bd..4e15dd544d 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -328,10 +328,10 @@ func TestCheckTransitionsData(t *testing.T) { wantErr error } var ibftTransitionsConfig, qbftTransitionsConfig, invalidTransition, invalidBlockOrder []Transition - tranI0 := Transition{big.NewInt(0), IBFT, 30000, 5, 10} - tranQ5 := Transition{big.NewInt(5), QBFT, 30000, 5, 10} - tranI10 := Transition{big.NewInt(10), IBFT, 30000, 5, 10} - tranQ8 := Transition{big.NewInt(8), QBFT, 30000, 5, 10} + tranI0 := Transition{big.NewInt(0), IBFT, 30000, 5, 10, 50} + tranQ5 := Transition{big.NewInt(5), QBFT, 30000, 5, 10, 50} + tranI10 := Transition{big.NewInt(10), IBFT, 30000, 5, 10, 50} + tranQ8 := Transition{big.NewInt(8), QBFT, 30000, 5, 10, 50} ibftTransitionsConfig = append(ibftTransitionsConfig, tranI0, tranI10) qbftTransitionsConfig = append(qbftTransitionsConfig, tranQ5, tranQ8) @@ -391,7 +391,7 @@ func TestCheckTransitionsData(t *testing.T) { wantErr: ErrBlockOrder, }, { - stored: &ChainConfig{Transitions: []Transition{{nil, IBFT, 30000, 5, 10}}}, + stored: &ChainConfig{Transitions: []Transition{{nil, IBFT, 30000, 5, 10, 50}}}, wantErr: ErrBlockNumberMissing, }, { @@ -402,6 +402,26 @@ func TestCheckTransitionsData(t *testing.T) { stored: &ChainConfig{Transitions: []Transition{{Block: big.NewInt(0), Algorithm: ""}}}, wantErr: nil, }, + { + stored: &ChainConfig{MaxCodeSizeConfig: []MaxCodeConfigStruct{{big.NewInt(10), 24}}, Transitions: []Transition{{Block: big.NewInt(0), ContractSizeLimit: 50}}}, + wantErr: ErrMaxCodeSizeConfigAndTransitions, + }, + { + stored: &ChainConfig{Transitions: []Transition{{Block: big.NewInt(0), ContractSizeLimit: 23}}}, + wantErr: ErrContractSizeLimit, + }, + { + stored: &ChainConfig{Transitions: []Transition{{Block: big.NewInt(0), ContractSizeLimit: 129}}}, + wantErr: ErrContractSizeLimit, + }, + { + stored: &ChainConfig{Transitions: []Transition{{Block: big.NewInt(0), ContractSizeLimit: 50}}}, + wantErr: nil, + }, + { + stored: &ChainConfig{Transitions: []Transition{{Block: big.NewInt(0)}}}, + wantErr: nil, + }, } for _, test := range tests { @@ -411,3 +431,58 @@ func TestCheckTransitionsData(t *testing.T) { } } } + +func TestGetMaxCodeSize(t *testing.T) { + type test struct { + config *ChainConfig + blockNumber int64 + maxCode int + } + config1, config2, config3 := *TestChainConfig, *TestChainConfig, *TestChainConfig + config1.MaxCodeSizeConfig = []MaxCodeConfigStruct{ + {big.NewInt(2), 28}, + {big.NewInt(4), 32}, + } + config1.MaxCodeSize = 34 + config2.MaxCodeSize = 36 + config2.MaxCodeSizeChangeBlock = big.NewInt(2) + config3.MaxCodeSize = 0 + config3.Transitions = []Transition{ + {Block: big.NewInt(2), ContractSizeLimit: 50}, + {Block: big.NewInt(4), ContractSizeLimit: 54}, + } + maxCodeDefault := 32 * 1024 + tests := []test{ + {MainnetChainConfig, 0, MaxCodeSize}, + {RopstenChainConfig, 0, MaxCodeSize}, + {RinkebyChainConfig, 0, MaxCodeSize}, + {GoerliChainConfig, 0, MaxCodeSize}, + {YoloV3ChainConfig, 0, MaxCodeSize}, + {AllEthashProtocolChanges, 0, 35 * 1024}, + {AllCliqueProtocolChanges, 0, maxCodeDefault}, + {TestChainConfig, 0, maxCodeDefault}, + {QuorumTestChainConfig, 0, maxCodeDefault}, + {QuorumMPSTestChainConfig, 0, maxCodeDefault}, + {&config1, 0, MaxCodeSize}, + {&config1, 1, MaxCodeSize}, + {&config1, 2, 28 * 1024}, + {&config1, 3, 28 * 1024}, + {&config1, 4, 32 * 1024}, + {&config2, 0, MaxCodeSize}, + {&config2, 1, MaxCodeSize}, + {&config2, 2, 36 * 1024}, + {&config2, 3, 36 * 1024}, + {&config3, 0, MaxCodeSize}, + {&config3, 1, MaxCodeSize}, + {&config3, 2, 50 * 1024}, + {&config3, 3, 50 * 1024}, + {&config3, 4, 54 * 1024}, + {&config3, 8, 54 * 1024}, + } + for _, test := range tests { + maxCodeSize := test.config.GetMaxCodeSize(big.NewInt(test.blockNumber)) + if !reflect.DeepEqual(maxCodeSize, test.maxCode) { + t.Errorf("error mismatch:\nexpected: %v\nreceived: %v\n", test.maxCode, maxCodeSize) + } + } +} diff --git a/params/errors.go b/params/errors.go index 1240177da8..a6aa14a7bf 100644 --- a/params/errors.go +++ b/params/errors.go @@ -6,11 +6,13 @@ import ( ) var ( - ErrTransitionAlgorithm = errors.New("transition algorithm is invalid, should be either `ibft` or `qbft`") - ErrBlockNumberMissing = errors.New("block number not given in transitions data") - ErrBlockOrder = errors.New("block order should be ascending") - ErrTransition = errors.New("can't transition from qbft to ibft") - ErrTestQBFTBlockAndTransitions = errors.New("can't use transition algorithm and testQBFTBlock at the same time") + ErrTransitionAlgorithm = errors.New("transition algorithm is invalid, should be either `ibft` or `qbft`") + ErrBlockNumberMissing = errors.New("block number not given in transitions data") + ErrBlockOrder = errors.New("block order should be ascending") + ErrTransition = errors.New("can't transition from qbft to ibft") + ErrTestQBFTBlockAndTransitions = errors.New("can't use transition algorithm and testQBFTBlock at the same time") + ErrMaxCodeSizeConfigAndTransitions = errors.New("can't use transition ContractSizeLimit and MaxCodeSizeConfig at the same time") + ErrContractSizeLimit = errors.New("transition contract code size must be between 24 and 128") ) func ErrTransitionIncompatible(field string) error {