author vsmk98 <vsaimk@gmail.com> 1585565614 +0800
committer Trung Nguyen <24930+trung@users.noreply.github.com> 1588174146 -0400
gpgsig -----BEGIN PGP SIGNATURE-----

 iQEzBAABCAAdFiEEzAFy365WU5uP2ESgRjZDTtlQXrcFAl6pnUIACgkQRjZDTtlQ
 XrcL2Af+IKHLFkPhSoEHvhN+KDEKqNt1GFZQuLLUhgP9yFsbnbgPlUaYPSkWTAOk
 v0EMUnYUjNsKSj6PsWj4CaW2DBovInIGE/R1oWjmpjPY+IiLJZHg9S+3o9D/pRd6
 a/jXcTdsL9t/u9RqZICzJwVEWcSFNHWH8/EhWpf/NExrxgiue9Ib146hzJn7Rzss
 vgts6vco87r+ck6SZlqNrldwz/V1kKQXDRsTrPC4h7sJ6DTG+NgGDjXu9A5PuVB2
 9byGG1EDNTuqhaHJoUu32m3qs61WKXprTAuo3Bo4i38yemiotPKotBCNKo1odbrV
 h9mseIdlSfWEOx0SdfbVXe3qMMYe7w==
 =7LJs
 -----END PGP SIGNATURE-----

Enable networks to change maxCodeSize multiple times #975

Current maxCodeSize implementation allows change of maxCodeSize once in network life.
If the maxCodeSize is changed multiple times, its possible that any new node joining
the network can get Bad block error while syncing the blocks as the historical value
of maxCodeSize at that particular block height may not match with the current maxCodeSize.
It should be possible to track the historical changes of maxCodeSize in genesis and
thus allow it to be changed multiple times in network life.
This commit is contained in:
vsmk98 2020-03-30 18:53:34 +08:00 committed by Trung Nguyen
parent c215989c10
commit 5cab3a794d
No known key found for this signature in database
GPG Key ID: 4636434ED9505EB7
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 {