From d6d999a10004c2fb76efac90d24614eaab517f18 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 27 Feb 2023 17:47:38 -0800 Subject: [PATCH 01/30] state modifications as network upgrade --- core/state_processor.go | 25 ++++++++++ core/vm/evm.go | 8 ++++ miner/worker.go | 6 +++ params/config.go | 17 ++++++- params/precompile_config_test.go | 8 ++-- params/precompile_upgrade.go | 78 +++++++++++++++++++++++++++--- params/state_upgrade.go | 44 +++++++++++++++++ precompile/contract/interfaces.go | 3 -- stateupgrade/interfaces.go | 35 ++++++++++++++ stateupgrade/state_upgrade.go | 80 +++++++++++++++++++++++++++++++ 10 files changed, 288 insertions(+), 16 deletions(-) create mode 100644 params/state_upgrade.go create mode 100644 stateupgrade/interfaces.go create mode 100644 stateupgrade/state_upgrade.go diff --git a/core/state_processor.go b/core/state_processor.go index 2634f83640..0a8854ebba 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/precompile/modules" + "github.com/ava-labs/subnet-evm/stateupgrade" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -86,6 +87,12 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state log.Error("failed to configure precompiles processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err) return nil, nil, 0, err } + // Configure any state upgrades that should go into effect during this block. + err = ApplyStateUpgrades(p.config, new(big.Int).SetUint64(parent.Time), p.bc, header, statedb, cfg) + if err != nil { + log.Error("failed to configure state upgrades processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err) + return nil, nil, 0, err + } blockContext := NewEVMBlockContext(header, p.bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) @@ -218,3 +225,21 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, } return nil } + +// ApplyStateUpgrades checks if any of the state upgrades specified by the chain config are activated by the block +// transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure] +// to apply the necessary state transitions for the upgrade. +// This function is called: +// - during block processing to update the state before processing the given block. +// - during block producing to apply the precompile upgrades before producing the block. +func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, bc ChainContext, header *types.Header, statedb *state.StateDB, cfg vm.Config) error { + blockContext := NewEVMBlockContext(header, bc, nil) + // Apply state upgrades + for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, c, cfg) + if err := stateupgrade.Configure(&upgrade, &blockContext, statedb, vmenv); err != nil { + return fmt.Errorf("could not configure state upgrade, reason: %w", err) + } + } + return nil +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 764bce4e65..be36b07edd 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -604,5 +604,13 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } +// CreateAt creates a new contract using code as deployment code, +// and deploys it to the specified address. For use by the state upgrade. +func (evm *EVM) CreateAt(contractAddr common.Address, callerAddr common.Address, code []byte, gas uint64, value *big.Int) ([]byte, common.Address, uint64, error) { + codeAndHash := &codeAndHash{code: code} + caller := AccountRef(callerAddr) + return evm.create(caller, codeAndHash, gas, value, contractAddr, CREATE) +} + // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } diff --git a/miner/worker.go b/miner/worker.go index 26a9c31168..5c96346d10 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -191,6 +191,12 @@ func (w *worker) commitNewWork() (*types.Block, error) { log.Error("failed to configure precompiles mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err) return nil, err } + // Configure any state upgrades that should go into effect during this block. + err = core.ApplyStateUpgrades(w.chainConfig, new(big.Int).SetUint64(parent.Time()), w.chain, header, env.state, *w.chain.GetVMConfig()) + if err != nil { + log.Error("failed to configure state upgrades mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err) + return nil, err + } // Fill the block with all available pending transactions. pending := w.eth.TxPool().Pending(true) diff --git a/params/config.go b/params/config.go index 1f2289f2a9..37e14c9ac9 100644 --- a/params/config.go +++ b/params/config.go @@ -220,6 +220,9 @@ type UpgradeConfig struct { // forks must be present or upgradeBytes will be rejected. NetworkUpgrades *NetworkUpgrades `json:"networkUpgrades,omitempty"` + // Config for modifying state as a network upgrade. + StateUpgrades []StateUpgrade `json:"stateUpgrades,omitempty"` + // Config for enabling and disabling precompiles as network upgrades. PrecompileUpgrades []PrecompileUpgrade `json:"precompileUpgrades,omitempty"` } @@ -323,7 +326,7 @@ func (c *ChainConfig) IsSubnetEVM(blockTimestamp *big.Int) bool { // IsPrecompileEnabled returns whether precompile with [address] is enabled at [blockTimestamp]. func (c *ChainConfig) IsPrecompileEnabled(address common.Address, blockTimestamp *big.Int) bool { - config := c.GetActivePrecompileConfig(address, blockTimestamp) + config := c.getActivePrecompileConfig(address, blockTimestamp) return config != nil && !config.IsDisabled() } @@ -357,6 +360,11 @@ func (c *ChainConfig) Verify() error { return fmt.Errorf("invalid precompile upgrades: %w", err) } + // Verify the state upgrades are internally consistent given the existing chainConfig. + if err := c.verifyStateUpgrades(); err != nil { + return fmt.Errorf("invalid state upgrades: %w", err) + } + return nil } @@ -491,6 +499,11 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, lastHeight *big.Int, return err } + // Check that the state upgrades on the new config are compatible with the existing state upgrade config. + if err := c.CheckStateUpgradesCompatible(newcfg.StateUpgrades, lastTimestamp); err != nil { + return err + } + // TODO verify that the fee config is fully compatible between [c] and [newcfg]. return nil } @@ -596,7 +609,7 @@ func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules { // Initialize the stateful precompiles that should be enabled at [blockTimestamp]. rules.ActivePrecompiles = make(map[common.Address]precompileconfig.Config) for _, module := range modules.RegisteredModules() { - if config := c.GetActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() { + if config := c.getActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() { rules.ActivePrecompiles[module.Address] = config } } diff --git a/params/precompile_config_test.go b/params/precompile_config_test.go index ec81ecaf36..488204631b 100644 --- a/params/precompile_config_test.go +++ b/params/precompile_config_test.go @@ -269,16 +269,16 @@ func TestGetPrecompileConfig(t *testing.T) { deployerallowlist.ConfigKey: deployerallowlist.NewConfig(big.NewInt(10), nil, nil), } - deployerConfig := config.GetActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(0)) + deployerConfig := config.getActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(0)) require.Nil(deployerConfig) - deployerConfig = config.GetActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(10)) + deployerConfig = config.getActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(10)) require.NotNil(deployerConfig) - deployerConfig = config.GetActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(11)) + deployerConfig = config.getActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(11)) require.NotNil(deployerConfig) - txAllowListConfig := config.GetActivePrecompileConfig(txallowlist.ContractAddress, big.NewInt(0)) + txAllowListConfig := config.getActivePrecompileConfig(txallowlist.ContractAddress, big.NewInt(0)) require.Nil(txAllowListConfig) } diff --git a/params/precompile_upgrade.go b/params/precompile_upgrade.go index 98a32d4d42..80bbc49d28 100644 --- a/params/precompile_upgrade.go +++ b/params/precompile_upgrade.go @@ -66,8 +66,7 @@ func (u *PrecompileUpgrade) MarshalJSON() ([]byte, error) { // verifyPrecompileUpgrades checks [c.PrecompileUpgrades] is well formed: // - [upgrades] must specify exactly one key per PrecompileUpgrade // - the specified blockTimestamps must monotonically increase -// - the specified blockTimestamps must be compatible with those -// specified in the chainConfig by genesis. +// - the specified blockTimestamps must be compatible with those specified in the chainConfig by genesis. // - check a precompile is disabled before it is re-enabled func (c *ChainConfig) verifyPrecompileUpgrades() error { // Store this struct to keep track of the last upgrade for each precompile key. @@ -149,9 +148,24 @@ func (c *ChainConfig) verifyPrecompileUpgrades() error { return nil } -// GetActivePrecompileConfig returns the most recent precompile config corresponding to [address]. +// verifyStateUpgrades checks [c.StateUpgrades] is well formed: +// - the specified blockTimestamps must monotonically increase +func (c *ChainConfig) verifyStateUpgrades() error { + var previousUpgradeTimestamp *big.Int + for i, upgrade := range c.StateUpgrades { + upgradeTimestamp := upgrade.blockTimestamp + // Verify specified timestamps are strictly monotonically increasing. + if previousUpgradeTimestamp != nil && upgradeTimestamp.Cmp(previousUpgradeTimestamp) <= 0 { + return fmt.Errorf("StateUpgrade[%d]: config block timestamp (%v) <= previous timestamp (%v)", i, upgradeTimestamp, previousUpgradeTimestamp) + } + previousUpgradeTimestamp = upgradeTimestamp + } + return nil +} + +// getActivePrecompileConfig returns the most recent precompile config corresponding to [address]. // If none have occurred, returns nil. -func (c *ChainConfig) GetActivePrecompileConfig(address common.Address, blockTimestamp *big.Int) precompileconfig.Config { +func (c *ChainConfig) getActivePrecompileConfig(address common.Address, blockTimestamp *big.Int) precompileconfig.Config { configs := c.GetActivatingPrecompileConfigs(address, nil, blockTimestamp, c.PrecompileUpgrades) if len(configs) == 0 { return nil @@ -159,8 +173,8 @@ func (c *ChainConfig) GetActivePrecompileConfig(address common.Address, blockTim return configs[len(configs)-1] // return the most recent config } -// GetActivatingPrecompileConfigs returns all upgrades configured to activate during the state transition from a block with timestamp [from] -// to a block with timestamp [to]. +// GetActivatingPrecompileConfigs returns all precompile upgrades configured to activate during the +// state transition from a block with timestamp [from] to a block with timestamp [to]. func (c *ChainConfig) GetActivatingPrecompileConfigs(address common.Address, from *big.Int, to *big.Int, upgrades []PrecompileUpgrade) []precompileconfig.Config { // Get key from address. module, ok := modules.GetPrecompileModuleByAddress(address) @@ -188,6 +202,18 @@ func (c *ChainConfig) GetActivatingPrecompileConfigs(address common.Address, fro return configs } +// GetActivatingStateUpgrades returns all state upgrades configured to activate during the +// state transition from a block with timestamp [from] to a block with timestamp [to]. +func (c *ChainConfig) GetActivatingStateUpgrades(from *big.Int, to *big.Int, upgrades []StateUpgrade) []StateUpgrade { + activating := make([]StateUpgrade, 0) + for _, upgrade := range upgrades { + if utils.IsForkTransition(upgrade.blockTimestamp, from, to) { + activating = append(activating, upgrade) + } + } + return activating +} + // CheckPrecompilesCompatible checks if [precompileUpgrades] are compatible with [c] at [headTimestamp]. // Returns a ConfigCompatError if upgrades already activated at [headTimestamp] are missing from // [precompileUpgrades]. Upgrades not already activated may be modified or absent from [precompileUpgrades]. @@ -246,11 +272,49 @@ func (c *ChainConfig) checkPrecompileCompatible(address common.Address, precompi return nil } +// CheckStateUpgradesCompatible checks if [stateUpgrades] are compatible with [c] at [headTimestamp]. +func (c *ChainConfig) CheckStateUpgradesCompatible(stateUpgrades []StateUpgrade, lastTimestamp *big.Int) *ConfigCompatError { + // All active upgrades (from nil to [lastTimestamp]) must match. + activeUpgrades := c.GetActivatingStateUpgrades(nil, lastTimestamp, c.StateUpgrades) + newUpgrades := c.GetActivatingStateUpgrades(nil, lastTimestamp, stateUpgrades) + + // Check activated upgrades are still present. + for i, upgrade := range activeUpgrades { + if len(newUpgrades) <= i { + // missing upgrade + return newCompatError( + fmt.Sprintf("missing StateUpgrade[%d]", i), + upgrade.blockTimestamp, + nil, + ) + } + // All upgrades that have activated must be identical. + if !upgrade.Equal(&newUpgrades[i]) { + return newCompatError( + fmt.Sprintf("StateUpgrade[%d]", i), + upgrade.blockTimestamp, + newUpgrades[i].blockTimestamp, + ) + } + } + // then, make sure newUpgrades does not have additional upgrades + // that are already activated. (cannot perform retroactive upgrade) + if len(newUpgrades) > len(activeUpgrades) { + return newCompatError( + fmt.Sprintf("cannot retroactively enable StateUpgrade[%d]", len(activeUpgrades)), + nil, + newUpgrades[len(activeUpgrades)].blockTimestamp, // this indexes to the first element in newUpgrades after the end of activeUpgrades + ) + } + + return nil +} + // EnabledStatefulPrecompiles returns current stateful precompile configs that are enabled at [blockTimestamp]. func (c *ChainConfig) EnabledStatefulPrecompiles(blockTimestamp *big.Int) Precompiles { statefulPrecompileConfigs := make(Precompiles) for _, module := range modules.RegisteredModules() { - if config := c.GetActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() { + if config := c.getActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() { statefulPrecompileConfigs[module.ConfigKey] = config } } diff --git a/params/state_upgrade.go b/params/state_upgrade.go new file mode 100644 index 0000000000..0fdb792284 --- /dev/null +++ b/params/state_upgrade.go @@ -0,0 +1,44 @@ +// (c) 2023 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package params + +import ( + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +type StateUpgrade struct { + blockTimestamp *big.Int + + // Adds the specified amount to the balance of the specified address + AddToBalance map[common.Address]*math.HexOrDecimal256 `json:"addToBalance,omitempty"` + + // Sets the specified storage slots of the specified addresses + // to the given values. Note that the value of common.Hash{} will + // remove the storage key + SetStorage map[common.Address]map[common.Hash]common.Hash `json:"setStorage,omitempty"` + + // Sets the code of the specified contract to the given value + SetCode map[common.Address][]byte `json:"setCode,omitempty"` + + // Deploys contracts with the specified creation bytecode to the + // specified addresses, instead of the normal rules for deriving + // the address of a created contract. + DeployContractTo []ContractDeploy `json:"deployContractTo,omitempty"` +} + +type ContractDeploy struct { + DeployTo common.Address `json:"deployTo,omitempty"` // The address to deploy the contract to + Caller common.Address `json:"caller,omitempty"` // The address of the caller + Input []byte `json:"input,omitempty"` // The input bytecode to create the contract + Gas uint64 `json:"gas,omitempty"` // The gas to use when creating the contract + Value *big.Int `json:"value,omitempty"` // The value to send when creating the contract +} + +func (s *StateUpgrade) Equal(other *StateUpgrade) bool { + return reflect.DeepEqual(s, other) +} diff --git a/precompile/contract/interfaces.go b/precompile/contract/interfaces.go index a8402bed75..b9c431272d 100644 --- a/precompile/contract/interfaces.go +++ b/precompile/contract/interfaces.go @@ -34,14 +34,11 @@ type StateDB interface { GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) - SetCode(common.Address, []byte) - SetNonce(common.Address, uint64) GetNonce(common.Address) uint64 GetBalance(common.Address) *big.Int AddBalance(common.Address, *big.Int) - SubBalance(common.Address, *big.Int) CreateAccount(common.Address) Exist(common.Address) bool diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go new file mode 100644 index 0000000000..44d6b39418 --- /dev/null +++ b/stateupgrade/interfaces.go @@ -0,0 +1,35 @@ +// (c) 2023 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package stateupgrade + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// StateDB is the interface for accessing EVM state in state upgrades +type StateDB interface { + SetState(common.Address, common.Hash, common.Hash) + SetCode(common.Address, []byte) + AddBalance(common.Address, *big.Int) + + CreateAccount(common.Address) + Exist(common.Address) bool + + Snapshot() int + RevertToSnapshot(int) +} + +// BlockContext defines an interface that provides information about the +// block that activates the state upgrade. +type BlockContext interface { + Number() *big.Int + Timestamp() *big.Int +} + +// AccessibleState defines the interface exposed to state upgrades +type AccessibleState interface { + CreateAt(contractAddr common.Address, callerAddr common.Address, code []byte, gas uint64, value *big.Int) ([]byte, common.Address, uint64, error) +} diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go new file mode 100644 index 0000000000..c0d13eaffb --- /dev/null +++ b/stateupgrade/state_upgrade.go @@ -0,0 +1,80 @@ +// (c) 2023 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package stateupgrade + +import ( + "math/big" + + "github.com/ava-labs/subnet-evm/params" + "github.com/ethereum/go-ethereum/log" +) + +func Configure( + stateUpgrade *params.StateUpgrade, + blockContext BlockContext, + state StateDB, + accessibleState AccessibleState, +) error { + log.Info("Configuring state upgrade", "block", blockContext.Number(), "timestamp", blockContext.Timestamp()) + if err := addToBalance(stateUpgrade, state); err != nil { + return err + } + if err := setStorage(stateUpgrade, state); err != nil { + return err + } + if err := setCode(stateUpgrade, state); err != nil { + return err + } + if err := deployContractTo(stateUpgrade, state, accessibleState); err != nil { + return err + } + return nil +} + +// addToBalance modifies the balances according to the [AddToBalance] map in [stateUpgrade]. +func addToBalance(stateUpgrade *params.StateUpgrade, state StateDB) error { + for account, amount := range stateUpgrade.AddToBalance { + // TODO: is it necessary to call CreateAccount? + // if there is no address in the state, create one. + if !state.Exist(account) { + state.CreateAccount(account) + } + + bigIntAmount := (*big.Int)(amount) + state.AddBalance(account, bigIntAmount) + } + return nil +} + +// setStorage modifies the storage slots according to the [SetStorage] map in [stateUpgrade]. +func setStorage(stateUpgrade *params.StateUpgrade, state StateDB) error { + for account, storage := range stateUpgrade.SetStorage { + for key, value := range storage { + state.SetState(account, key, value) + } + } + return nil +} + +// setCode modifies the code according to the [SetCode] map in [stateUpgrade]. +func setCode(stateUpgrade *params.StateUpgrade, state StateDB) error { + for account, code := range stateUpgrade.SetCode { + state.SetCode(account, code) + } + return nil +} + +// deployContractTo deploys contracts according to the [DeployContractTo] list in [stateUpgrade]. +func deployContractTo(stateUpgrade *params.StateUpgrade, statedb StateDB, evm AccessibleState) error { + for _, contract := range stateUpgrade.DeployContractTo { + snapshot := statedb.Snapshot() + output, _, remainingGas, err := evm.CreateAt(contract.DeployTo, contract.Caller, contract.Input, contract.Gas, contract.Value) + log.Info("Deploying contract to address as state upgrade", "address", contract.DeployTo, "output", output, "remainingGas", remainingGas, "err", err) + if err != nil { + // if the creation fails, revert the state to the snapshot + statedb.RevertToSnapshot(snapshot) + } + } + return nil +} From f8edd2c94028ed0a5bfc6a9f0440587ab4d2deeb Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 05:37:24 -0800 Subject: [PATCH 02/30] remove contract deploy --- core/state_processor.go | 33 +++++++++++++++------------------ core/vm/evm.go | 8 -------- miner/worker.go | 5 +++-- stateupgrade/interfaces.go | 8 -------- stateupgrade/state_upgrade.go | 18 ------------------ 5 files changed, 18 insertions(+), 54 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 0a8854ebba..ba0671b95a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -71,26 +71,25 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // transactions failed to execute due to insufficient gas it will return an error. func (p *StateProcessor) Process(block *types.Block, parent *types.Header, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { var ( - receipts types.Receipts - usedGas = new(uint64) - header = block.Header() - blockHash = block.Hash() - blockNumber = block.Number() - allLogs []*types.Log - gp = new(GasPool).AddGas(block.GasLimit()) - timestamp = new(big.Int).SetUint64(header.Time) + receipts types.Receipts + usedGas = new(uint64) + header = block.Header() + blockHash = block.Hash() + blockNumber = block.Number() + allLogs []*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) + timestamp = new(big.Int).SetUint64(header.Time) + parentTimestamp = new(big.Int).SetUint64(parent.Time) ) // Configure any stateful precompiles that should go into effect during this block. - err := ApplyPrecompileActivations(p.config, new(big.Int).SetUint64(parent.Time), block, statedb) - if err != nil { - log.Error("failed to configure precompiles processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err) + if err := ApplyPrecompileActivations(p.config, parentTimestamp, block, statedb); err != nil { + log.Error("failed to configure precompiles processing block", "hash", blockHash, "number", blockNumber, "timestamp", block.Time(), "err", err) return nil, nil, 0, err } // Configure any state upgrades that should go into effect during this block. - err = ApplyStateUpgrades(p.config, new(big.Int).SetUint64(parent.Time), p.bc, header, statedb, cfg) - if err != nil { - log.Error("failed to configure state upgrades processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err) + if err := ApplyStateUpgrades(p.config, parentTimestamp, block, statedb); err != nil { + log.Error("failed to configure state upgrades processing block", "hash", blockHash, "number", blockNumber, "timestamp", block.Time(), "err", err) return nil, nil, 0, err } @@ -232,12 +231,10 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, // This function is called: // - during block processing to update the state before processing the given block. // - during block producing to apply the precompile upgrades before producing the block. -func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, bc ChainContext, header *types.Header, statedb *state.StateDB, cfg vm.Config) error { - blockContext := NewEVMBlockContext(header, bc, nil) +func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, c, cfg) - if err := stateupgrade.Configure(&upgrade, &blockContext, statedb, vmenv); err != nil { + if err := stateupgrade.Configure(&upgrade, blockContext, statedb); err != nil { return fmt.Errorf("could not configure state upgrade, reason: %w", err) } } diff --git a/core/vm/evm.go b/core/vm/evm.go index be36b07edd..764bce4e65 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -604,13 +604,5 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } -// CreateAt creates a new contract using code as deployment code, -// and deploys it to the specified address. For use by the state upgrade. -func (evm *EVM) CreateAt(contractAddr common.Address, callerAddr common.Address, code []byte, gas uint64, value *big.Int) ([]byte, common.Address, uint64, error) { - codeAndHash := &codeAndHash{code: code} - caller := AccountRef(callerAddr) - return evm.create(caller, codeAndHash, gas, value, contractAddr, CREATE) -} - // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } diff --git a/miner/worker.go b/miner/worker.go index 5c96346d10..c12267365e 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -186,13 +186,14 @@ func (w *worker) commitNewWork() (*types.Block, error) { return nil, fmt.Errorf("failed to create new current environment: %w", err) } // Configure any stateful precompiles that should go into effect during this block. - err = core.ApplyPrecompileActivations(w.chainConfig, new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), env.state) + blockContext := types.NewBlockWithHeader(header) + err = core.ApplyPrecompileActivations(w.chainConfig, new(big.Int).SetUint64(parent.Time()), blockContext, env.state) if err != nil { log.Error("failed to configure precompiles mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err) return nil, err } // Configure any state upgrades that should go into effect during this block. - err = core.ApplyStateUpgrades(w.chainConfig, new(big.Int).SetUint64(parent.Time()), w.chain, header, env.state, *w.chain.GetVMConfig()) + err = core.ApplyStateUpgrades(w.chainConfig, new(big.Int).SetUint64(parent.Time()), blockContext, env.state) if err != nil { log.Error("failed to configure state upgrades mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err) return nil, err diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go index 44d6b39418..2910308afb 100644 --- a/stateupgrade/interfaces.go +++ b/stateupgrade/interfaces.go @@ -17,9 +17,6 @@ type StateDB interface { CreateAccount(common.Address) Exist(common.Address) bool - - Snapshot() int - RevertToSnapshot(int) } // BlockContext defines an interface that provides information about the @@ -28,8 +25,3 @@ type BlockContext interface { Number() *big.Int Timestamp() *big.Int } - -// AccessibleState defines the interface exposed to state upgrades -type AccessibleState interface { - CreateAt(contractAddr common.Address, callerAddr common.Address, code []byte, gas uint64, value *big.Int) ([]byte, common.Address, uint64, error) -} diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index c0d13eaffb..f80c756227 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -14,7 +14,6 @@ func Configure( stateUpgrade *params.StateUpgrade, blockContext BlockContext, state StateDB, - accessibleState AccessibleState, ) error { log.Info("Configuring state upgrade", "block", blockContext.Number(), "timestamp", blockContext.Timestamp()) if err := addToBalance(stateUpgrade, state); err != nil { @@ -26,9 +25,6 @@ func Configure( if err := setCode(stateUpgrade, state); err != nil { return err } - if err := deployContractTo(stateUpgrade, state, accessibleState); err != nil { - return err - } return nil } @@ -64,17 +60,3 @@ func setCode(stateUpgrade *params.StateUpgrade, state StateDB) error { } return nil } - -// deployContractTo deploys contracts according to the [DeployContractTo] list in [stateUpgrade]. -func deployContractTo(stateUpgrade *params.StateUpgrade, statedb StateDB, evm AccessibleState) error { - for _, contract := range stateUpgrade.DeployContractTo { - snapshot := statedb.Snapshot() - output, _, remainingGas, err := evm.CreateAt(contract.DeployTo, contract.Caller, contract.Input, contract.Gas, contract.Value) - log.Info("Deploying contract to address as state upgrade", "address", contract.DeployTo, "output", output, "remainingGas", remainingGas, "err", err) - if err != nil { - // if the creation fails, revert the state to the snapshot - statedb.RevertToSnapshot(snapshot) - } - } - return nil -} From 0c1eeddaf828d186081384c1b35bfa30c6c407f3 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 05:59:40 -0800 Subject: [PATCH 03/30] use consolidated struct --- core/state_processor.go | 2 +- params/state_upgrade.go | 32 ++++++------------ stateupgrade/interfaces.go | 3 ++ stateupgrade/state_upgrade.go | 64 +++++++++++++---------------------- 4 files changed, 38 insertions(+), 63 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index ba0671b95a..8af2e33f2a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -230,7 +230,7 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, // to apply the necessary state transitions for the upgrade. // This function is called: // - during block processing to update the state before processing the given block. -// - during block producing to apply the precompile upgrades before producing the block. +// - during block producing to apply the state upgrades before producing the block. func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { diff --git a/params/state_upgrade.go b/params/state_upgrade.go index 0fdb792284..5e44d770cb 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -8,35 +8,23 @@ import ( "reflect" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" ) +// StateUpgrade describes the modifications to be made to the state during +// a state upgrade. type StateUpgrade struct { blockTimestamp *big.Int - // Adds the specified amount to the balance of the specified address - AddToBalance map[common.Address]*math.HexOrDecimal256 `json:"addToBalance,omitempty"` - - // Sets the specified storage slots of the specified addresses - // to the given values. Note that the value of common.Hash{} will - // remove the storage key - SetStorage map[common.Address]map[common.Hash]common.Hash `json:"setStorage,omitempty"` - - // Sets the code of the specified contract to the given value - SetCode map[common.Address][]byte `json:"setCode,omitempty"` - - // Deploys contracts with the specified creation bytecode to the - // specified addresses, instead of the normal rules for deriving - // the address of a created contract. - DeployContractTo []ContractDeploy `json:"deployContractTo,omitempty"` + // map from account address to the modification to be made to the account. + ModifiedAccounts map[common.Address]StateUpgradeAccount `json:"modifiedAccounts"` } -type ContractDeploy struct { - DeployTo common.Address `json:"deployTo,omitempty"` // The address to deploy the contract to - Caller common.Address `json:"caller,omitempty"` // The address of the caller - Input []byte `json:"input,omitempty"` // The input bytecode to create the contract - Gas uint64 `json:"gas,omitempty"` // The gas to use when creating the contract - Value *big.Int `json:"value,omitempty"` // The value to send when creating the contract +// StateUpgradeAccount describes the modifications to be made to an account during +// a state upgrade. +type StateUpgradeAccount struct { + Code []byte `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + BalanceChange *big.Int `json:"balanceChange,omitempty"` } func (s *StateUpgrade) Equal(other *StateUpgrade) bool { diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go index 2910308afb..aeba0c28af 100644 --- a/stateupgrade/interfaces.go +++ b/stateupgrade/interfaces.go @@ -15,6 +15,9 @@ type StateDB interface { SetCode(common.Address, []byte) AddBalance(common.Address, *big.Int) + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) + CreateAccount(common.Address) Exist(common.Address) bool } diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index f80c756227..2fdc83ebc0 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -4,59 +4,43 @@ package stateupgrade import ( - "math/big" - "github.com/ava-labs/subnet-evm/params" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) -func Configure( - stateUpgrade *params.StateUpgrade, - blockContext BlockContext, - state StateDB, -) error { +// Configure applies the state upgrade to the state. +func Configure(stateUpgrade *params.StateUpgrade, blockContext BlockContext, state StateDB) error { log.Info("Configuring state upgrade", "block", blockContext.Number(), "timestamp", blockContext.Timestamp()) - if err := addToBalance(stateUpgrade, state); err != nil { - return err - } - if err := setStorage(stateUpgrade, state); err != nil { - return err - } - if err := setCode(stateUpgrade, state); err != nil { - return err + for account, upgrade := range stateUpgrade.ModifiedAccounts { + if err := upgradeAccount(account, upgrade, state); err != nil { + return err + } } return nil } -// addToBalance modifies the balances according to the [AddToBalance] map in [stateUpgrade]. -func addToBalance(stateUpgrade *params.StateUpgrade, state StateDB) error { - for account, amount := range stateUpgrade.AddToBalance { - // TODO: is it necessary to call CreateAccount? - // if there is no address in the state, create one. - if !state.Exist(account) { - state.CreateAccount(account) - } - - bigIntAmount := (*big.Int)(amount) - state.AddBalance(account, bigIntAmount) +// upgradeAccount applies the state upgrade to the given account. +func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount, state StateDB) error { + // TODO: is this necessary? + if !state.Exist(account) { + // Create the account if it does not exist. + state.CreateAccount(account) } - return nil -} -// setStorage modifies the storage slots according to the [SetStorage] map in [stateUpgrade]. -func setStorage(stateUpgrade *params.StateUpgrade, state StateDB) error { - for account, storage := range stateUpgrade.SetStorage { - for key, value := range storage { - state.SetState(account, key, value) + if upgrade.BalanceChange != nil { + state.AddBalance(account, upgrade.BalanceChange) + } + if upgrade.Code != nil { + if state.GetNonce(account) == 0 { + // If the nonce is 0, we will set it to a non-zero value + // so the account is not considered empty. + state.SetNonce(account, 1) } + state.SetCode(account, upgrade.Code) } - return nil -} - -// setCode modifies the code according to the [SetCode] map in [stateUpgrade]. -func setCode(stateUpgrade *params.StateUpgrade, state StateDB) error { - for account, code := range stateUpgrade.SetCode { - state.SetCode(account, code) + for key, value := range upgrade.Storage { + state.SetState(account, key, value) } return nil } From 6afe5fcb20c46974b4d579aa658c7578949dc5fa Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 07:02:51 -0800 Subject: [PATCH 04/30] add support for genesis and test --- core/genesis.go | 19 +++-- core/state_processor.go | 5 +- params/precompile_upgrade.go | 12 ++-- params/state_upgrade.go | 4 +- plugin/evm/vm_test.go | 63 ++++++++--------- plugin/evm/vm_upgrade_bytes_test.go | 103 ++++++++++++++++++++++++++-- stateupgrade/state_upgrade.go | 2 +- 7 files changed, 153 insertions(+), 55 deletions(-) diff --git a/core/genesis.go b/core/genesis.go index 07a0ac1bc0..50dd55483f 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -306,12 +306,6 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { Coinbase: g.Coinbase, } - // Configure any stateful precompiles that should be enabled in the genesis. - err = ApplyPrecompileActivations(g.Config, nil, types.NewBlockWithHeader(head), statedb) - if err != nil { - panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err)) - } - // Do custom allocation after airdrop in case an address shows up in standard // allocation for addr, account := range g.Alloc { @@ -322,6 +316,19 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { statedb.SetState(addr, key, value) } } + + // Configure any stateful precompiles that should activate in the genesis. + blockContext := types.NewBlockWithHeader(head) + err = ApplyPrecompileActivations(g.Config, nil, blockContext, statedb) + if err != nil { + panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err)) + } + // Configure any state upgrades that should activate in the genesis. + err = ApplyStateUpgrades(g.Config, nil, blockContext, statedb) + if err != nil { + panic(fmt.Sprintf("unable to configure state upgrades in genesis block: %v", err)) + } + root := statedb.IntermediateRoot(false) head.Root = root diff --git a/core/state_processor.go b/core/state_processor.go index 8af2e33f2a..83151e4990 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -182,7 +182,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // to apply the necessary state transitions for the upgrade. // This function is called: // - within genesis setup to configure the starting state for precompiles enabled at genesis, -// - during block processing to update the state before processing the given block. +// - during block processing to update the state before processing the given block, // - during block producing to apply the precompile upgrades before producing the block. func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { blockTimestamp := blockContext.Timestamp() @@ -229,7 +229,8 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, // transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure] // to apply the necessary state transitions for the upgrade. // This function is called: -// - during block processing to update the state before processing the given block. +// - within genesis setup to apply state upgrades if they are specified at genesis, +// - during block processing to update the state before processing the given block, // - during block producing to apply the state upgrades before producing the block. func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { // Apply state upgrades diff --git a/params/precompile_upgrade.go b/params/precompile_upgrade.go index 80bbc49d28..94fd8e2c99 100644 --- a/params/precompile_upgrade.go +++ b/params/precompile_upgrade.go @@ -153,7 +153,7 @@ func (c *ChainConfig) verifyPrecompileUpgrades() error { func (c *ChainConfig) verifyStateUpgrades() error { var previousUpgradeTimestamp *big.Int for i, upgrade := range c.StateUpgrades { - upgradeTimestamp := upgrade.blockTimestamp + upgradeTimestamp := upgrade.BlockTimestamp // Verify specified timestamps are strictly monotonically increasing. if previousUpgradeTimestamp != nil && upgradeTimestamp.Cmp(previousUpgradeTimestamp) <= 0 { return fmt.Errorf("StateUpgrade[%d]: config block timestamp (%v) <= previous timestamp (%v)", i, upgradeTimestamp, previousUpgradeTimestamp) @@ -207,7 +207,7 @@ func (c *ChainConfig) GetActivatingPrecompileConfigs(address common.Address, fro func (c *ChainConfig) GetActivatingStateUpgrades(from *big.Int, to *big.Int, upgrades []StateUpgrade) []StateUpgrade { activating := make([]StateUpgrade, 0) for _, upgrade := range upgrades { - if utils.IsForkTransition(upgrade.blockTimestamp, from, to) { + if utils.IsForkTransition(upgrade.BlockTimestamp, from, to) { activating = append(activating, upgrade) } } @@ -284,7 +284,7 @@ func (c *ChainConfig) CheckStateUpgradesCompatible(stateUpgrades []StateUpgrade, // missing upgrade return newCompatError( fmt.Sprintf("missing StateUpgrade[%d]", i), - upgrade.blockTimestamp, + upgrade.BlockTimestamp, nil, ) } @@ -292,8 +292,8 @@ func (c *ChainConfig) CheckStateUpgradesCompatible(stateUpgrades []StateUpgrade, if !upgrade.Equal(&newUpgrades[i]) { return newCompatError( fmt.Sprintf("StateUpgrade[%d]", i), - upgrade.blockTimestamp, - newUpgrades[i].blockTimestamp, + upgrade.BlockTimestamp, + newUpgrades[i].BlockTimestamp, ) } } @@ -303,7 +303,7 @@ func (c *ChainConfig) CheckStateUpgradesCompatible(stateUpgrades []StateUpgrade, return newCompatError( fmt.Sprintf("cannot retroactively enable StateUpgrade[%d]", len(activeUpgrades)), nil, - newUpgrades[len(activeUpgrades)].blockTimestamp, // this indexes to the first element in newUpgrades after the end of activeUpgrades + newUpgrades[len(activeUpgrades)].BlockTimestamp, // this indexes to the first element in newUpgrades after the end of activeUpgrades ) } diff --git a/params/state_upgrade.go b/params/state_upgrade.go index 5e44d770cb..c078a8fe2d 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -13,10 +13,10 @@ import ( // StateUpgrade describes the modifications to be made to the state during // a state upgrade. type StateUpgrade struct { - blockTimestamp *big.Int + BlockTimestamp *big.Int `json:"blockTimestamp,omitempty"` // map from account address to the modification to be made to the account. - ModifiedAccounts map[common.Address]StateUpgradeAccount `json:"modifiedAccounts"` + Accounts map[common.Address]StateUpgradeAccount `json:"accounts"` } // StateUpgradeAccount describes the modifications to be made to an account during diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 8acca71c79..8bb310d9e4 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -17,17 +17,6 @@ import ( "testing" "time" - "github.com/ava-labs/subnet-evm/commontype" - "github.com/ava-labs/subnet-evm/internal/ethapi" - "github.com/ava-labs/subnet-evm/metrics" - "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ava-labs/subnet-evm/precompile/allowlist" - "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" - "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" - "github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager" - "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" - "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -41,6 +30,7 @@ import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/consensus/snowman" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/validators" avalancheConstants "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/formatting" @@ -48,18 +38,26 @@ import ( "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/chain" - engCommon "github.com/ava-labs/avalanchego/snow/engine/common" - + "github.com/ava-labs/subnet-evm/accounts/abi" + accountKeystore "github.com/ava-labs/subnet-evm/accounts/keystore" + "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/eth" + "github.com/ava-labs/subnet-evm/internal/ethapi" + "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/plugin/evm/message" + "github.com/ava-labs/subnet-evm/precompile/allowlist" + "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" + "github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager" + "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/rpc" - - "github.com/ava-labs/subnet-evm/accounts/abi" - accountKeystore "github.com/ava-labs/subnet-evm/accounts/keystore" + "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/vmerrs" ) var ( @@ -162,7 +160,7 @@ func setupGenesis(t *testing.T, ) (*snow.Context, manager.Manager, []byte, - chan engCommon.Message, + chan commonEng.Message, ) { if len(genesisJSON) == 0 { genesisJSON = genesisJSONLatest @@ -190,7 +188,7 @@ func setupGenesis(t *testing.T, } ctx.Keystore = userKeystore.NewBlockchainKeyStore(ctx.ChainID) - issuer := make(chan engCommon.Message, 1) + issuer := make(chan commonEng.Message, 1) prefixedDBManager := baseDBManager.NewPrefixDBManager([]byte{1}) return ctx, prefixedDBManager, genesisBytes, issuer } @@ -204,16 +202,16 @@ func GenesisVM(t *testing.T, genesisJSON string, configJSON string, upgradeJSON string, -) (chan engCommon.Message, +) (chan commonEng.Message, *VM, manager.Manager, - *engCommon.SenderTest, + *commonEng.SenderTest, ) { vm := &VM{} ctx, dbManager, genesisBytes, issuer := setupGenesis(t, genesisJSON) - appSender := &engCommon.SenderTest{T: t} + appSender := &commonEng.SenderTest{T: t} appSender.CantSendAppGossip = true appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } - if err := vm.Initialize( + err := vm.Initialize( context.Background(), ctx, dbManager, @@ -221,11 +219,10 @@ func GenesisVM(t *testing.T, []byte(upgradeJSON), []byte(configJSON), issuer, - []*engCommon.Fx{}, + []*commonEng.Fx{}, appSender, - ); err != nil { - t.Fatal(err) - } + ) + require.NoError(t, err, "error initializing GenesisVM") if finishBootstrapping { require.NoError(t, vm.SetState(context.Background(), snow.Bootstrapping)) @@ -357,7 +354,7 @@ func TestVMUpgrades(t *testing.T) { } } -func issueAndAccept(t *testing.T, issuer <-chan engCommon.Message, vm *VM) snowman.Block { +func issueAndAccept(t *testing.T, issuer <-chan commonEng.Message, vm *VM) snowman.Block { t.Helper() <-issuer @@ -428,7 +425,7 @@ func TestSubnetEVMUpgradeRequiredAtGenesis(t *testing.T) { []byte(""), []byte(test.configJSON), issuer, - []*engCommon.Fx{}, + []*commonEng.Fx{}, nil, ) @@ -546,7 +543,7 @@ func TestBuildEthTxBlock(t *testing.T) { []byte(""), []byte("{\"pruning-enabled\":true}"), issuer, - []*engCommon.Fx{}, + []*commonEng.Fx{}, nil, ); err != nil { t.Fatal(err) @@ -2037,7 +2034,7 @@ func TestConfigureLogLevel(t *testing.T) { t.Run(test.name, func(t *testing.T) { vm := &VM{} ctx, dbManager, genesisBytes, issuer := setupGenesis(t, test.genesisJSON) - appSender := &engCommon.SenderTest{T: t} + appSender := &commonEng.SenderTest{T: t} appSender.CantSendAppGossip = true appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } err := vm.Initialize( @@ -2048,7 +2045,7 @@ func TestConfigureLogLevel(t *testing.T) { []byte(""), []byte(test.logConfig), issuer, - []*engCommon.Fx{}, + []*commonEng.Fx{}, appSender, ) if len(test.expectedErr) == 0 && err != nil { @@ -2998,12 +2995,12 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) { require.NoError(t, err) // this will not be allowed - err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, []byte{}, issuer, []*engCommon.Fx{}, appSender) + err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, []byte{}, issuer, []*commonEng.Fx{}, appSender) require.ErrorContains(t, err, "mismatching SubnetEVM fork block timestamp in database") // try again with skip-upgrade-check config := []byte("{\"skip-upgrade-check\": true}") - err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, config, issuer, []*engCommon.Fx{}, appSender) + err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, config, issuer, []*commonEng.Fx{}, appSender) require.NoError(t, err) require.NoError(t, reinitVM.Shutdown(context.Background())) } diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 4880f0e348..cbf0a6ae72 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -7,12 +7,13 @@ import ( "context" "encoding/json" "errors" + "fmt" "math/big" "testing" "time" "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/avalanchego/snow/engine/common" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/vms/components/chain" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" @@ -20,7 +21,9 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/vmerrs" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestVMUpgradeBytesPrecompile(t *testing.T) { @@ -83,7 +86,7 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { // restart the vm ctx := NewContext() if err := vm.Initialize( - context.Background(), ctx, dbManager, []byte(genesisJSONSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*common.Fx{}, appSender, + context.Background(), ctx, dbManager, []byte(genesisJSONSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*commonEng.Fx{}, appSender, ); err != nil { t.Fatal(err) } @@ -192,7 +195,7 @@ func TestVMUpgradeBytesNetworkUpgrades(t *testing.T) { } // VM should not start again without proper upgrade bytes. - err = vm.Initialize(context.Background(), vm.ctx, dbManager, []byte(genesisJSONPreSubnetEVM), []byte{}, []byte{}, issuer, []*common.Fx{}, appSender) + err = vm.Initialize(context.Background(), vm.ctx, dbManager, []byte(genesisJSONPreSubnetEVM), []byte{}, []byte{}, issuer, []*commonEng.Fx{}, appSender) assert.ErrorContains(t, err, "mismatching SubnetEVM fork block timestamp in database") // VM should not start if fork is moved back @@ -201,7 +204,7 @@ func TestVMUpgradeBytesNetworkUpgrades(t *testing.T) { if err != nil { t.Fatalf("could not marshal upgradeConfig to json: %s", err) } - err = vm.Initialize(context.Background(), vm.ctx, dbManager, []byte(genesisJSONPreSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*common.Fx{}, appSender) + err = vm.Initialize(context.Background(), vm.ctx, dbManager, []byte(genesisJSONPreSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*commonEng.Fx{}, appSender) assert.ErrorContains(t, err, "mismatching SubnetEVM fork block timestamp in database") // VM should not start if fork is moved forward @@ -210,7 +213,7 @@ func TestVMUpgradeBytesNetworkUpgrades(t *testing.T) { if err != nil { t.Fatalf("could not marshal upgradeConfig to json: %s", err) } - err = vm.Initialize(context.Background(), vm.ctx, dbManager, []byte(genesisJSONPreSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*common.Fx{}, appSender) + err = vm.Initialize(context.Background(), vm.ctx, dbManager, []byte(genesisJSONPreSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*commonEng.Fx{}, appSender) assert.ErrorContains(t, err, "mismatching SubnetEVM fork block timestamp in database") } @@ -270,3 +273,93 @@ func TestVMUpgradeBytesNetworkUpgradesWithGenesis(t *testing.T) { t.Fatal(err) } } + +func mustMarshal(t *testing.T, v interface{}) string { + b, err := json.Marshal(v) + require.NoError(t, err) + return string(b) +} + +func TestVMStateUpgrade(t *testing.T) { + // modify genesis to add a key to the state + genesis := &core.Genesis{} + err := json.Unmarshal([]byte(genesisJSONSubnetEVM), genesis) + require.NoError(t, err) + storageKey := common.HexToHash("0x1234") + genesisAccount, ok := genesis.Alloc[testEthAddrs[0]] + require.True(t, ok) + genesisAccount.Storage = map[common.Hash]common.Hash{storageKey: common.HexToHash("0x5555")} + genesisStr := mustMarshal(t, genesis) + + // This modification will be applied to an existing account + genesisAccountUpgrade := ¶ms.StateUpgradeAccount{ + BalanceChange: big.NewInt(-100), + Storage: map[common.Hash]common.Hash{storageKey: {}}, + } + + // This modification will be applied to a new account + upgradeTimestamp := time.Unix(10, 0) + newAccount := common.Address{42} + code := []byte{0x01, 0x02, 0x03} + newAccountUpgrade := ¶ms.StateUpgradeAccount{ + BalanceChange: big.NewInt(100), + Storage: map[common.Hash]common.Hash{storageKey: common.HexToHash("0x6666")}, + Code: code, + } + + upgradeBytesJSON := fmt.Sprintf( + `{ + "stateUpgrades": [ + { + "blockTimestamp": %d, + "accounts": { + "%s": %s, + "%s": %s + } + } + ] + }`, + upgradeTimestamp.Unix(), + testEthAddrs[0].Hex(), + mustMarshal(t, genesisAccountUpgrade), + newAccount.Hex(), + mustMarshal(t, newAccountUpgrade), + ) + fmt.Println(upgradeBytesJSON) + + // initialize the VM with these upgrade bytes + issuer, vm, _, _ := GenesisVM(t, true, genesisStr, "", upgradeBytesJSON) + defer func() { require.NoError(t, vm.Shutdown(context.Background())) }() + + // Advance the chain to the upgrade time + vm.clock.Set(upgradeTimestamp) + + // Submit a successful (unrelated) transaction, so we can build a block + // in this tx, testEthAddrs[1] sends 1 wei to itself. + tx0 := types.NewTransaction(uint64(0), testEthAddrs[1], big.NewInt(1), 21000, big.NewInt(testMinGasPrice), nil) + signedTx0, err := types.SignTx(tx0, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) + require.NoError(t, err) + + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0}) + require.NoError(t, errs[0], "Failed to add tx") + + blk := issueAndAccept(t, issuer, vm) + require.NotNil(t, blk) + require.EqualValues(t, 1, blk.Height()) + + // Verify the state upgrade was applied + state, err := vm.blockChain.State() + require.NoError(t, err) + + // Existing account + expectedGenesisBalance := genesisAccount.Balance.Add(genesisAccount.Balance, genesisAccountUpgrade.BalanceChange) + require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisBalance) + require.Equal(t, state.GetState(testEthAddrs[0], storageKey), genesisAccountUpgrade.Storage[storageKey]) + + // New account + expectedNewAccountBalance := newAccountUpgrade.BalanceChange + require.Equal(t, state.GetBalance(newAccount), expectedNewAccountBalance) + require.Equal(t, state.GetCode(newAccount), code) + require.Equal(t, state.GetNonce(newAccount), uint64(1)) // Nonce should be set to 1 when code is set if nonce was 0 + require.Equal(t, state.GetState(newAccount, storageKey), newAccountUpgrade.Storage[storageKey]) +} diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index 2fdc83ebc0..2dbbf88095 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -12,7 +12,7 @@ import ( // Configure applies the state upgrade to the state. func Configure(stateUpgrade *params.StateUpgrade, blockContext BlockContext, state StateDB) error { log.Info("Configuring state upgrade", "block", blockContext.Number(), "timestamp", blockContext.Timestamp()) - for account, upgrade := range stateUpgrade.ModifiedAccounts { + for account, upgrade := range stateUpgrade.Accounts { if err := upgradeAccount(account, upgrade, state); err != nil { return err } From 2a59979aa25438f2dea0d9d0146f8fff09b109cb Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 07:10:34 -0800 Subject: [PATCH 05/30] test improvements --- plugin/evm/vm_upgrade_bytes_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index cbf0a6ae72..c00e994d49 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -285,9 +285,9 @@ func TestVMStateUpgrade(t *testing.T) { genesis := &core.Genesis{} err := json.Unmarshal([]byte(genesisJSONSubnetEVM), genesis) require.NoError(t, err) - storageKey := common.HexToHash("0x1234") genesisAccount, ok := genesis.Alloc[testEthAddrs[0]] require.True(t, ok) + storageKey := common.HexToHash("0x1234") genesisAccount.Storage = map[common.Hash]common.Hash{storageKey: common.HexToHash("0x5555")} genesisStr := mustMarshal(t, genesis) @@ -325,12 +325,16 @@ func TestVMStateUpgrade(t *testing.T) { newAccount.Hex(), mustMarshal(t, newAccountUpgrade), ) - fmt.Println(upgradeBytesJSON) // initialize the VM with these upgrade bytes issuer, vm, _, _ := GenesisVM(t, true, genesisStr, "", upgradeBytesJSON) defer func() { require.NoError(t, vm.Shutdown(context.Background())) }() + // Verify the new account doesn't exist yet + genesisState, err := vm.blockChain.State() + require.NoError(t, err) + require.Equal(t, common.Big0, genesisState.GetBalance(newAccount)) + // Advance the chain to the upgrade time vm.clock.Set(upgradeTimestamp) From 5c617bc2d322d2bd5c6088d91040662bdd6a7912 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 07:48:39 -0800 Subject: [PATCH 06/30] add configuration tests --- params/precompile_config_test.go | 2 +- params/precompile_upgrade_test.go | 59 ++++++------ params/stateupgrade_config_test.go | 136 ++++++++++++++++++++++++++++ plugin/evm/vm_upgrade_bytes_test.go | 7 +- 4 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 params/stateupgrade_config_test.go diff --git a/params/precompile_config_test.go b/params/precompile_config_test.go index 488204631b..82054ec024 100644 --- a/params/precompile_config_test.go +++ b/params/precompile_config_test.go @@ -179,7 +179,7 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { Config: txallowlist.NewDisableConfig(big.NewInt(1)), }, }, - expectedError: " config block timestamp (1) <= previous timestamp (1) of same key", + expectedError: "config block timestamp (1) <= previous timestamp (1) of same key", }, } for _, tt := range tests { diff --git a/params/precompile_upgrade_test.go b/params/precompile_upgrade_test.go index 54078aad3b..a653531026 100644 --- a/params/precompile_upgrade_test.go +++ b/params/precompile_upgrade_test.go @@ -78,13 +78,7 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { deployerallowlist.ConfigKey: deployerallowlist.NewConfig(big.NewInt(10), admins, nil), } - type test struct { - configs []*UpgradeConfig - startTimestamps []*big.Int - expectedErrorString string - } - - tests := map[string]test{ + tests := map[string]upgradeCompatibilityTest{ "disable and re-enable": { startTimestamps: []*big.Int{big.NewInt(5)}, configs: []*UpgradeConfig{ @@ -252,32 +246,39 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { - // make a local copy of the chainConfig - chainConfig := chainConfig + tt.run(t, chainConfig) + }) + } +} - // apply all the upgrade bytes specified in order - for i, upgrade := range tt.configs { - newCfg := chainConfig - newCfg.UpgradeConfig = *upgrade +type upgradeCompatibilityTest struct { + configs []*UpgradeConfig + startTimestamps []*big.Int + expectedErrorString string +} - err := chainConfig.checkCompatible(&newCfg, nil, tt.startTimestamps[i]) +func (tt *upgradeCompatibilityTest) run(t *testing.T, chainConfig ChainConfig) { + // apply all the upgrade bytes specified in order + for i, upgrade := range tt.configs { + newCfg := chainConfig + newCfg.UpgradeConfig = *upgrade - // if this is not the final upgradeBytes, continue applying - // the next upgradeBytes. (only check the result on the last apply) - if i != len(tt.configs)-1 { - if err != nil { - t.Fatalf("expecting ApplyUpgradeBytes call %d to return nil, got %s", i+1, err) - } - chainConfig = newCfg - continue - } + err := chainConfig.checkCompatible(&newCfg, nil, tt.startTimestamps[i]) - if tt.expectedErrorString != "" { - require.ErrorContains(t, err, tt.expectedErrorString) - } else { - require.Nil(t, err) - } + // if this is not the final upgradeBytes, continue applying + // the next upgradeBytes. (only check the result on the last apply) + if i != len(tt.configs)-1 { + if err != nil { + t.Fatalf("expecting ApplyUpgradeBytes call %d to return nil, got %s", i+1, err) } - }) + chainConfig = newCfg + continue + } + + if tt.expectedErrorString != "" { + require.ErrorContains(t, err, tt.expectedErrorString) + } else { + require.Nil(t, err) + } } } diff --git a/params/stateupgrade_config_test.go b/params/stateupgrade_config_test.go new file mode 100644 index 0000000000..c57543198d --- /dev/null +++ b/params/stateupgrade_config_test.go @@ -0,0 +1,136 @@ +// (c) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package params + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestVerifyStateUpgrades(t *testing.T) { + modifiedAccounts := map[common.Address]StateUpgradeAccount{ + {1}: { + BalanceChange: common.Big1, + }, + } + tests := []struct { + name string + upgrades []StateUpgrade + expectedError string + }{ + { + name: "valid upgrade", + upgrades: []StateUpgrade{ + {BlockTimestamp: common.Big1, Accounts: modifiedAccounts}, + {BlockTimestamp: common.Big2, Accounts: modifiedAccounts}, + }, + }, + { + name: "upgrade block timestamp is not strictly increasing", + upgrades: []StateUpgrade{ + {BlockTimestamp: common.Big1, Accounts: modifiedAccounts}, + {BlockTimestamp: common.Big1, Accounts: modifiedAccounts}, + }, + expectedError: "config block timestamp (1) <= previous timestamp (1)", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + baseConfig := *SubnetEVMDefaultChainConfig + config := &baseConfig + config.StateUpgrades = tt.upgrades + + err := config.Verify() + if tt.expectedError == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tt.expectedError) + } + }) + } + +} + +func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { + chainConfig := *TestChainConfig + stateUpgrade := map[common.Address]StateUpgradeAccount{ + {1}: {BalanceChange: common.Big1}, + } + differentStateUpgrade := map[common.Address]StateUpgradeAccount{ + {2}: {BalanceChange: common.Big1}, + } + + tests := map[string]upgradeCompatibilityTest{ + "reschedule upgrade before it happens": { + startTimestamps: []*big.Int{big.NewInt(5), big.NewInt(6)}, + configs: []*UpgradeConfig{ + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + }, + }, + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + }, + }, + }, + }, + "modify upgrade after it happens not allowed": { + expectedErrorString: "mismatching StateUpgrade", + startTimestamps: []*big.Int{big.NewInt(5), big.NewInt(8)}, + configs: []*UpgradeConfig{ + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(7), Accounts: stateUpgrade}, + }, + }, + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(7), Accounts: differentStateUpgrade}, + }, + }, + }, + }, + "cancel upgrade before it happens": { + startTimestamps: []*big.Int{big.NewInt(5), big.NewInt(6)}, + configs: []*UpgradeConfig{ + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(7), Accounts: stateUpgrade}, + }, + }, + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + }, + }, + }, + }, + "retroactively enabling upgrades is not allowed": { + expectedErrorString: "cannot retroactively enable StateUpgrade", + startTimestamps: []*big.Int{big.NewInt(6)}, + configs: []*UpgradeConfig{ + { + StateUpgrades: []StateUpgrade{ + {BlockTimestamp: big.NewInt(5), Accounts: stateUpgrade}, + }, + }, + }, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + tt.run(t, chainConfig) + }) + } +} diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index c00e994d49..87b0bb7c7f 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -356,8 +356,11 @@ func TestVMStateUpgrade(t *testing.T) { require.NoError(t, err) // Existing account - expectedGenesisBalance := genesisAccount.Balance.Add(genesisAccount.Balance, genesisAccountUpgrade.BalanceChange) - require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisBalance) + expectedGenesisAccountBalance := new(big.Int).Add( + genesisAccount.Balance, + genesisAccountUpgrade.BalanceChange, + ) + require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisAccountBalance) require.Equal(t, state.GetState(testEthAddrs[0], storageKey), genesisAccountUpgrade.Storage[storageKey]) // New account From d02102f2e45d2f77c0a386adb391a030608df5b8 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 07:52:05 -0800 Subject: [PATCH 07/30] nit --- plugin/evm/vm_upgrade_bytes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 87b0bb7c7f..5e60cbb510 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -298,7 +298,6 @@ func TestVMStateUpgrade(t *testing.T) { } // This modification will be applied to a new account - upgradeTimestamp := time.Unix(10, 0) newAccount := common.Address{42} code := []byte{0x01, 0x02, 0x03} newAccountUpgrade := ¶ms.StateUpgradeAccount{ @@ -307,6 +306,7 @@ func TestVMStateUpgrade(t *testing.T) { Code: code, } + upgradeTimestamp := time.Unix(10, 0) // arbitrary timestamp to perform the network upgrade upgradeBytesJSON := fmt.Sprintf( `{ "stateUpgrades": [ From a97ee33a4b736e015a0b3408d6f0fb7d7e378053 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 07:55:03 -0800 Subject: [PATCH 08/30] lint --- params/stateupgrade_config_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/params/stateupgrade_config_test.go b/params/stateupgrade_config_test.go index c57543198d..2f39664c15 100644 --- a/params/stateupgrade_config_test.go +++ b/params/stateupgrade_config_test.go @@ -53,7 +53,6 @@ func TestVerifyStateUpgrades(t *testing.T) { } }) } - } func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { From 7ea5d94d9bdf2fb55912a9ade58d3a154076ca64 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 08:24:12 -0800 Subject: [PATCH 09/30] nit --- params/precompile_upgrade_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/precompile_upgrade_test.go b/params/precompile_upgrade_test.go index a653531026..17ef49a3c5 100644 --- a/params/precompile_upgrade_test.go +++ b/params/precompile_upgrade_test.go @@ -269,7 +269,7 @@ func (tt *upgradeCompatibilityTest) run(t *testing.T, chainConfig ChainConfig) { // the next upgradeBytes. (only check the result on the last apply) if i != len(tt.configs)-1 { if err != nil { - t.Fatalf("expecting ApplyUpgradeBytes call %d to return nil, got %s", i+1, err) + t.Fatalf("expecting checkCompatible call %d to return nil, got %s", i+1, err) } chainConfig = newCfg continue From 3a1cf827db469716c7a8c1c570fa9da04c0beb9e Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 10:40:46 -0800 Subject: [PATCH 10/30] make json compatible w/ genesis style --- params/gen_state_upgrade_account.go | 59 +++++++++++++++++++++++++++++ params/state_upgrade.go | 52 ++++++++++++++++++++++++- params/stateupgrade_config_test.go | 54 ++++++++++++++++++-------- stateupgrade/state_upgrade.go | 2 +- 4 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 params/gen_state_upgrade_account.go diff --git a/params/gen_state_upgrade_account.go b/params/gen_state_upgrade_account.go new file mode 100644 index 0000000000..2ea1a091fa --- /dev/null +++ b/params/gen_state_upgrade_account.go @@ -0,0 +1,59 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package params + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*stateUpgradeAccountMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s StateUpgradeAccount) MarshalJSON() ([]byte, error) { + type StateUpgradeAccount struct { + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[storageJSON]storageJSON `json:"storage,omitempty"` + BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` + } + var enc StateUpgradeAccount + enc.Code = s.Code + if s.Storage != nil { + enc.Storage = make(map[storageJSON]storageJSON, len(s.Storage)) + for k, v := range s.Storage { + enc.Storage[storageJSON(k)] = storageJSON(v) + } + } + enc.BalanceChange = (*math.HexOrDecimal256)(s.BalanceChange) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *StateUpgradeAccount) UnmarshalJSON(input []byte) error { + type StateUpgradeAccount struct { + Code *hexutil.Bytes `json:"code,omitempty"` + Storage map[storageJSON]storageJSON `json:"storage,omitempty"` + BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` + } + var dec StateUpgradeAccount + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Code != nil { + s.Code = *dec.Code + } + if dec.Storage != nil { + s.Storage = make(map[common.Hash]common.Hash, len(dec.Storage)) + for k, v := range dec.Storage { + s.Storage[common.Hash(k)] = common.Hash(v) + } + } + if dec.BalanceChange != nil { + s.BalanceChange = (*big.Int)(dec.BalanceChange) + } + return nil +} diff --git a/params/state_upgrade.go b/params/state_upgrade.go index c078a8fe2d..f5693c46ee 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -4,19 +4,27 @@ package params import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" "math/big" "reflect" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" ) +//go:generate go run github.com/fjl/gencodec -type StateUpgradeAccount -field-override stateUpgradeAccountMarshaling -out gen_state_upgrade_account.go + // StateUpgrade describes the modifications to be made to the state during // a state upgrade. type StateUpgrade struct { BlockTimestamp *big.Int `json:"blockTimestamp,omitempty"` // map from account address to the modification to be made to the account. - Accounts map[common.Address]StateUpgradeAccount `json:"accounts"` + StateUpgradeAccounts StateUpgradeAccounts `json:"accounts"` } // StateUpgradeAccount describes the modifications to be made to an account during @@ -30,3 +38,45 @@ type StateUpgradeAccount struct { func (s *StateUpgrade) Equal(other *StateUpgrade) bool { return reflect.DeepEqual(s, other) } + +type stateUpgradeAccountMarshaling struct { + Code hexutil.Bytes + Storage map[storageJSON]storageJSON + BalanceChange *math.HexOrDecimal256 +} + +// storageJSON represents a 256 bit byte array, but allows less than 256 bits when +// unmarshaling from hex. +type storageJSON common.Hash + +func (h *storageJSON) UnmarshalText(text []byte) error { + text = bytes.TrimPrefix(text, []byte("0x")) + if len(text) > 64 { + return fmt.Errorf("too many hex characters in storage key/value %q", text) + } + offset := len(h) - len(text)/2 // pad on the left + if _, err := hex.Decode(h[offset:], text); err != nil { + fmt.Println(err) + return fmt.Errorf("invalid hex storage key/value %q", text) + } + return nil +} + +func (h storageJSON) MarshalText() ([]byte, error) { + return hexutil.Bytes(h[:]).MarshalText() +} + +// StateUpgradeAlloc specifies the state upgrade type. +type StateUpgradeAccounts map[common.Address]StateUpgradeAccount + +func (s *StateUpgradeAccounts) UnmarshalJSON(data []byte) error { + m := make(map[common.UnprefixedAddress]StateUpgradeAccount) + if err := json.Unmarshal(data, &m); err != nil { + return err + } + *s = make(StateUpgradeAccounts) + for addr, a := range m { + (*s)[common.Address(addr)] = a + } + return nil +} diff --git a/params/stateupgrade_config_test.go b/params/stateupgrade_config_test.go index 2f39664c15..247654439b 100644 --- a/params/stateupgrade_config_test.go +++ b/params/stateupgrade_config_test.go @@ -4,6 +4,7 @@ package params import ( + "encoding/json" "math/big" "testing" @@ -25,15 +26,15 @@ func TestVerifyStateUpgrades(t *testing.T) { { name: "valid upgrade", upgrades: []StateUpgrade{ - {BlockTimestamp: common.Big1, Accounts: modifiedAccounts}, - {BlockTimestamp: common.Big2, Accounts: modifiedAccounts}, + {BlockTimestamp: common.Big1, StateUpgradeAccounts: modifiedAccounts}, + {BlockTimestamp: common.Big2, StateUpgradeAccounts: modifiedAccounts}, }, }, { name: "upgrade block timestamp is not strictly increasing", upgrades: []StateUpgrade{ - {BlockTimestamp: common.Big1, Accounts: modifiedAccounts}, - {BlockTimestamp: common.Big1, Accounts: modifiedAccounts}, + {BlockTimestamp: common.Big1, StateUpgradeAccounts: modifiedAccounts}, + {BlockTimestamp: common.Big1, StateUpgradeAccounts: modifiedAccounts}, }, expectedError: "config block timestamp (1) <= previous timestamp (1)", }, @@ -57,10 +58,10 @@ func TestVerifyStateUpgrades(t *testing.T) { func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { chainConfig := *TestChainConfig - stateUpgrade := map[common.Address]StateUpgradeAccount{ + stateUpgrade := StateUpgradeAccounts{ {1}: {BalanceChange: common.Big1}, } - differentStateUpgrade := map[common.Address]StateUpgradeAccount{ + differentStateUpgrade := StateUpgradeAccounts{ {2}: {BalanceChange: common.Big1}, } @@ -70,12 +71,12 @@ func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { configs: []*UpgradeConfig{ { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(6), StateUpgradeAccounts: stateUpgrade}, }, }, { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(6), StateUpgradeAccounts: stateUpgrade}, }, }, }, @@ -86,14 +87,14 @@ func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { configs: []*UpgradeConfig{ { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, - {BlockTimestamp: big.NewInt(7), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(6), StateUpgradeAccounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(7), StateUpgradeAccounts: stateUpgrade}, }, }, { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, - {BlockTimestamp: big.NewInt(7), Accounts: differentStateUpgrade}, + {BlockTimestamp: big.NewInt(6), StateUpgradeAccounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(7), StateUpgradeAccounts: differentStateUpgrade}, }, }, }, @@ -103,13 +104,13 @@ func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { configs: []*UpgradeConfig{ { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, - {BlockTimestamp: big.NewInt(7), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(6), StateUpgradeAccounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(7), StateUpgradeAccounts: stateUpgrade}, }, }, { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(6), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(6), StateUpgradeAccounts: stateUpgrade}, }, }, }, @@ -120,7 +121,7 @@ func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { configs: []*UpgradeConfig{ { StateUpgrades: []StateUpgrade{ - {BlockTimestamp: big.NewInt(5), Accounts: stateUpgrade}, + {BlockTimestamp: big.NewInt(5), StateUpgradeAccounts: stateUpgrade}, }, }, }, @@ -133,3 +134,24 @@ func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { }) } } + +func TestUnmarshalJSON(t *testing.T) { + jsonBytes := []byte( + `{ + "stateUpgrades": [ + { + "blockTimestamp": 1677608400, + "accounts": { + "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { + "balanceChange": "0x52B7D2DCC80CD2E4000000" + } + } + } + ] + }`, + ) + + var config UpgradeConfig + err := json.Unmarshal(jsonBytes, &config) + require.NoError(t, err) +} diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index 2dbbf88095..336a1db923 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -12,7 +12,7 @@ import ( // Configure applies the state upgrade to the state. func Configure(stateUpgrade *params.StateUpgrade, blockContext BlockContext, state StateDB) error { log.Info("Configuring state upgrade", "block", blockContext.Number(), "timestamp", blockContext.Timestamp()) - for account, upgrade := range stateUpgrade.Accounts { + for account, upgrade := range stateUpgrade.StateUpgradeAccounts { if err := upgradeAccount(account, upgrade, state); err != nil { return err } From 6e921be3ea084179760777b2a060711c00d52b13 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 11:19:17 -0800 Subject: [PATCH 11/30] add support for neg. balance change --- params/gen_state_upgrade_account.go | 7 +++---- params/state_upgrade.go | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/params/gen_state_upgrade_account.go b/params/gen_state_upgrade_account.go index 2ea1a091fa..3bea2b51e2 100644 --- a/params/gen_state_upgrade_account.go +++ b/params/gen_state_upgrade_account.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" ) var _ = (*stateUpgradeAccountMarshaling)(nil) @@ -18,7 +17,7 @@ func (s StateUpgradeAccount) MarshalJSON() ([]byte, error) { type StateUpgradeAccount struct { Code hexutil.Bytes `json:"code,omitempty"` Storage map[storageJSON]storageJSON `json:"storage,omitempty"` - BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` + BalanceChange *SignedHexOrDecimal256 `json:"balanceChange,omitempty"` } var enc StateUpgradeAccount enc.Code = s.Code @@ -28,7 +27,7 @@ func (s StateUpgradeAccount) MarshalJSON() ([]byte, error) { enc.Storage[storageJSON(k)] = storageJSON(v) } } - enc.BalanceChange = (*math.HexOrDecimal256)(s.BalanceChange) + enc.BalanceChange = (*SignedHexOrDecimal256)(s.BalanceChange) return json.Marshal(&enc) } @@ -37,7 +36,7 @@ func (s *StateUpgradeAccount) UnmarshalJSON(input []byte) error { type StateUpgradeAccount struct { Code *hexutil.Bytes `json:"code,omitempty"` Storage map[storageJSON]storageJSON `json:"storage,omitempty"` - BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` + BalanceChange *SignedHexOrDecimal256 `json:"balanceChange,omitempty"` } var dec StateUpgradeAccount if err := json.Unmarshal(input, &dec); err != nil { diff --git a/params/state_upgrade.go b/params/state_upgrade.go index f5693c46ee..24f53d8167 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -42,7 +42,27 @@ func (s *StateUpgrade) Equal(other *StateUpgrade) bool { type stateUpgradeAccountMarshaling struct { Code hexutil.Bytes Storage map[storageJSON]storageJSON - BalanceChange *math.HexOrDecimal256 + BalanceChange *SignedHexOrDecimal256 +} + +type SignedHexOrDecimal256 math.HexOrDecimal256 + +func (s *SignedHexOrDecimal256) UnmarshalText(input []byte) error { + if len(input) > 0 && input[0] == '-' { + err := (*math.HexOrDecimal256)(s).UnmarshalText(input[1:]) + if err != nil { + return err + } + neg := new(big.Int).Neg((*big.Int)(s)) + *s = *(*SignedHexOrDecimal256)(neg) + return nil + } + + return (*math.HexOrDecimal256)(s).UnmarshalText(input) +} + +func (s *SignedHexOrDecimal256) MarshalText() ([]byte, error) { + return (*math.HexOrDecimal256)(s).MarshalText() } // storageJSON represents a 256 bit byte array, but allows less than 256 bits when From c6bb9542f2f9082c6b0c4cfdf198b0a47ca06e6a Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 28 Feb 2023 16:10:37 -0800 Subject: [PATCH 12/30] avoid state upgrades in genesis --- core/genesis.go | 19 +++++-------- core/state_processor.go | 60 +++++++++++++++++++++-------------------- miner/worker.go | 11 ++------ 3 files changed, 39 insertions(+), 51 deletions(-) diff --git a/core/genesis.go b/core/genesis.go index 50dd55483f..1fda0bd448 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -306,6 +306,12 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { Coinbase: g.Coinbase, } + // Configure any stateful precompiles that should be enabled in the genesis. + err = ApplyPrecompileUpgrades(g.Config, nil, types.NewBlockWithHeader(head), statedb) + if err != nil { + panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err)) + } + // Do custom allocation after airdrop in case an address shows up in standard // allocation for addr, account := range g.Alloc { @@ -316,19 +322,6 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { statedb.SetState(addr, key, value) } } - - // Configure any stateful precompiles that should activate in the genesis. - blockContext := types.NewBlockWithHeader(head) - err = ApplyPrecompileActivations(g.Config, nil, blockContext, statedb) - if err != nil { - panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err)) - } - // Configure any state upgrades that should activate in the genesis. - err = ApplyStateUpgrades(g.Config, nil, blockContext, statedb) - if err != nil { - panic(fmt.Sprintf("unable to configure state upgrades in genesis block: %v", err)) - } - root := statedb.IntermediateRoot(false) head.Root = root diff --git a/core/state_processor.go b/core/state_processor.go index 83151e4990..23c9d96649 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -71,25 +71,20 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // transactions failed to execute due to insufficient gas it will return an error. func (p *StateProcessor) Process(block *types.Block, parent *types.Header, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { var ( - receipts types.Receipts - usedGas = new(uint64) - header = block.Header() - blockHash = block.Hash() - blockNumber = block.Number() - allLogs []*types.Log - gp = new(GasPool).AddGas(block.GasLimit()) - timestamp = new(big.Int).SetUint64(header.Time) - parentTimestamp = new(big.Int).SetUint64(parent.Time) + receipts types.Receipts + usedGas = new(uint64) + header = block.Header() + blockHash = block.Hash() + blockNumber = block.Number() + allLogs []*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) + timestamp = new(big.Int).SetUint64(header.Time) ) - // Configure any stateful precompiles that should go into effect during this block. - if err := ApplyPrecompileActivations(p.config, parentTimestamp, block, statedb); err != nil { - log.Error("failed to configure precompiles processing block", "hash", blockHash, "number", blockNumber, "timestamp", block.Time(), "err", err) - return nil, nil, 0, err - } - // Configure any state upgrades that should go into effect during this block. - if err := ApplyStateUpgrades(p.config, parentTimestamp, block, statedb); err != nil { - log.Error("failed to configure state upgrades processing block", "hash", blockHash, "number", blockNumber, "timestamp", block.Time(), "err", err) + // Configure any upgrades that should go into effect during this block. + err := ApplyUpgrades(p.config, new(big.Int).SetUint64(parent.Time), block, statedb) + if err != nil { + log.Error("failed to configure precompiles processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err) return nil, nil, 0, err } @@ -177,14 +172,12 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo return applyTransaction(msg, config, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } -// ApplyPrecompileActivations checks if any of the precompiles specified by the chain config are enabled or disabled by the block +// ApplyPrecompileUpgrades checks if any of the precompiles specified by the chain config are enabled or disabled by the block // transition from [parentTimestamp] to the timestamp set in [blockContext]. If this is the case, it calls [Configure] // to apply the necessary state transitions for the upgrade. -// This function is called: -// - within genesis setup to configure the starting state for precompiles enabled at genesis, -// - during block processing to update the state before processing the given block, -// - during block producing to apply the precompile upgrades before producing the block. -func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { +// This function is called within genesis setup to configure the starting state for precompiles enabled at genesis. +// In block processing and building, ApplyUpgrades is called instead which also applies state upgrades. +func ApplyPrecompileUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { blockTimestamp := blockContext.Timestamp() // Note: RegisteredModules returns precompiles sorted by module addresses. // This ensures that the order we call Configure for each precompile is consistent. @@ -225,14 +218,10 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, return nil } -// ApplyStateUpgrades checks if any of the state upgrades specified by the chain config are activated by the block +// applyStateUpgrades checks if any of the state upgrades specified by the chain config are activated by the block // transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure] // to apply the necessary state transitions for the upgrade. -// This function is called: -// - within genesis setup to apply state upgrades if they are specified at genesis, -// - during block processing to update the state before processing the given block, -// - during block producing to apply the state upgrades before producing the block. -func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { +func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { if err := stateupgrade.Configure(&upgrade, blockContext, statedb); err != nil { @@ -241,3 +230,16 @@ func ApplyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockCo } return nil } + +// ApplyUpgrades checks if any of the precompile or state upgrades specified by the chain config are activated by the block +// transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure] +// to apply the necessary state transitions for the upgrade. +// This function is called: +// - during block processing to update the state before processing the given block, +// - during block producing to apply the state upgrades before producing the block. +func ApplyUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { + if err := ApplyPrecompileUpgrades(c, parentTimestamp, blockContext, statedb); err != nil { + return err + } + return applyStateUpgrades(c, parentTimestamp, blockContext, statedb) +} diff --git a/miner/worker.go b/miner/worker.go index c12267365e..041ca12a90 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -185,19 +185,12 @@ func (w *worker) commitNewWork() (*types.Block, error) { if err != nil { return nil, fmt.Errorf("failed to create new current environment: %w", err) } - // Configure any stateful precompiles that should go into effect during this block. - blockContext := types.NewBlockWithHeader(header) - err = core.ApplyPrecompileActivations(w.chainConfig, new(big.Int).SetUint64(parent.Time()), blockContext, env.state) + // Configure any upgrades that should go into effect during this block. + err = core.ApplyUpgrades(w.chainConfig, new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), env.state) if err != nil { log.Error("failed to configure precompiles mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err) return nil, err } - // Configure any state upgrades that should go into effect during this block. - err = core.ApplyStateUpgrades(w.chainConfig, new(big.Int).SetUint64(parent.Time()), blockContext, env.state) - if err != nil { - log.Error("failed to configure state upgrades mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err) - return nil, err - } // Fill the block with all available pending transactions. pending := w.eth.TxPool().Pending(true) From 62760ee548265b29cdb1ea5c488e0d0fa136dcca Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 28 Feb 2023 16:33:10 -0800 Subject: [PATCH 13/30] Revert apply precompile func name change --- core/genesis.go | 2 +- core/state_processor.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/genesis.go b/core/genesis.go index 1fda0bd448..07a0ac1bc0 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -307,7 +307,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { } // Configure any stateful precompiles that should be enabled in the genesis. - err = ApplyPrecompileUpgrades(g.Config, nil, types.NewBlockWithHeader(head), statedb) + err = ApplyPrecompileActivations(g.Config, nil, types.NewBlockWithHeader(head), statedb) if err != nil { panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err)) } diff --git a/core/state_processor.go b/core/state_processor.go index 23c9d96649..7f71470bf3 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -172,12 +172,12 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo return applyTransaction(msg, config, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } -// ApplyPrecompileUpgrades checks if any of the precompiles specified by the chain config are enabled or disabled by the block +// ApplyPrecompileActivations checks if any of the precompiles specified by the chain config are enabled or disabled by the block // transition from [parentTimestamp] to the timestamp set in [blockContext]. If this is the case, it calls [Configure] // to apply the necessary state transitions for the upgrade. // This function is called within genesis setup to configure the starting state for precompiles enabled at genesis. // In block processing and building, ApplyUpgrades is called instead which also applies state upgrades. -func ApplyPrecompileUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { +func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { blockTimestamp := blockContext.Timestamp() // Note: RegisteredModules returns precompiles sorted by module addresses. // This ensures that the order we call Configure for each precompile is consistent. @@ -238,7 +238,7 @@ func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockCo // - during block processing to update the state before processing the given block, // - during block producing to apply the state upgrades before producing the block. func ApplyUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { - if err := ApplyPrecompileUpgrades(c, parentTimestamp, blockContext, statedb); err != nil { + if err := ApplyPrecompileActivations(c, parentTimestamp, blockContext, statedb); err != nil { return err } return applyStateUpgrades(c, parentTimestamp, blockContext, statedb) From bc3a8c782db7f39fe2624495d00683e4caecc98b Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 28 Feb 2023 16:34:52 -0800 Subject: [PATCH 14/30] Update comment --- core/state_processor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 7f71470bf3..a3e426f259 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -235,8 +235,8 @@ func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockCo // transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure] // to apply the necessary state transitions for the upgrade. // This function is called: -// - during block processing to update the state before processing the given block, -// - during block producing to apply the state upgrades before producing the block. +// - in block processing to update the state when processing a block. +// - in the miner to apply the state upgrades when producing a block. func ApplyUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { if err := ApplyPrecompileActivations(c, parentTimestamp, blockContext, statedb); err != nil { return err From fd2510903010626ee83a4caf7f3d2e18463def0e Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 28 Feb 2023 16:38:18 -0800 Subject: [PATCH 15/30] Add state upgrade test case --- params/stateupgrade_config_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/params/stateupgrade_config_test.go b/params/stateupgrade_config_test.go index 247654439b..ec1b2eb032 100644 --- a/params/stateupgrade_config_test.go +++ b/params/stateupgrade_config_test.go @@ -38,6 +38,14 @@ func TestVerifyStateUpgrades(t *testing.T) { }, expectedError: "config block timestamp (1) <= previous timestamp (1)", }, + { + name: "upgrade block timestamp is decreases", + upgrades: []StateUpgrade{ + {BlockTimestamp: common.Big2, StateUpgradeAccounts: modifiedAccounts}, + {BlockTimestamp: common.Big1, StateUpgradeAccounts: modifiedAccounts}, + }, + expectedError: "config block timestamp (1) <= previous timestamp (2)", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From ca2c88959b480ab5216b195959b62481b48019d7 Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 28 Feb 2023 17:37:52 -0800 Subject: [PATCH 16/30] Cleanup --- core/state_processor.go | 8 ++-- params/gen_state_upgrade_account.go | 58 ----------------------- params/state_upgrade.go | 71 +---------------------------- params/stateupgrade_config_test.go | 34 ++++++++++---- plugin/evm/vm_upgrade_bytes_test.go | 9 ++-- stateupgrade/interfaces.go | 7 --- stateupgrade/state_upgrade.go | 17 ++++--- 7 files changed, 43 insertions(+), 161 deletions(-) delete mode 100644 params/gen_state_upgrade_account.go diff --git a/core/state_processor.go b/core/state_processor.go index a3e426f259..e8dc3ef6e0 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -221,11 +221,11 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, // applyStateUpgrades checks if any of the state upgrades specified by the chain config are activated by the block // transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure] // to apply the necessary state transitions for the upgrade. -func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { +func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { - if err := stateupgrade.Configure(&upgrade, blockContext, statedb); err != nil { - return fmt.Errorf("could not configure state upgrade, reason: %w", err) + if err := stateupgrade.Configure(&upgrade, statedb); err != nil { + return fmt.Errorf("could not configure state upgrade: %w", err) } } return nil @@ -237,7 +237,7 @@ func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockCo // This function is called: // - in block processing to update the state when processing a block. // - in the miner to apply the state upgrades when producing a block. -func ApplyUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext stateupgrade.BlockContext, statedb *state.StateDB) error { +func ApplyUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { if err := ApplyPrecompileActivations(c, parentTimestamp, blockContext, statedb); err != nil { return err } diff --git a/params/gen_state_upgrade_account.go b/params/gen_state_upgrade_account.go deleted file mode 100644 index 3bea2b51e2..0000000000 --- a/params/gen_state_upgrade_account.go +++ /dev/null @@ -1,58 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package params - -import ( - "encoding/json" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*stateUpgradeAccountMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (s StateUpgradeAccount) MarshalJSON() ([]byte, error) { - type StateUpgradeAccount struct { - Code hexutil.Bytes `json:"code,omitempty"` - Storage map[storageJSON]storageJSON `json:"storage,omitempty"` - BalanceChange *SignedHexOrDecimal256 `json:"balanceChange,omitempty"` - } - var enc StateUpgradeAccount - enc.Code = s.Code - if s.Storage != nil { - enc.Storage = make(map[storageJSON]storageJSON, len(s.Storage)) - for k, v := range s.Storage { - enc.Storage[storageJSON(k)] = storageJSON(v) - } - } - enc.BalanceChange = (*SignedHexOrDecimal256)(s.BalanceChange) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (s *StateUpgradeAccount) UnmarshalJSON(input []byte) error { - type StateUpgradeAccount struct { - Code *hexutil.Bytes `json:"code,omitempty"` - Storage map[storageJSON]storageJSON `json:"storage,omitempty"` - BalanceChange *SignedHexOrDecimal256 `json:"balanceChange,omitempty"` - } - var dec StateUpgradeAccount - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Code != nil { - s.Code = *dec.Code - } - if dec.Storage != nil { - s.Storage = make(map[common.Hash]common.Hash, len(dec.Storage)) - for k, v := range dec.Storage { - s.Storage[common.Hash(k)] = common.Hash(v) - } - } - if dec.BalanceChange != nil { - s.BalanceChange = (*big.Int)(dec.BalanceChange) - } - return nil -} diff --git a/params/state_upgrade.go b/params/state_upgrade.go index 24f53d8167..f60e9d7c46 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -4,15 +4,10 @@ package params import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" "math/big" "reflect" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" ) @@ -24,7 +19,7 @@ type StateUpgrade struct { BlockTimestamp *big.Int `json:"blockTimestamp,omitempty"` // map from account address to the modification to be made to the account. - StateUpgradeAccounts StateUpgradeAccounts `json:"accounts"` + StateUpgradeAccounts map[common.Address]StateUpgradeAccount `json:"accounts"` } // StateUpgradeAccount describes the modifications to be made to an account during @@ -32,71 +27,9 @@ type StateUpgrade struct { type StateUpgradeAccount struct { Code []byte `json:"code,omitempty"` Storage map[common.Hash]common.Hash `json:"storage,omitempty"` - BalanceChange *big.Int `json:"balanceChange,omitempty"` + BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` } func (s *StateUpgrade) Equal(other *StateUpgrade) bool { return reflect.DeepEqual(s, other) } - -type stateUpgradeAccountMarshaling struct { - Code hexutil.Bytes - Storage map[storageJSON]storageJSON - BalanceChange *SignedHexOrDecimal256 -} - -type SignedHexOrDecimal256 math.HexOrDecimal256 - -func (s *SignedHexOrDecimal256) UnmarshalText(input []byte) error { - if len(input) > 0 && input[0] == '-' { - err := (*math.HexOrDecimal256)(s).UnmarshalText(input[1:]) - if err != nil { - return err - } - neg := new(big.Int).Neg((*big.Int)(s)) - *s = *(*SignedHexOrDecimal256)(neg) - return nil - } - - return (*math.HexOrDecimal256)(s).UnmarshalText(input) -} - -func (s *SignedHexOrDecimal256) MarshalText() ([]byte, error) { - return (*math.HexOrDecimal256)(s).MarshalText() -} - -// storageJSON represents a 256 bit byte array, but allows less than 256 bits when -// unmarshaling from hex. -type storageJSON common.Hash - -func (h *storageJSON) UnmarshalText(text []byte) error { - text = bytes.TrimPrefix(text, []byte("0x")) - if len(text) > 64 { - return fmt.Errorf("too many hex characters in storage key/value %q", text) - } - offset := len(h) - len(text)/2 // pad on the left - if _, err := hex.Decode(h[offset:], text); err != nil { - fmt.Println(err) - return fmt.Errorf("invalid hex storage key/value %q", text) - } - return nil -} - -func (h storageJSON) MarshalText() ([]byte, error) { - return hexutil.Bytes(h[:]).MarshalText() -} - -// StateUpgradeAlloc specifies the state upgrade type. -type StateUpgradeAccounts map[common.Address]StateUpgradeAccount - -func (s *StateUpgradeAccounts) UnmarshalJSON(data []byte) error { - m := make(map[common.UnprefixedAddress]StateUpgradeAccount) - if err := json.Unmarshal(data, &m); err != nil { - return err - } - *s = make(StateUpgradeAccounts) - for addr, a := range m { - (*s)[common.Address(addr)] = a - } - return nil -} diff --git a/params/stateupgrade_config_test.go b/params/stateupgrade_config_test.go index ec1b2eb032..61e00321ee 100644 --- a/params/stateupgrade_config_test.go +++ b/params/stateupgrade_config_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/stretchr/testify/require" ) func TestVerifyStateUpgrades(t *testing.T) { modifiedAccounts := map[common.Address]StateUpgradeAccount{ {1}: { - BalanceChange: common.Big1, + BalanceChange: (*math.HexOrDecimal256)(common.Big1), }, } tests := []struct { @@ -66,11 +67,11 @@ func TestVerifyStateUpgrades(t *testing.T) { func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { chainConfig := *TestChainConfig - stateUpgrade := StateUpgradeAccounts{ - {1}: {BalanceChange: common.Big1}, + stateUpgrade := map[common.Address]StateUpgradeAccount{ + {1}: {BalanceChange: (*math.HexOrDecimal256)(common.Big1)}, } - differentStateUpgrade := StateUpgradeAccounts{ - {2}: {BalanceChange: common.Big1}, + differentStateUpgrade := map[common.Address]StateUpgradeAccount{ + {2}: {BalanceChange: (*math.HexOrDecimal256)(common.Big1)}, } tests := map[string]upgradeCompatibilityTest{ @@ -143,15 +144,15 @@ func TestCheckCompatibleStateUpgradeConfigs(t *testing.T) { } } -func TestUnmarshalJSON(t *testing.T) { +func TestUnmarshalStateUpgradeJSON(t *testing.T) { jsonBytes := []byte( `{ "stateUpgrades": [ { "blockTimestamp": 1677608400, "accounts": { - "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { - "balanceChange": "0x52B7D2DCC80CD2E4000000" + "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { + "balanceChange": "100" } } } @@ -159,7 +160,20 @@ func TestUnmarshalJSON(t *testing.T) { }`, ) - var config UpgradeConfig - err := json.Unmarshal(jsonBytes, &config) + upgradeConfig := UpgradeConfig{ + StateUpgrades: []StateUpgrade{ + { + BlockTimestamp: big.NewInt(1677608400), + StateUpgradeAccounts: map[common.Address]StateUpgradeAccount{ + common.HexToAddress("0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"): { + BalanceChange: (*math.HexOrDecimal256)(big.NewInt(100)), + }, + }, + }, + }, + } + var unmarshaledConfig UpgradeConfig + err := json.Unmarshal(jsonBytes, &unmarshaledConfig) require.NoError(t, err) + require.Equal(t, upgradeConfig, unmarshaledConfig) } diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 5e60cbb510..5eb2ad9e34 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -22,6 +22,7 @@ import ( "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -293,7 +294,7 @@ func TestVMStateUpgrade(t *testing.T) { // This modification will be applied to an existing account genesisAccountUpgrade := ¶ms.StateUpgradeAccount{ - BalanceChange: big.NewInt(-100), + BalanceChange: (*math.HexOrDecimal256)(big.NewInt(100)), Storage: map[common.Hash]common.Hash{storageKey: {}}, } @@ -301,7 +302,7 @@ func TestVMStateUpgrade(t *testing.T) { newAccount := common.Address{42} code := []byte{0x01, 0x02, 0x03} newAccountUpgrade := ¶ms.StateUpgradeAccount{ - BalanceChange: big.NewInt(100), + BalanceChange: (*math.HexOrDecimal256)(big.NewInt(100)), Storage: map[common.Hash]common.Hash{storageKey: common.HexToHash("0x6666")}, Code: code, } @@ -358,14 +359,14 @@ func TestVMStateUpgrade(t *testing.T) { // Existing account expectedGenesisAccountBalance := new(big.Int).Add( genesisAccount.Balance, - genesisAccountUpgrade.BalanceChange, + (*big.Int)(genesisAccountUpgrade.BalanceChange), ) require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisAccountBalance) require.Equal(t, state.GetState(testEthAddrs[0], storageKey), genesisAccountUpgrade.Storage[storageKey]) // New account expectedNewAccountBalance := newAccountUpgrade.BalanceChange - require.Equal(t, state.GetBalance(newAccount), expectedNewAccountBalance) + require.Equal(t, state.GetBalance(newAccount), (*big.Int)(expectedNewAccountBalance)) require.Equal(t, state.GetCode(newAccount), code) require.Equal(t, state.GetNonce(newAccount), uint64(1)) // Nonce should be set to 1 when code is set if nonce was 0 require.Equal(t, state.GetState(newAccount, storageKey), newAccountUpgrade.Storage[storageKey]) diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go index aeba0c28af..df5dc8eb30 100644 --- a/stateupgrade/interfaces.go +++ b/stateupgrade/interfaces.go @@ -21,10 +21,3 @@ type StateDB interface { CreateAccount(common.Address) Exist(common.Address) bool } - -// BlockContext defines an interface that provides information about the -// block that activates the state upgrade. -type BlockContext interface { - Number() *big.Int - Timestamp() *big.Int -} diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index 336a1db923..adada77cb8 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -4,14 +4,14 @@ package stateupgrade import ( + "math/big" + "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" ) // Configure applies the state upgrade to the state. -func Configure(stateUpgrade *params.StateUpgrade, blockContext BlockContext, state StateDB) error { - log.Info("Configuring state upgrade", "block", blockContext.Number(), "timestamp", blockContext.Timestamp()) +func Configure(stateUpgrade *params.StateUpgrade, state StateDB) error { for account, upgrade := range stateUpgrade.StateUpgradeAccounts { if err := upgradeAccount(account, upgrade, state); err != nil { return err @@ -22,19 +22,18 @@ func Configure(stateUpgrade *params.StateUpgrade, blockContext BlockContext, sta // upgradeAccount applies the state upgrade to the given account. func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount, state StateDB) error { - // TODO: is this necessary? + // Create the account if it does not exist if !state.Exist(account) { - // Create the account if it does not exist. state.CreateAccount(account) } if upgrade.BalanceChange != nil { - state.AddBalance(account, upgrade.BalanceChange) + state.AddBalance(account, (*big.Int)(upgrade.BalanceChange)) } - if upgrade.Code != nil { + if len(upgrade.Code) != 0 { + // if the nonce is 0, set the nonce to 1 as we would when deploying a contract at + // the address. if state.GetNonce(account) == 0 { - // If the nonce is 0, we will set it to a non-zero value - // so the account is not considered empty. state.SetNonce(account, 1) } state.SetCode(account, upgrade.Code) From fae7aa3b1c517864630ed60810f3a66bcc50cf0d Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 28 Feb 2023 17:38:59 -0800 Subject: [PATCH 17/30] remove codegen comment --- params/state_upgrade.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/params/state_upgrade.go b/params/state_upgrade.go index f60e9d7c46..ce5e4bb60e 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -11,8 +11,6 @@ import ( "github.com/ethereum/go-ethereum/common/math" ) -//go:generate go run github.com/fjl/gencodec -type StateUpgradeAccount -field-override stateUpgradeAccountMarshaling -out gen_state_upgrade_account.go - // StateUpgrade describes the modifications to be made to the state during // a state upgrade. type StateUpgrade struct { From 3db8adcd4edf0fc6127d8173a55cfb795e83b19b Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Wed, 1 Mar 2023 09:36:44 -0800 Subject: [PATCH 18/30] add EIP158 check --- core/state_processor.go | 2 +- stateupgrade/interfaces.go | 12 ++++++++++++ stateupgrade/state_upgrade.go | 9 +++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index e8dc3ef6e0..5ad7122da9 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -224,7 +224,7 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { - if err := stateupgrade.Configure(&upgrade, statedb); err != nil { + if err := stateupgrade.Configure(&upgrade, c, statedb, blockContext); err != nil { return fmt.Errorf("could not configure state upgrade: %w", err) } } diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go index df5dc8eb30..f667980487 100644 --- a/stateupgrade/interfaces.go +++ b/stateupgrade/interfaces.go @@ -21,3 +21,15 @@ type StateDB interface { CreateAccount(common.Address) Exist(common.Address) bool } + +// ChainContext defines an interface that provides information to a state upgrade +// about the chain configuration. +type ChainContext interface { + IsEIP158(num *big.Int) bool +} + +// BlockContext defines an interface that provides information to a state upgrade +// about the block that activates the upgrade. +type BlockContext interface { + Number() *big.Int +} diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index adada77cb8..682e7cd493 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -11,9 +11,10 @@ import ( ) // Configure applies the state upgrade to the state. -func Configure(stateUpgrade *params.StateUpgrade, state StateDB) error { +func Configure(stateUpgrade *params.StateUpgrade, chainConfig ChainContext, state StateDB, blockContext BlockContext) error { + isEIP158 := chainConfig.IsEIP158(blockContext.Number()) for account, upgrade := range stateUpgrade.StateUpgradeAccounts { - if err := upgradeAccount(account, upgrade, state); err != nil { + if err := upgradeAccount(account, upgrade, state, isEIP158); err != nil { return err } } @@ -21,7 +22,7 @@ func Configure(stateUpgrade *params.StateUpgrade, state StateDB) error { } // upgradeAccount applies the state upgrade to the given account. -func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount, state StateDB) error { +func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount, state StateDB, isEIP158 bool) error { // Create the account if it does not exist if !state.Exist(account) { state.CreateAccount(account) @@ -33,7 +34,7 @@ func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount, if len(upgrade.Code) != 0 { // if the nonce is 0, set the nonce to 1 as we would when deploying a contract at // the address. - if state.GetNonce(account) == 0 { + if isEIP158 && state.GetNonce(account) == 0 { state.SetNonce(account, 1) } state.SetCode(account, upgrade.Code) From 9ef281e2cf5d13f6acd4c0f6e62b95f1dade4a41 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Wed, 1 Mar 2023 22:36:59 -0800 Subject: [PATCH 19/30] start v0.4.12 release cycle --- compatibility.json | 1 + scripts/versions.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compatibility.json b/compatibility.json index d84a25cbbe..7783acaee3 100644 --- a/compatibility.json +++ b/compatibility.json @@ -1,5 +1,6 @@ { "rpcChainVMProtocolVersion": { + "v0.4.12": 24, "v0.4.11": 24, "v0.4.10": 23, "v0.4.9": 23, diff --git a/scripts/versions.sh b/scripts/versions.sh index 2cf2271d1c..43df281829 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Set up the versions to be used - populate ENV variables only if they are not already populated -SUBNET_EVM_VERSION=${SUBNET_EVM_VERSION:-'v0.4.11'} +SUBNET_EVM_VERSION=${SUBNET_EVM_VERSION:-'v0.4.12'} # Don't export them as they're used in the context of other calls AVALANCHEGO_VERSION=${AVALANCHE_VERSION:-'v1.9.10'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} From 2351e24125835acd0e1bb08d82d9d2ec220adf16 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Wed, 1 Mar 2023 22:56:06 -0800 Subject: [PATCH 20/30] add setup action to lint ci for proper go version --- .github/workflows/lint-tests-release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/lint-tests-release.yml b/.github/workflows/lint-tests-release.yml index b20e28fa50..e0688b3a59 100644 --- a/.github/workflows/lint-tests-release.yml +++ b/.github/workflows/lint-tests-release.yml @@ -16,6 +16,9 @@ jobs: - uses: actions/checkout@v3 - run: ./scripts/lint_allowed_geth_imports.sh shell: bash + - uses: actions/setup-go@v3 + with: + go-version: "1.19" - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: From 0c6d8c3c72d7d762c8aea68f0df37ff1b61b2ffd Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 10:42:07 -0800 Subject: [PATCH 21/30] fix code interpretation as hex --- params/state_upgrade.go | 3 ++- plugin/evm/vm_upgrade_bytes_test.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/params/state_upgrade.go b/params/state_upgrade.go index ce5e4bb60e..c03ad0d637 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -8,6 +8,7 @@ import ( "reflect" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" ) @@ -23,7 +24,7 @@ type StateUpgrade struct { // StateUpgradeAccount describes the modifications to be made to an account during // a state upgrade. type StateUpgradeAccount struct { - Code []byte `json:"code,omitempty"` + Code hexutil.Bytes `json:"code,omitempty"` Storage map[common.Hash]common.Hash `json:"storage,omitempty"` BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` } diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 5eb2ad9e34..335e2e42c4 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -22,7 +22,9 @@ import ( "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -300,7 +302,9 @@ func TestVMStateUpgrade(t *testing.T) { // This modification will be applied to a new account newAccount := common.Address{42} - code := []byte{0x01, 0x02, 0x03} + codeStr := "0xdeadbeef" + code, err := hexutil.Decode(codeStr) + require.NoError(t, err) newAccountUpgrade := ¶ms.StateUpgradeAccount{ BalanceChange: (*math.HexOrDecimal256)(big.NewInt(100)), Storage: map[common.Hash]common.Hash{storageKey: common.HexToHash("0x6666")}, @@ -326,6 +330,7 @@ func TestVMStateUpgrade(t *testing.T) { newAccount.Hex(), mustMarshal(t, newAccountUpgrade), ) + require.Contains(t, upgradeBytesJSON, codeStr) // initialize the VM with these upgrade bytes issuer, vm, _, _ := GenesisVM(t, true, genesisStr, "", upgradeBytesJSON) @@ -368,6 +373,7 @@ func TestVMStateUpgrade(t *testing.T) { expectedNewAccountBalance := newAccountUpgrade.BalanceChange require.Equal(t, state.GetBalance(newAccount), (*big.Int)(expectedNewAccountBalance)) require.Equal(t, state.GetCode(newAccount), code) + require.Equal(t, state.GetCodeHash(newAccount), crypto.Keccak256Hash(code)) require.Equal(t, state.GetNonce(newAccount), uint64(1)) // Nonce should be set to 1 when code is set if nonce was 0 require.Equal(t, state.GetState(newAccount, storageKey), newAccountUpgrade.Storage[storageKey]) } From f6e6c1ba24d0fb7386c6ec5567a78e656cb4b5aa Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 11:09:06 -0800 Subject: [PATCH 22/30] fix test --- plugin/evm/vm_upgrade_bytes_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 335e2e42c4..2772f4c7e3 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -292,6 +292,7 @@ func TestVMStateUpgrade(t *testing.T) { require.True(t, ok) storageKey := common.HexToHash("0x1234") genesisAccount.Storage = map[common.Hash]common.Hash{storageKey: common.HexToHash("0x5555")} + genesis.Alloc[testEthAddrs[0]] = genesisAccount // have to assign this back to the map for changes to take effect. genesisStr := mustMarshal(t, genesis) // This modification will be applied to an existing account From 7dc8742435028281aef7170181ed611426524455 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 11:21:42 -0800 Subject: [PATCH 23/30] bump lint action runner --- .github/workflows/lint-tests-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-tests-release.yml b/.github/workflows/lint-tests-release.yml index e0688b3a59..859e54eec3 100644 --- a/.github/workflows/lint-tests-release.yml +++ b/.github/workflows/lint-tests-release.yml @@ -20,7 +20,7 @@ jobs: with: go-version: "1.19" - name: golangci-lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 with: version: v1.47 working-directory: . From 0f3dedf9ad9134808399b44d997ef4c9f7da8187 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 15:53:21 -0800 Subject: [PATCH 24/30] add log --- core/state_processor.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/state_processor.go b/core/state_processor.go index 5ad7122da9..481281bd86 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -224,6 +224,7 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { + log.Info("Applying state upgrade", "blockNumber", blockContext.Number(), "numAccounts", len(upgrade.StateUpgradeAccounts)) if err := stateupgrade.Configure(&upgrade, c, statedb, blockContext); err != nil { return fmt.Errorf("could not configure state upgrade: %w", err) } From 923ecf7214e79ced8fd17c24111b04fd804eb4b3 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 15:57:54 -0800 Subject: [PATCH 25/30] add log --- core/state_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state_processor.go b/core/state_processor.go index 481281bd86..8ddc8c4f0d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -224,7 +224,7 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error { // Apply state upgrades for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) { - log.Info("Applying state upgrade", "blockNumber", blockContext.Number(), "numAccounts", len(upgrade.StateUpgradeAccounts)) + log.Info("Applying state upgrade", "blockNumber", blockContext.Number(), "upgrade", upgrade) if err := stateupgrade.Configure(&upgrade, c, statedb, blockContext); err != nil { return fmt.Errorf("could not configure state upgrade: %w", err) } From f03b40e7f03489b32a48fea28bb4c80d534ff33a Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 16:54:02 -0800 Subject: [PATCH 26/30] add genesis code test case --- plugin/evm/vm_upgrade_bytes_test.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 2772f4c7e3..8b4e033d23 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -292,24 +292,29 @@ func TestVMStateUpgrade(t *testing.T) { require.True(t, ok) storageKey := common.HexToHash("0x1234") genesisAccount.Storage = map[common.Hash]common.Hash{storageKey: common.HexToHash("0x5555")} + genesisCode, err := hexutil.Decode("0xabcd") + require.NoError(t, err) + genesisAccount.Code = genesisCode + genesisAccount.Nonce = 2 // set to a non-zero value to test that it is preserved genesis.Alloc[testEthAddrs[0]] = genesisAccount // have to assign this back to the map for changes to take effect. genesisStr := mustMarshal(t, genesis) + upgradedCodeStr := "0xdeadbeef" // this upgradedCode will be applied during the upgrade + upgradedCode, err := hexutil.Decode(upgradedCodeStr) // This modification will be applied to an existing account genesisAccountUpgrade := ¶ms.StateUpgradeAccount{ BalanceChange: (*math.HexOrDecimal256)(big.NewInt(100)), Storage: map[common.Hash]common.Hash{storageKey: {}}, + Code: upgradedCode, } // This modification will be applied to a new account newAccount := common.Address{42} - codeStr := "0xdeadbeef" - code, err := hexutil.Decode(codeStr) require.NoError(t, err) newAccountUpgrade := ¶ms.StateUpgradeAccount{ BalanceChange: (*math.HexOrDecimal256)(big.NewInt(100)), Storage: map[common.Hash]common.Hash{storageKey: common.HexToHash("0x6666")}, - Code: code, + Code: upgradedCode, } upgradeTimestamp := time.Unix(10, 0) // arbitrary timestamp to perform the network upgrade @@ -331,7 +336,7 @@ func TestVMStateUpgrade(t *testing.T) { newAccount.Hex(), mustMarshal(t, newAccountUpgrade), ) - require.Contains(t, upgradeBytesJSON, codeStr) + require.Contains(t, upgradeBytesJSON, upgradedCodeStr) // initialize the VM with these upgrade bytes issuer, vm, _, _ := GenesisVM(t, true, genesisStr, "", upgradeBytesJSON) @@ -369,12 +374,15 @@ func TestVMStateUpgrade(t *testing.T) { ) require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisAccountBalance) require.Equal(t, state.GetState(testEthAddrs[0], storageKey), genesisAccountUpgrade.Storage[storageKey]) + require.Equal(t, state.GetCode(testEthAddrs[0]), upgradedCode) + require.Equal(t, state.GetCodeHash(testEthAddrs[0]), crypto.Keccak256Hash(upgradedCode)) + require.Equal(t, state.GetNonce(testEthAddrs[0]), genesisAccount.Nonce) // Nonce should be preserved since it was non-zero // New account expectedNewAccountBalance := newAccountUpgrade.BalanceChange require.Equal(t, state.GetBalance(newAccount), (*big.Int)(expectedNewAccountBalance)) - require.Equal(t, state.GetCode(newAccount), code) - require.Equal(t, state.GetCodeHash(newAccount), crypto.Keccak256Hash(code)) + require.Equal(t, state.GetCode(newAccount), upgradedCode) + require.Equal(t, state.GetCodeHash(newAccount), crypto.Keccak256Hash(upgradedCode)) require.Equal(t, state.GetNonce(newAccount), uint64(1)) // Nonce should be set to 1 when code is set if nonce was 0 require.Equal(t, state.GetState(newAccount, storageKey), newAccountUpgrade.Storage[storageKey]) } From eca4551392a775eb730da8687c9a3a061b2dbb63 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 17:01:26 -0800 Subject: [PATCH 27/30] fix typo --- plugin/evm/vm_upgrade_bytes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 8b4e033d23..9124945487 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -299,7 +299,7 @@ func TestVMStateUpgrade(t *testing.T) { genesis.Alloc[testEthAddrs[0]] = genesisAccount // have to assign this back to the map for changes to take effect. genesisStr := mustMarshal(t, genesis) - upgradedCodeStr := "0xdeadbeef" // this upgradedCode will be applied during the upgrade + upgradedCodeStr := "0xdeadbeef" // this code will be applied during the upgrade upgradedCode, err := hexutil.Decode(upgradedCodeStr) // This modification will be applied to an existing account genesisAccountUpgrade := ¶ms.StateUpgradeAccount{ From 0cb03a28db63a44dc1792a18d31942efc8a5b15c Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 3 Mar 2023 18:45:57 -0800 Subject: [PATCH 28/30] bump avalanchego version --- go.mod | 2 +- go.sum | 4 ++-- scripts/versions.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index e4bae89122..524fbdfb94 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanchego v1.9.10 + github.com/ava-labs/avalanchego v1.9.11 github.com/cespare/cp v0.1.0 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v1.8.0 diff --git a/go.sum b/go.sum index cfcf45fdc5..e471048922 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/ava-labs/avalanchego v1.9.10 h1:IQYUruncY3yuKLwfbGXGslydTJQcjzLMtZuW8g5wQOY= -github.com/ava-labs/avalanchego v1.9.10/go.mod h1:nNc+4JCIJMaEt2xRmeMVAUyQwDIap7RvnMrfWD2Tpo8= +github.com/ava-labs/avalanchego v1.9.11 h1:5hXHJMvErfaolWD7Hw9gZaVylck2shBaV/2NTHA0BfA= +github.com/ava-labs/avalanchego v1.9.11/go.mod h1:nNc+4JCIJMaEt2xRmeMVAUyQwDIap7RvnMrfWD2Tpo8= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= diff --git a/scripts/versions.sh b/scripts/versions.sh index 43df281829..926aca9df0 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -3,7 +3,7 @@ # Set up the versions to be used - populate ENV variables only if they are not already populated SUBNET_EVM_VERSION=${SUBNET_EVM_VERSION:-'v0.4.12'} # Don't export them as they're used in the context of other calls -AVALANCHEGO_VERSION=${AVALANCHE_VERSION:-'v1.9.10'} +AVALANCHEGO_VERSION=${AVALANCHE_VERSION:-'v1.9.11'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier From 95bb290cda93a6ede242a06cc9f11f1971327dbe Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 7 Mar 2023 08:44:47 -0800 Subject: [PATCH 29/30] fix merge --- params/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/params/config.go b/params/config.go index 0bb9a879e3..b583ec00dd 100644 --- a/params/config.go +++ b/params/config.go @@ -142,6 +142,9 @@ type UpgradeConfig struct { // forks must be present or upgradeBytes will be rejected. NetworkUpgrades *NetworkUpgrades `json:"networkUpgrades,omitempty"` + // Config for modifying state as a network upgrade. + StateUpgrades []StateUpgrade `json:"stateUpgrades,omitempty"` + // Config for enabling and disabling precompiles as network upgrades. PrecompileUpgrades []PrecompileUpgrade `json:"precompileUpgrades,omitempty"` } From c53b0942834036562022233950a49e907a70d88e Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Wed, 8 Mar 2023 12:43:36 -0800 Subject: [PATCH 30/30] add check for timestamp 0 --- params/precompile_upgrade.go | 5 +++++ params/stateupgrade_config_test.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/params/precompile_upgrade.go b/params/precompile_upgrade.go index efa3bdcb0a..e8346174bf 100644 --- a/params/precompile_upgrade.go +++ b/params/precompile_upgrade.go @@ -155,6 +155,11 @@ func (c *ChainConfig) verifyStateUpgrades() error { var previousUpgradeTimestamp *big.Int for i, upgrade := range c.StateUpgrades { upgradeTimestamp := upgrade.BlockTimestamp + // Verify the upgrade's timestamp is greater than 0 (to avoid confusion with genesis). + if upgradeTimestamp.Cmp(common.Big0) <= 0 { + return fmt.Errorf("StateUpgrade[%d]: config block timestamp (%v) must be greater than 0", i, upgradeTimestamp) + } + // Verify specified timestamps are strictly monotonically increasing. if previousUpgradeTimestamp != nil && upgradeTimestamp.Cmp(previousUpgradeTimestamp) <= 0 { return fmt.Errorf("StateUpgrade[%d]: config block timestamp (%v) <= previous timestamp (%v)", i, upgradeTimestamp, previousUpgradeTimestamp) diff --git a/params/stateupgrade_config_test.go b/params/stateupgrade_config_test.go index 61e00321ee..4a7a85fe81 100644 --- a/params/stateupgrade_config_test.go +++ b/params/stateupgrade_config_test.go @@ -40,13 +40,20 @@ func TestVerifyStateUpgrades(t *testing.T) { expectedError: "config block timestamp (1) <= previous timestamp (1)", }, { - name: "upgrade block timestamp is decreases", + name: "upgrade block timestamp decreases", upgrades: []StateUpgrade{ {BlockTimestamp: common.Big2, StateUpgradeAccounts: modifiedAccounts}, {BlockTimestamp: common.Big1, StateUpgradeAccounts: modifiedAccounts}, }, expectedError: "config block timestamp (1) <= previous timestamp (2)", }, + { + name: "upgrade block timestamp is zero", + upgrades: []StateUpgrade{ + {BlockTimestamp: common.Big0, StateUpgradeAccounts: modifiedAccounts}, + }, + expectedError: "config block timestamp (0) must be greater than 0", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {