Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d6d999a
state modifications as network upgrade
Feb 28, 2023
f8edd2c
remove contract deploy
Feb 28, 2023
e18dec2
Merge branch 'master' of github.com:ava-labs/subnet-evm into state-up…
Feb 28, 2023
0c1eedd
use consolidated struct
Feb 28, 2023
6afe5fc
add support for genesis and test
Feb 28, 2023
2a59979
test improvements
Feb 28, 2023
5c617bc
add configuration tests
Feb 28, 2023
d02102f
nit
Feb 28, 2023
a97ee33
lint
Feb 28, 2023
7ea5d94
nit
Feb 28, 2023
3a1cf82
make json compatible w/ genesis style
Feb 28, 2023
6e921be
add support for neg. balance change
Feb 28, 2023
c6bb954
avoid state upgrades in genesis
Mar 1, 2023
62760ee
Revert apply precompile func name change
aaronbuchwald Mar 1, 2023
bc3a8c7
Update comment
aaronbuchwald Mar 1, 2023
fd25109
Add state upgrade test case
aaronbuchwald Mar 1, 2023
ca2c889
Cleanup
aaronbuchwald Mar 1, 2023
fae7aa3
remove codegen comment
aaronbuchwald Mar 1, 2023
3db8adc
add EIP158 check
Mar 1, 2023
5624d17
Merge branch 'master' of github.com:ava-labs/subnet-evm into state-up…
Mar 1, 2023
9ef281e
start v0.4.12 release cycle
Mar 2, 2023
a111d22
Merge branch 'start-release-cycle' of github.com:ava-labs/subnet-evm …
Mar 2, 2023
2351e24
add setup action to lint ci for proper go version
Mar 2, 2023
0c6d8c3
fix code interpretation as hex
Mar 3, 2023
f6e6c1b
fix test
Mar 3, 2023
7dc8742
bump lint action runner
Mar 3, 2023
0f3dedf
add log
Mar 3, 2023
923ecf7
add log
Mar 3, 2023
f03b40e
add genesis code test case
Mar 4, 2023
eca4551
fix typo
Mar 4, 2023
0cb03a2
bump avalanchego version
Mar 4, 2023
281d3f6
Merge branch 'master' of github.com:ava-labs/subnet-evm into state-up…
Mar 7, 2023
95bb290
fix merge
Mar 7, 2023
c53b094
add check for timestamp 0
Mar 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -80,8 +81,8 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state
timestamp = new(big.Int).SetUint64(header.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)
// 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
Expand Down Expand Up @@ -174,10 +175,8 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// 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,
// - during block processing to update the state before processing the given block.
// - during block producing to apply the precompile upgrades before producing the block.
// 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 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.
Expand Down Expand Up @@ -218,3 +217,29 @@ 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.
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 {
return fmt.Errorf("could not configure state upgrade, reason: %w", err)
}
}
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 := ApplyPrecompileActivations(c, parentTimestamp, blockContext, statedb); err != nil {
return err
}
return applyStateUpgrades(c, parentTimestamp, blockContext, statedb)
}
4 changes: 2 additions & 2 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ 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.
err = core.ApplyPrecompileActivations(w.chainConfig, new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), 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
Expand Down
17 changes: 15 additions & 2 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
}
Expand Down
58 changes: 58 additions & 0 deletions params/gen_state_upgrade_account.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions params/precompile_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}

Expand Down
78 changes: 71 additions & 7 deletions params/precompile_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -149,18 +148,33 @@ 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we decided not to use state upgrades at genesis, should we check if timestamp != 0? cc @aaronbuchwald

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I think we should add this check since we are not allowing them in the genesis

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
}
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)
Expand Down Expand Up @@ -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].
Expand Down Expand Up @@ -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
}
}
Expand Down
Loading