diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index e61ce5909..f798c2cc4 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -231,6 +231,13 @@ func initGenesis(ctx *cli.Context) error { file.Seek(0, 0) genesis.Config.IsQuorum = getIsQuorum(file) + // check the data given as a part of newMaxConfigData to ensure that + // its in expected order + err = genesis.Config.CheckMaxCodeConfigData() + if err != nil { + utils.Fatalf("maxCodeSize data invalid: %v", err) + } + // Open an initialise both full and light databases stack := makeFullNode(ctx) defer stack.Close() diff --git a/core/genesis.go b/core/genesis.go index 5bc2b856c..4e3109b77 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -173,10 +173,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override if genesis.Config.TransactionSizeLimit == 0 { genesis.Config.TransactionSizeLimit = DefaultTxPoolConfig.TransactionSizeLimit } - // Set default contract size limit that can be deployed if not set in genesis - if genesis.Config.MaxCodeSize == 0 { - genesis.Config.MaxCodeSize = DefaultTxPoolConfig.MaxCodeSize - } // Check transaction size limit and max contract code size err := genesis.Config.IsValid() diff --git a/core/vm/evm.go b/core/vm/evm.go index d53996709..ef4eef8e6 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -500,14 +500,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := run(evm, contract, nil, false) - // Quorum + maxCodeSize := evm.ChainConfig().GetMaxCodeSize(evm.BlockNumber) // check whether the max code size has been exceeded, check maxcode size from chain config - var maxCodeSize int - if evm.chainConfig.IsMaxCodeSizeChangeBlock(evm.BlockNumber) && evm.ChainConfig().MaxCodeSize > 0 { - maxCodeSize = int(evm.ChainConfig().MaxCodeSize * 1024) - } else { - maxCodeSize = params.MaxCodeSize - } maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > 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 diff --git a/eth/handler.go b/eth/handler.go index 6f0cde92d..87b828c50 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -897,11 +897,19 @@ type NodeInfo struct { // NodeInfo retrieves some protocol metadata about the running host node. func (pm *ProtocolManager) NodeInfo() *NodeInfo { currentBlock := pm.blockchain.CurrentBlock() + // //Quorum + // + // changes done to fetch maxCodeSize dynamically based on the + // maxCodeSizeConfig changes + // /Quorum + chainConfig := pm.blockchain.Config() + chainConfig.MaxCodeSize = uint64(chainConfig.GetMaxCodeSize(pm.blockchain.CurrentBlock().Number()) / 1024) + return &NodeInfo{ Network: pm.networkID, Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), Genesis: pm.blockchain.Genesis().Hash(), - Config: pm.blockchain.Config(), + Config: chainConfig, Head: currentBlock.Hash(), Consensus: pm.getConsensusAlgorithm(), } diff --git a/params/config.go b/params/config.go index b17cebbfe..bcc8313ab 100644 --- a/params/config.go +++ b/params/config.go @@ -214,19 +214,19 @@ 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), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil, false, 32, 32, big.NewInt(0), big.NewInt(0)} + 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), big.NewInt(0), nil, new(EthashConfig), nil, nil, false, 32, 32, big.NewInt(0), big.NewInt(0), nil} // 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), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false, 32, 32, big.NewInt(0), big.NewInt(0)} + 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), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false, 32, 32, big.NewInt(0), big.NewInt(0), nil} - TestChainConfig = &ChainConfig{big.NewInt(10), 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), big.NewInt(0), nil, new(EthashConfig), nil, nil, false, 32, 32, big.NewInt(0), big.NewInt(0)} + TestChainConfig = &ChainConfig{big.NewInt(10), 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), big.NewInt(0), nil, new(EthashConfig), nil, nil, false, 32, 32, big.NewInt(0), big.NewInt(0), nil} TestRules = TestChainConfig.Rules(new(big.Int)) - QuorumTestChainConfig = &ChainConfig{big.NewInt(10), 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), big.NewInt(0), nil, new(EthashConfig), nil, nil, true, 64, 32, big.NewInt(0), big.NewInt(0)} + QuorumTestChainConfig = &ChainConfig{big.NewInt(10), 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), big.NewInt(0), nil, new(EthashConfig), nil, nil, true, 64, 32, big.NewInt(0), big.NewInt(0), nil} ) // TrustedCheckpoint represents a set of post-processed trie roots (CHT and @@ -271,6 +271,11 @@ type CheckpointOracleConfig struct { Threshold uint64 `json:"threshold"` } +type MaxCodeConfigStruct struct { + Block *big.Int `json:"block,omitempty"` + Size uint64 `json:"size,omitempty"` +} + // ChainConfig is the core config which determines the blockchain settings. // // ChainConfig is stored in the database on a per block basis. This means @@ -310,6 +315,9 @@ type ChainConfig struct { // QIP714Block implements the permissions related changes QIP714Block *big.Int `json:"qip714Block,omitempty"` MaxCodeSizeChangeBlock *big.Int `json:"maxCodeSizeChangeBlock,omitempty"` + // to track multiple changes to maxCodeSize + MaxCodeSizeConfig []MaxCodeConfigStruct `json:"maxCodeSizeConfig,omitempty"` + // Quorum } // EthashConfig is the consensus engine configs for proof-of-work based sealing. @@ -384,7 +392,7 @@ func (c *ChainConfig) IsValid() error { return errors.New("Genesis transaction size limit must be between 32 and 128") } - if c.MaxCodeSize < 24 || c.MaxCodeSize > 128 { + if c.MaxCodeSize != 0 && (c.MaxCodeSize < 24 || c.MaxCodeSize > 128) { return errors.New("Genesis max code size must be between 24 and 128") } @@ -450,19 +458,133 @@ func (c *ChainConfig) IsQIP714(num *big.Int) bool { return isForked(c.QIP714Block, num) } -// Quorum -// -// IsMaxCodeSizeChangeBlock returns whether num represents a block number max code size -// was changed from default 24K to new value +// IsMaxCodeSizeChangeBlock returns whether num represents a block number +// where maxCodeSize change was done func (c *ChainConfig) IsMaxCodeSizeChangeBlock(num *big.Int) bool { return isForked(c.MaxCodeSizeChangeBlock, num) } +// Quorum +// +// GetMaxCodeSize returns maxCodeSize for the given block number +func (c *ChainConfig) GetMaxCodeSize(num *big.Int) int { + maxCodeSize := MaxCodeSize + + if len(c.MaxCodeSizeConfig) > 0 { + for _, data := range c.MaxCodeSizeConfig { + if data.Block.Cmp(num) > 0 { + break + } + maxCodeSize = int(data.Size) * 1024 + } + } else if c.MaxCodeSize > 0 { + if c.MaxCodeSizeChangeBlock != nil && c.MaxCodeSizeChangeBlock.Cmp(big.NewInt(0)) >= 0 { + if c.IsMaxCodeSizeChangeBlock(num) { + maxCodeSize = int(c.MaxCodeSize) * 1024 + } + } else { + maxCodeSize = int(c.MaxCodeSize) * 1024 + } + } + return maxCodeSize +} + +// validates the maxCodeSizeConfig data passed in config +func (c *ChainConfig) CheckMaxCodeConfigData() error { + if c.MaxCodeSize != 0 || (c.MaxCodeSizeChangeBlock != nil && c.MaxCodeSizeChangeBlock.Cmp(big.NewInt(0)) >= 0) { + return errors.New("maxCodeSize & maxCodeSizeChangeBlock deprecated. Consider using maxCodeSizeConfig") + } + // validate max code size data + // 1. Code size should not be less than 24 and greater than 128 + // 2. block entries are in ascending order + prevBlock := big.NewInt(0) + for _, data := range c.MaxCodeSizeConfig { + if data.Size < 24 || data.Size > 128 { + return errors.New("Genesis max code size must be between 24 and 128") + } + if data.Block == nil { + return errors.New("Block number not given in maxCodeSizeConfig data") + } + if data.Block.Cmp(prevBlock) < 0 { + return errors.New("invalid maxCodeSize detail, block order has to be ascending") + } + prevBlock = data.Block + } + + return nil +} + +// checks if changes to maxCodeSizeConfig proposed are compatible +// with already existing genesis data +func isMaxCodeSizeConfigCompatible(c1, c2 *ChainConfig, head *big.Int) (error, *big.Int, *big.Int) { + if len(c1.MaxCodeSizeConfig) == 0 && len(c2.MaxCodeSizeConfig) == 0 { + // maxCodeSizeConfig not used. return + return nil, big.NewInt(0), big.NewInt(0) + } + + // existing config had maxCodeSizeConfig and new one does not have the same return error + if len(c1.MaxCodeSizeConfig) > 0 && len(c2.MaxCodeSizeConfig) == 0 { + return fmt.Errorf("genesis file missing max code size information"), head, head + } + + if len(c2.MaxCodeSizeConfig) > 0 && len(c1.MaxCodeSizeConfig) == 0 { + return nil, big.NewInt(0), big.NewInt(0) + } + + // check the number of records below current head in both configs + // if they do not match throw an error + c1RecsBelowHead := 0 + for _, data := range c1.MaxCodeSizeConfig { + if data.Block.Cmp(head) <= 0 { + c1RecsBelowHead++ + } else { + break + } + } + + c2RecsBelowHead := 0 + for _, data := range c2.MaxCodeSizeConfig { + if data.Block.Cmp(head) <= 0 { + c2RecsBelowHead++ + } else { + break + } + } + + // if the count of past records is not matching return error + if c1RecsBelowHead != c2RecsBelowHead { + return errors.New("maxCodeSizeConfig data incompatible. updating maxCodeSize for past"), head, head + } + + // validate that each past record is matching exactly. if not return error + for i := 0; i < c1RecsBelowHead; i++ { + if c1.MaxCodeSizeConfig[i].Block.Cmp(c2.MaxCodeSizeConfig[i].Block) != 0 || + c1.MaxCodeSizeConfig[i].Size != c2.MaxCodeSizeConfig[i].Size { + return errors.New("maxCodeSizeConfig data incompatible. maxCodeSize historical data does not match"), head, head + } + } + + return nil, big.NewInt(0), big.NewInt(0) +} + +// /Quorum + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, isQuorumEIP155Activated bool) *ConfigCompatError { bhead := new(big.Int).SetUint64(height) + // check if the maxCodesize data passed is compatible 1st + // this is being handled separately as it can have breaks + // at multiple block heights and cannot be handled with in + // checkCompatible + + // compare the maxCodeSize data between the old and new config + err, cBlock, newCfgBlock := isMaxCodeSizeConfigCompatible(c, newcfg, bhead) + if err != nil { + return newCompatError(err.Error(), cBlock, newCfgBlock) + } + // Iterate checkCompatible to find the lowest conflict. var lasterr *ConfigCompatError for { @@ -558,7 +680,7 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int, isQuor if isForkIncompatible(c.QIP714Block, newcfg.QIP714Block, head) { return newCompatError("permissions fork block", c.QIP714Block, newcfg.QIP714Block) } - if isForkIncompatible(c.MaxCodeSizeChangeBlock, newcfg.MaxCodeSizeChangeBlock, head) { + if newcfg.MaxCodeSizeChangeBlock != nil && isForkIncompatible(c.MaxCodeSizeChangeBlock, newcfg.MaxCodeSizeChangeBlock, head) { return newCompatError("max code size change fork block", c.MaxCodeSizeChangeBlock, newcfg.MaxCodeSizeChangeBlock) } return nil diff --git a/params/config_test.go b/params/config_test.go index 1c716d2d5..5f3189b34 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -92,6 +92,31 @@ func TestCheckCompatible(t *testing.T) { 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}, @@ -152,8 +177,8 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: &ChainConfig{MaxCodeSizeChangeBlock:big.NewInt(10)}, - new: &ChainConfig{MaxCodeSizeChangeBlock:big.NewInt(20)}, + stored: &ChainConfig{MaxCodeSizeChangeBlock: big.NewInt(10)}, + new: &ChainConfig{MaxCodeSizeChangeBlock: big.NewInt(20)}, head: 30, wantErr: &ConfigCompatError{ What: "max code size change fork block", @@ -163,14 +188,14 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: &ChainConfig{MaxCodeSizeChangeBlock:big.NewInt(10)}, - new: &ChainConfig{MaxCodeSizeChangeBlock:big.NewInt(20)}, + 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)}, + stored: &ChainConfig{QIP714Block: big.NewInt(10)}, + new: &ChainConfig{QIP714Block: big.NewInt(20)}, head: 30, wantErr: &ConfigCompatError{ What: "permissions fork block", @@ -180,12 +205,80 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: &ChainConfig{QIP714Block:big.NewInt(10)}, - new: &ChainConfig{QIP714Block:big.NewInt(20)}, + 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 {