Enable networks to change maxCodeSize multiple times (#975)

This commit is contained in:
Sai V 2020-04-30 23:48:23 +08:00 committed by GitHub
parent 4c6727c959
commit b4e2199ca1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 251 additions and 31 deletions

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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(),
}

View File

@ -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

View File

@ -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 {