Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

state modifications as network upgrade #549

Merged
merged 34 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d6d999a
state modifications as network upgrade
darioush Feb 28, 2023
f8edd2c
remove contract deploy
darioush Feb 28, 2023
e18dec2
Merge branch 'master' of github.com:ava-labs/subnet-evm into state-up…
darioush Feb 28, 2023
0c1eedd
use consolidated struct
darioush Feb 28, 2023
6afe5fc
add support for genesis and test
darioush Feb 28, 2023
2a59979
test improvements
darioush Feb 28, 2023
5c617bc
add configuration tests
darioush Feb 28, 2023
d02102f
nit
darioush Feb 28, 2023
a97ee33
lint
darioush Feb 28, 2023
7ea5d94
nit
darioush Feb 28, 2023
3a1cf82
make json compatible w/ genesis style
darioush Feb 28, 2023
6e921be
add support for neg. balance change
darioush Feb 28, 2023
c6bb954
avoid state upgrades in genesis
darioush 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
darioush Mar 1, 2023
5624d17
Merge branch 'master' of github.com:ava-labs/subnet-evm into state-up…
darioush Mar 1, 2023
9ef281e
start v0.4.12 release cycle
darioush Mar 2, 2023
a111d22
Merge branch 'start-release-cycle' of github.com:ava-labs/subnet-evm …
darioush Mar 2, 2023
2351e24
add setup action to lint ci for proper go version
darioush Mar 2, 2023
0c6d8c3
fix code interpretation as hex
darioush Mar 3, 2023
f6e6c1b
fix test
darioush Mar 3, 2023
7dc8742
bump lint action runner
darioush Mar 3, 2023
0f3dedf
add log
darioush Mar 3, 2023
923ecf7
add log
darioush Mar 3, 2023
f03b40e
add genesis code test case
darioush Mar 4, 2023
eca4551
fix typo
darioush Mar 4, 2023
0cb03a2
bump avalanchego version
darioush Mar 4, 2023
281d3f6
Merge branch 'master' of github.com:ava-labs/subnet-evm into state-up…
darioush Mar 7, 2023
95bb290
fix merge
darioush Mar 7, 2023
c53b094
add check for timestamp 0
darioush 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
38 changes: 32 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,30 @@ 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 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(), "upgrade", upgrade)
if err := stateupgrade.Configure(&upgrade, c, statedb, blockContext); err != nil {
return fmt.Errorf("could not configure state upgrade: %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:
// - 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 contract.BlockContext, statedb *state.StateDB) error {
if err := ApplyPrecompileActivations(c, parentTimestamp, blockContext, statedb); err != nil {
return err
}
return applyStateUpgrades(c, parentTimestamp, blockContext, statedb)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
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 @@ -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"`
}
Expand Down Expand Up @@ -325,7 +328,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 @@ -359,6 +362,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 @@ -493,6 +501,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 @@ -598,7 +611,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
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
80 changes: 75 additions & 5 deletions params/precompile_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,38 @@ 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 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 {
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 +208,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 +278,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
59 changes: 30 additions & 29 deletions params/precompile_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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 checkCompatible 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)
}
}
}
Loading