// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package params import ( "math/big" "reflect" "testing" "github.com/ethereum/go-ethereum/common" ) // Quorum - test code size and transaction size limit in chain config func TestMaxCodeSizeAndTransactionSizeLimit(t *testing.T) { type testData struct { size uint64 valid bool err string } type testDataType struct { isCodeSize bool data []testData } const codeSizeErr = "Genesis max code size must be between 24 and 128" const txSizeErr = "Genesis transaction size limit must be between 32 and 128" var codeSizeData = []testData{ {23, false, codeSizeErr}, {24, true, ""}, {50, true, ""}, {128, true, ""}, {129, false, codeSizeErr}, } var txSizeData = []testData{ {31, false, txSizeErr}, {32, true, ""}, {50, true, ""}, {128, true, ""}, {129, false, txSizeErr}, } var testDataArr = []testDataType{ {true, codeSizeData}, {false, txSizeData}, } for _, td := range testDataArr { var ccfg *ChainConfig for _, d := range td.data { var msgPrefix string if td.isCodeSize { ccfg = &ChainConfig{MaxCodeSize: d.size, TransactionSizeLimit: 50} msgPrefix = "max code size" } else { ccfg = &ChainConfig{MaxCodeSize: 50, TransactionSizeLimit: d.size} msgPrefix = "transaction size limit" } err := ccfg.IsValid() if d.valid { if err != nil { t.Errorf(msgPrefix+" %d, expected no error but got %v", d.size, err) } } else { if err == nil { t.Errorf(msgPrefix+" %d, expected error but got none", d.size) } else { if err.Error() != d.err { t.Errorf(msgPrefix+" %d, expected error but got %v", d.size, err.Error()) } } } } } } func TestCheckCompatible(t *testing.T) { type test struct { stored, new *ChainConfig head uint64 wantErr *ConfigCompatError } var storedMaxCodeConfig0, storedMaxCodeConfig1, storedMaxCodeConfig2 []MaxCodeConfigStruct defaultRec := MaxCodeConfigStruct{big.NewInt(0), 24} rec1 := MaxCodeConfigStruct{big.NewInt(5), 32} rec2 := MaxCodeConfigStruct{big.NewInt(10), 40} rec3 := MaxCodeConfigStruct{big.NewInt(8), 40} storedMaxCodeConfig0 = append(storedMaxCodeConfig0, defaultRec) storedMaxCodeConfig1 = append(storedMaxCodeConfig1, defaultRec) storedMaxCodeConfig1 = append(storedMaxCodeConfig1, rec1) storedMaxCodeConfig1 = append(storedMaxCodeConfig1, rec2) storedMaxCodeConfig2 = append(storedMaxCodeConfig2, rec1) storedMaxCodeConfig2 = append(storedMaxCodeConfig2, rec2) var passedValidMaxConfig0 []MaxCodeConfigStruct passedValidMaxConfig0 = append(passedValidMaxConfig0, defaultRec) passedValidMaxConfig0 = append(passedValidMaxConfig0, rec1) var passedValidMaxConfig1 []MaxCodeConfigStruct passedValidMaxConfig1 = append(passedValidMaxConfig1, defaultRec) passedValidMaxConfig1 = append(passedValidMaxConfig1, rec1) passedValidMaxConfig1 = append(passedValidMaxConfig1, rec3) tests := []test{ {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 0, wantErr: nil}, {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 100, wantErr: nil}, { stored: &ChainConfig{EIP150Block: big.NewInt(10)}, new: &ChainConfig{EIP150Block: big.NewInt(20)}, head: 9, wantErr: nil, }, { stored: AllEthashProtocolChanges, new: &ChainConfig{HomesteadBlock: nil}, head: 3, wantErr: &ConfigCompatError{ What: "Homestead fork block", StoredConfig: big.NewInt(0), NewConfig: nil, RewindTo: 0, }, }, { stored: AllEthashProtocolChanges, new: &ChainConfig{HomesteadBlock: big.NewInt(1)}, head: 3, wantErr: &ConfigCompatError{ What: "Homestead fork block", StoredConfig: big.NewInt(0), NewConfig: big.NewInt(1), RewindTo: 0, }, }, { stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)}, new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)}, head: 25, wantErr: &ConfigCompatError{ What: "EIP150 fork block", StoredConfig: big.NewInt(10), NewConfig: big.NewInt(20), RewindTo: 9, }, }, { stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)}, new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)}, head: 40, wantErr: nil, }, { stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)}, new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)}, head: 40, wantErr: &ConfigCompatError{ What: "Petersburg fork block", StoredConfig: nil, NewConfig: big.NewInt(31), RewindTo: 30, }, }, { stored: &ChainConfig{Istanbul: &IstanbulConfig{Ceil2Nby3Block: big.NewInt(10)}}, new: &ChainConfig{Istanbul: &IstanbulConfig{Ceil2Nby3Block: big.NewInt(20)}}, head: 4, wantErr: nil, }, { stored: &ChainConfig{Istanbul: &IstanbulConfig{Ceil2Nby3Block: big.NewInt(10)}}, new: &ChainConfig{Istanbul: &IstanbulConfig{Ceil2Nby3Block: big.NewInt(20)}}, head: 30, wantErr: &ConfigCompatError{ What: "Ceil 2N/3 fork block", StoredConfig: big.NewInt(10), NewConfig: big.NewInt(20), RewindTo: 9, }, }, { stored: &ChainConfig{Istanbul: &IstanbulConfig{TestQBFTBlock: big.NewInt(50)}}, new: &ChainConfig{Istanbul: &IstanbulConfig{TestQBFTBlock: big.NewInt(60)}}, head: 40, wantErr: nil, }, { stored: &ChainConfig{Istanbul: &IstanbulConfig{TestQBFTBlock: big.NewInt(20)}}, new: &ChainConfig{Istanbul: &IstanbulConfig{TestQBFTBlock: big.NewInt(30)}}, head: 20, wantErr: &ConfigCompatError{ What: "Test QBFT fork block", StoredConfig: big.NewInt(20), NewConfig: big.NewInt(30), RewindTo: 19, }, }, { stored: &ChainConfig{MaxCodeSizeChangeBlock: big.NewInt(10)}, new: &ChainConfig{MaxCodeSizeChangeBlock: big.NewInt(20)}, head: 30, wantErr: &ConfigCompatError{ What: "max code size change fork block", StoredConfig: big.NewInt(10), NewConfig: big.NewInt(20), RewindTo: 9, }, }, { stored: &ChainConfig{MaxCodeSizeChangeBlock: big.NewInt(10)}, new: &ChainConfig{MaxCodeSizeChangeBlock: big.NewInt(20)}, head: 4, wantErr: nil, }, { stored: &ChainConfig{QIP714Block: big.NewInt(10)}, new: &ChainConfig{QIP714Block: big.NewInt(20)}, head: 30, wantErr: &ConfigCompatError{ What: "permissions fork block", StoredConfig: big.NewInt(10), NewConfig: big.NewInt(20), RewindTo: 9, }, }, { stored: &ChainConfig{QIP714Block: big.NewInt(10)}, new: &ChainConfig{QIP714Block: big.NewInt(20)}, head: 4, wantErr: nil, }, { stored: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig0}, new: &ChainConfig{MaxCodeSizeConfig: nil}, head: 4, wantErr: &ConfigCompatError{ What: "genesis file missing max code size information", StoredConfig: big.NewInt(4), NewConfig: big.NewInt(4), RewindTo: 3, }, }, { stored: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig0}, new: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig0}, head: 4, wantErr: nil, }, { stored: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig0}, new: &ChainConfig{MaxCodeSizeConfig: passedValidMaxConfig0}, head: 10, wantErr: &ConfigCompatError{ What: "maxCodeSizeConfig data incompatible. updating maxCodeSize for past", StoredConfig: big.NewInt(10), NewConfig: big.NewInt(10), RewindTo: 9, }, }, { stored: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig0}, new: &ChainConfig{MaxCodeSizeConfig: passedValidMaxConfig0}, head: 4, wantErr: nil, }, { stored: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig1}, new: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig1}, head: 12, wantErr: nil, }, { stored: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig1}, new: &ChainConfig{MaxCodeSizeConfig: passedValidMaxConfig1}, head: 12, wantErr: &ConfigCompatError{ What: "maxCodeSizeConfig data incompatible. maxCodeSize historical data does not match", StoredConfig: big.NewInt(12), NewConfig: big.NewInt(12), RewindTo: 11, }, }, { stored: &ChainConfig{MaxCodeSize: 32}, new: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig2}, head: 8, wantErr: nil, }, { stored: &ChainConfig{MaxCodeSize: 32}, new: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig2}, head: 15, wantErr: nil, }, { stored: &ChainConfig{MaxCodeSize: 32, MaxCodeSizeChangeBlock: big.NewInt(10)}, new: &ChainConfig{MaxCodeSizeConfig: storedMaxCodeConfig1}, head: 15, wantErr: nil, }, } for _, test := range tests { err := test.stored.CheckCompatible(test.new, test.head, false) if !reflect.DeepEqual(err, test.wantErr) { t.Errorf("error mismatch:\nstored: %v\nnew: %v\nhead: %v\nerr: %v\nwant: %v", test.stored, test.new, test.head, err, test.wantErr) } } } func TestCheckTransitionsData(t *testing.T) { type test struct { stored *ChainConfig wantErr error } var ibftTransitionsConfig, qbftTransitionsConfig, invalidTransition, invalidBlockOrder []Transition var emptyBlockPeriodSeconds uint64 = 10 tranI0 := Transition{big.NewInt(0), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil} tranQ5 := Transition{big.NewInt(5), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil} tranI10 := Transition{big.NewInt(10), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil} tranQ8 := Transition{big.NewInt(8), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil} ibftTransitionsConfig = append(ibftTransitionsConfig, tranI0, tranI10) qbftTransitionsConfig = append(qbftTransitionsConfig, tranQ5, tranQ8) invalidTransition = append(invalidTransition, tranI0, tranQ5, tranI10) invalidBlockOrder = append(invalidBlockOrder, tranQ8, tranQ5) tests := []test{ {stored: MainnetChainConfig, wantErr: nil}, {stored: RopstenChainConfig, wantErr: nil}, {stored: RinkebyChainConfig, wantErr: nil}, {stored: GoerliChainConfig, wantErr: nil}, {stored: YoloV3ChainConfig, wantErr: nil}, {stored: AllEthashProtocolChanges, wantErr: nil}, {stored: AllCliqueProtocolChanges, wantErr: nil}, {stored: TestChainConfig, wantErr: nil}, {stored: QuorumTestChainConfig, wantErr: nil}, {stored: QuorumMPSTestChainConfig, wantErr: nil}, { stored: &ChainConfig{IBFT: &IBFTConfig{}}, wantErr: nil, }, { stored: &ChainConfig{IBFT: &IBFTConfig{}, Transitions: ibftTransitionsConfig}, wantErr: nil, }, { stored: &ChainConfig{QBFT: &QBFTConfig{}}, wantErr: nil, }, { stored: &ChainConfig{QBFT: &QBFTConfig{}, Transitions: qbftTransitionsConfig}, wantErr: nil, }, { stored: &ChainConfig{IBFT: &IBFTConfig{}, Transitions: qbftTransitionsConfig}, wantErr: nil, }, { stored: &ChainConfig{Transitions: ibftTransitionsConfig}, wantErr: nil, }, { stored: &ChainConfig{Transitions: qbftTransitionsConfig}, wantErr: nil, }, { stored: &ChainConfig{IBFT: &IBFTConfig{}, Transitions: invalidTransition}, wantErr: ErrTransition, }, { stored: &ChainConfig{QBFT: &QBFTConfig{}, Transitions: ibftTransitionsConfig}, wantErr: ErrTransition, }, { stored: &ChainConfig{Transitions: invalidBlockOrder}, wantErr: ErrBlockOrder, }, { stored: &ChainConfig{Transitions: []Transition{{nil, IBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}}}, wantErr: ErrBlockNumberMissing, }, { stored: &ChainConfig{Transitions: []Transition{{Block: big.NewInt(0), Algorithm: "AA"}}}, wantErr: ErrTransitionAlgorithm, }, { 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 { err := test.stored.CheckTransitionsData() if !reflect.DeepEqual(err, test.wantErr) { t.Errorf("error mismatch:\nstored: %v\nerr: %v\nwant: %v", test.stored, err, test.wantErr) } } } 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) } } } func TestIsQIP714(t *testing.T) { type test struct { config *ChainConfig blockNumber int64 IsQIP714 bool } config1, config2 := *TestChainConfig, *TestChainConfig config1.QIP714Block = big.NewInt(11) config2.QIP714Block = nil config2.Transitions = []Transition{ {Block: big.NewInt(21), EnhancedPermissioningEnabled: newPBool(true)}, } tests := []test{ {MainnetChainConfig, 0, false}, {&config1, 10, false}, {&config1, 11, true}, {&config2, 20, false}, {&config2, 21, true}, {&config2, 22, true}, } for _, test := range tests { isQIP714 := test.config.IsQIP714(big.NewInt(test.blockNumber)) if !reflect.DeepEqual(isQIP714, test.IsQIP714) { t.Errorf("error mismatch on %v:\nexpected: %v\nreceived: %v\n", test.blockNumber, test.IsQIP714, isQIP714) } } } func TestIsPrivacyEnhancementsEnabled(t *testing.T) { type test struct { config *ChainConfig blockNumber int64 PrivacyEnhancementsEnabled bool } config1, config2 := *TestChainConfig, *TestChainConfig config1.PrivacyEnhancementsBlock = big.NewInt(11) config2.PrivacyEnhancementsBlock = nil config2.Transitions = []Transition{ {Block: big.NewInt(21), PrivacyEnhancementsEnabled: newPBool(true)}, } tests := []test{ {MainnetChainConfig, 0, false}, {&config1, 10, false}, {&config1, 11, true}, {&config2, 20, false}, {&config2, 21, true}, {&config2, 22, true}, } for _, test := range tests { isPrivacyEnhancementsEnabled := test.config.IsPrivacyEnhancementsEnabled(big.NewInt(test.blockNumber)) if !reflect.DeepEqual(isPrivacyEnhancementsEnabled, test.PrivacyEnhancementsEnabled) { t.Errorf("error mismatch on %v:\nexpected: %v\nreceived: %v\n", test.blockNumber, test.PrivacyEnhancementsEnabled, isPrivacyEnhancementsEnabled) } } } func TestIsPrivacyPrecompileEnabled(t *testing.T) { type test struct { config *ChainConfig blockNumber int64 PrivacyPrecompileEnabled bool } config1, config2 := *TestChainConfig, *TestChainConfig config1.PrivacyPrecompileBlock = big.NewInt(11) config2.PrivacyPrecompileBlock = nil config2.Transitions = []Transition{ {Block: big.NewInt(21), PrivacyPrecompileEnabled: newPBool(true)}, } tests := []test{ {MainnetChainConfig, 0, false}, {&config1, 10, false}, {&config1, 11, true}, {&config2, 20, false}, {&config2, 21, true}, {&config2, 22, true}, } for _, test := range tests { isPrivacyPrecompileEnabled := test.config.IsPrivacyPrecompileEnabled(big.NewInt(test.blockNumber)) if !reflect.DeepEqual(isPrivacyPrecompileEnabled, test.PrivacyPrecompileEnabled) { t.Errorf("error mismatch on %v:\nexpected: %v\nreceived: %v\n", test.blockNumber, test.PrivacyPrecompileEnabled, isPrivacyPrecompileEnabled) } } } func TestIsGasPriceEnabled(t *testing.T) { type test struct { config *ChainConfig blockNumber int64 GasPriceEnabled bool } config1, config2 := *TestChainConfig, *TestChainConfig config1.EnableGasPriceBlock = big.NewInt(11) config2.EnableGasPriceBlock = nil config2.Transitions = []Transition{ {Block: big.NewInt(21), GasPriceEnabled: newPBool(true)}, } tests := []test{ {MainnetChainConfig, 0, false}, {&config1, 10, false}, {&config1, 11, true}, {&config2, 20, false}, {&config2, 21, true}, {&config2, 22, true}, } for _, test := range tests { isGasPriceEnabled := test.config.IsGasPriceEnabled(big.NewInt(test.blockNumber)) if !reflect.DeepEqual(isGasPriceEnabled, test.GasPriceEnabled) { t.Errorf("error mismatch on %v:\nexpected: %v\nreceived: %v\n", test.blockNumber, test.GasPriceEnabled, isGasPriceEnabled) } } } func newPBool(b bool) *bool { return &b }