Skip to content

Commit 8b776d6

Browse files
Darioush Jalaliaaronbuchwald
andauthored
state modifications as network upgrade (#549)
Co-authored-by: Aaron Buchwald <aaron.buchwald56@gmail.com>
1 parent 694f922 commit 8b776d6

16 files changed

+615
-94
lines changed

core/state_processor.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/ava-labs/subnet-evm/params"
3838
"github.com/ava-labs/subnet-evm/precompile/contract"
3939
"github.com/ava-labs/subnet-evm/precompile/modules"
40+
"github.com/ava-labs/subnet-evm/stateupgrade"
4041
"github.com/ethereum/go-ethereum/common"
4142
"github.com/ethereum/go-ethereum/crypto"
4243
"github.com/ethereum/go-ethereum/log"
@@ -80,8 +81,8 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state
8081
timestamp = new(big.Int).SetUint64(header.Time)
8182
)
8283

83-
// Configure any stateful precompiles that should go into effect during this block.
84-
err := ApplyPrecompileActivations(p.config, new(big.Int).SetUint64(parent.Time), block, statedb)
84+
// Configure any upgrades that should go into effect during this block.
85+
err := ApplyUpgrades(p.config, new(big.Int).SetUint64(parent.Time), block, statedb)
8586
if err != nil {
8687
log.Error("failed to configure precompiles processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err)
8788
return nil, nil, 0, err
@@ -174,10 +175,8 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
174175
// ApplyPrecompileActivations checks if any of the precompiles specified by the chain config are enabled or disabled by the block
175176
// transition from [parentTimestamp] to the timestamp set in [blockContext]. If this is the case, it calls [Configure]
176177
// to apply the necessary state transitions for the upgrade.
177-
// This function is called:
178-
// - within genesis setup to configure the starting state for precompiles enabled at genesis,
179-
// - during block processing to update the state before processing the given block.
180-
// - during block producing to apply the precompile upgrades before producing the block.
178+
// This function is called within genesis setup to configure the starting state for precompiles enabled at genesis.
179+
// In block processing and building, ApplyUpgrades is called instead which also applies state upgrades.
181180
func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error {
182181
blockTimestamp := blockContext.Timestamp()
183182
// Note: RegisteredModules returns precompiles sorted by module addresses.
@@ -218,3 +217,30 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *big.Int,
218217
}
219218
return nil
220219
}
220+
221+
// applyStateUpgrades checks if any of the state upgrades specified by the chain config are activated by the block
222+
// transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure]
223+
// to apply the necessary state transitions for the upgrade.
224+
func applyStateUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error {
225+
// Apply state upgrades
226+
for _, upgrade := range c.GetActivatingStateUpgrades(parentTimestamp, blockContext.Timestamp(), c.StateUpgrades) {
227+
log.Info("Applying state upgrade", "blockNumber", blockContext.Number(), "upgrade", upgrade)
228+
if err := stateupgrade.Configure(&upgrade, c, statedb, blockContext); err != nil {
229+
return fmt.Errorf("could not configure state upgrade: %w", err)
230+
}
231+
}
232+
return nil
233+
}
234+
235+
// ApplyUpgrades checks if any of the precompile or state upgrades specified by the chain config are activated by the block
236+
// transition from [parentTimestamp] to the timestamp set in [header]. If this is the case, it calls [Configure]
237+
// to apply the necessary state transitions for the upgrade.
238+
// This function is called:
239+
// - in block processing to update the state when processing a block.
240+
// - in the miner to apply the state upgrades when producing a block.
241+
func ApplyUpgrades(c *params.ChainConfig, parentTimestamp *big.Int, blockContext contract.BlockContext, statedb *state.StateDB) error {
242+
if err := ApplyPrecompileActivations(c, parentTimestamp, blockContext, statedb); err != nil {
243+
return err
244+
}
245+
return applyStateUpgrades(c, parentTimestamp, blockContext, statedb)
246+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.19
44

55
require (
66
github.com/VictoriaMetrics/fastcache v1.10.0
7-
github.com/ava-labs/avalanchego v1.9.10
7+
github.com/ava-labs/avalanchego v1.9.11
88
github.com/cespare/cp v0.1.0
99
github.com/davecgh/go-spew v1.1.1
1010
github.com/deckarep/golang-set v1.8.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax
6161
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
6262
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
6363
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
64-
github.com/ava-labs/avalanchego v1.9.10 h1:IQYUruncY3yuKLwfbGXGslydTJQcjzLMtZuW8g5wQOY=
65-
github.com/ava-labs/avalanchego v1.9.10/go.mod h1:nNc+4JCIJMaEt2xRmeMVAUyQwDIap7RvnMrfWD2Tpo8=
64+
github.com/ava-labs/avalanchego v1.9.11 h1:5hXHJMvErfaolWD7Hw9gZaVylck2shBaV/2NTHA0BfA=
65+
github.com/ava-labs/avalanchego v1.9.11/go.mod h1:nNc+4JCIJMaEt2xRmeMVAUyQwDIap7RvnMrfWD2Tpo8=
6666
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
6767
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
6868
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=

miner/worker.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ func (w *worker) commitNewWork() (*types.Block, error) {
185185
if err != nil {
186186
return nil, fmt.Errorf("failed to create new current environment: %w", err)
187187
}
188-
// Configure any stateful precompiles that should go into effect during this block.
189-
err = core.ApplyPrecompileActivations(w.chainConfig, new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), env.state)
188+
// Configure any upgrades that should go into effect during this block.
189+
err = core.ApplyUpgrades(w.chainConfig, new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), env.state)
190190
if err != nil {
191191
log.Error("failed to configure precompiles mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err)
192192
return nil, err

params/config.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ type UpgradeConfig struct {
142142
// forks must be present or upgradeBytes will be rejected.
143143
NetworkUpgrades *NetworkUpgrades `json:"networkUpgrades,omitempty"`
144144

145+
// Config for modifying state as a network upgrade.
146+
StateUpgrades []StateUpgrade `json:"stateUpgrades,omitempty"`
147+
145148
// Config for enabling and disabling precompiles as network upgrades.
146149
PrecompileUpgrades []PrecompileUpgrade `json:"precompileUpgrades,omitempty"`
147150
}
@@ -325,7 +328,7 @@ func (c *ChainConfig) IsSubnetEVM(blockTimestamp *big.Int) bool {
325328

326329
// IsPrecompileEnabled returns whether precompile with [address] is enabled at [blockTimestamp].
327330
func (c *ChainConfig) IsPrecompileEnabled(address common.Address, blockTimestamp *big.Int) bool {
328-
config := c.GetActivePrecompileConfig(address, blockTimestamp)
331+
config := c.getActivePrecompileConfig(address, blockTimestamp)
329332
return config != nil && !config.IsDisabled()
330333
}
331334

@@ -359,6 +362,11 @@ func (c *ChainConfig) Verify() error {
359362
return fmt.Errorf("invalid precompile upgrades: %w", err)
360363
}
361364

365+
// Verify the state upgrades are internally consistent given the existing chainConfig.
366+
if err := c.verifyStateUpgrades(); err != nil {
367+
return fmt.Errorf("invalid state upgrades: %w", err)
368+
}
369+
362370
return nil
363371
}
364372

@@ -493,6 +501,11 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, lastHeight *big.Int,
493501
return err
494502
}
495503

504+
// Check that the state upgrades on the new config are compatible with the existing state upgrade config.
505+
if err := c.CheckStateUpgradesCompatible(newcfg.StateUpgrades, lastTimestamp); err != nil {
506+
return err
507+
}
508+
496509
// TODO verify that the fee config is fully compatible between [c] and [newcfg].
497510
return nil
498511
}
@@ -598,7 +611,7 @@ func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules {
598611
// Initialize the stateful precompiles that should be enabled at [blockTimestamp].
599612
rules.ActivePrecompiles = make(map[common.Address]precompileconfig.Config)
600613
for _, module := range modules.RegisteredModules() {
601-
if config := c.GetActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() {
614+
if config := c.getActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() {
602615
rules.ActivePrecompiles[module.Address] = config
603616
}
604617
}

params/precompile_config_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func TestVerifyPrecompileUpgrades(t *testing.T) {
179179
Config: txallowlist.NewDisableConfig(big.NewInt(1)),
180180
},
181181
},
182-
expectedError: " config block timestamp (1) <= previous timestamp (1) of same key",
182+
expectedError: "config block timestamp (1) <= previous timestamp (1) of same key",
183183
},
184184
}
185185
for _, tt := range tests {
@@ -269,16 +269,16 @@ func TestGetPrecompileConfig(t *testing.T) {
269269
deployerallowlist.ConfigKey: deployerallowlist.NewConfig(big.NewInt(10), nil, nil),
270270
}
271271

272-
deployerConfig := config.GetActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(0))
272+
deployerConfig := config.getActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(0))
273273
require.Nil(deployerConfig)
274274

275-
deployerConfig = config.GetActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(10))
275+
deployerConfig = config.getActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(10))
276276
require.NotNil(deployerConfig)
277277

278-
deployerConfig = config.GetActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(11))
278+
deployerConfig = config.getActivePrecompileConfig(deployerallowlist.ContractAddress, big.NewInt(11))
279279
require.NotNil(deployerConfig)
280280

281-
txAllowListConfig := config.GetActivePrecompileConfig(txallowlist.ContractAddress, big.NewInt(0))
281+
txAllowListConfig := config.getActivePrecompileConfig(txallowlist.ContractAddress, big.NewInt(0))
282282
require.Nil(txAllowListConfig)
283283
}
284284

params/precompile_upgrade.go

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,18 +149,38 @@ func (c *ChainConfig) verifyPrecompileUpgrades() error {
149149
return nil
150150
}
151151

152-
// GetActivePrecompileConfig returns the most recent precompile config corresponding to [address].
152+
// verifyStateUpgrades checks [c.StateUpgrades] is well formed:
153+
// - the specified blockTimestamps must monotonically increase
154+
func (c *ChainConfig) verifyStateUpgrades() error {
155+
var previousUpgradeTimestamp *big.Int
156+
for i, upgrade := range c.StateUpgrades {
157+
upgradeTimestamp := upgrade.BlockTimestamp
158+
// Verify the upgrade's timestamp is greater than 0 (to avoid confusion with genesis).
159+
if upgradeTimestamp.Cmp(common.Big0) <= 0 {
160+
return fmt.Errorf("StateUpgrade[%d]: config block timestamp (%v) must be greater than 0", i, upgradeTimestamp)
161+
}
162+
163+
// Verify specified timestamps are strictly monotonically increasing.
164+
if previousUpgradeTimestamp != nil && upgradeTimestamp.Cmp(previousUpgradeTimestamp) <= 0 {
165+
return fmt.Errorf("StateUpgrade[%d]: config block timestamp (%v) <= previous timestamp (%v)", i, upgradeTimestamp, previousUpgradeTimestamp)
166+
}
167+
previousUpgradeTimestamp = upgradeTimestamp
168+
}
169+
return nil
170+
}
171+
172+
// getActivePrecompileConfig returns the most recent precompile config corresponding to [address].
153173
// If none have occurred, returns nil.
154-
func (c *ChainConfig) GetActivePrecompileConfig(address common.Address, blockTimestamp *big.Int) precompileconfig.Config {
174+
func (c *ChainConfig) getActivePrecompileConfig(address common.Address, blockTimestamp *big.Int) precompileconfig.Config {
155175
configs := c.GetActivatingPrecompileConfigs(address, nil, blockTimestamp, c.PrecompileUpgrades)
156176
if len(configs) == 0 {
157177
return nil
158178
}
159179
return configs[len(configs)-1] // return the most recent config
160180
}
161181

162-
// GetActivatingPrecompileConfigs returns all upgrades configured to activate during the state transition from a block with timestamp [from]
163-
// to a block with timestamp [to].
182+
// GetActivatingPrecompileConfigs returns all precompile upgrades configured to activate during the
183+
// state transition from a block with timestamp [from] to a block with timestamp [to].
164184
func (c *ChainConfig) GetActivatingPrecompileConfigs(address common.Address, from *big.Int, to *big.Int, upgrades []PrecompileUpgrade) []precompileconfig.Config {
165185
// Get key from address.
166186
module, ok := modules.GetPrecompileModuleByAddress(address)
@@ -188,6 +208,18 @@ func (c *ChainConfig) GetActivatingPrecompileConfigs(address common.Address, fro
188208
return configs
189209
}
190210

211+
// GetActivatingStateUpgrades returns all state upgrades configured to activate during the
212+
// state transition from a block with timestamp [from] to a block with timestamp [to].
213+
func (c *ChainConfig) GetActivatingStateUpgrades(from *big.Int, to *big.Int, upgrades []StateUpgrade) []StateUpgrade {
214+
activating := make([]StateUpgrade, 0)
215+
for _, upgrade := range upgrades {
216+
if utils.IsForkTransition(upgrade.BlockTimestamp, from, to) {
217+
activating = append(activating, upgrade)
218+
}
219+
}
220+
return activating
221+
}
222+
191223
// CheckPrecompilesCompatible checks if [precompileUpgrades] are compatible with [c] at [headTimestamp].
192224
// Returns a ConfigCompatError if upgrades already activated at [headTimestamp] are missing from
193225
// [precompileUpgrades]. Upgrades not already activated may be modified or absent from [precompileUpgrades].
@@ -246,11 +278,49 @@ func (c *ChainConfig) checkPrecompileCompatible(address common.Address, precompi
246278
return nil
247279
}
248280

281+
// CheckStateUpgradesCompatible checks if [stateUpgrades] are compatible with [c] at [headTimestamp].
282+
func (c *ChainConfig) CheckStateUpgradesCompatible(stateUpgrades []StateUpgrade, lastTimestamp *big.Int) *ConfigCompatError {
283+
// All active upgrades (from nil to [lastTimestamp]) must match.
284+
activeUpgrades := c.GetActivatingStateUpgrades(nil, lastTimestamp, c.StateUpgrades)
285+
newUpgrades := c.GetActivatingStateUpgrades(nil, lastTimestamp, stateUpgrades)
286+
287+
// Check activated upgrades are still present.
288+
for i, upgrade := range activeUpgrades {
289+
if len(newUpgrades) <= i {
290+
// missing upgrade
291+
return newCompatError(
292+
fmt.Sprintf("missing StateUpgrade[%d]", i),
293+
upgrade.BlockTimestamp,
294+
nil,
295+
)
296+
}
297+
// All upgrades that have activated must be identical.
298+
if !upgrade.Equal(&newUpgrades[i]) {
299+
return newCompatError(
300+
fmt.Sprintf("StateUpgrade[%d]", i),
301+
upgrade.BlockTimestamp,
302+
newUpgrades[i].BlockTimestamp,
303+
)
304+
}
305+
}
306+
// then, make sure newUpgrades does not have additional upgrades
307+
// that are already activated. (cannot perform retroactive upgrade)
308+
if len(newUpgrades) > len(activeUpgrades) {
309+
return newCompatError(
310+
fmt.Sprintf("cannot retroactively enable StateUpgrade[%d]", len(activeUpgrades)),
311+
nil,
312+
newUpgrades[len(activeUpgrades)].BlockTimestamp, // this indexes to the first element in newUpgrades after the end of activeUpgrades
313+
)
314+
}
315+
316+
return nil
317+
}
318+
249319
// EnabledStatefulPrecompiles returns current stateful precompile configs that are enabled at [blockTimestamp].
250320
func (c *ChainConfig) EnabledStatefulPrecompiles(blockTimestamp *big.Int) Precompiles {
251321
statefulPrecompileConfigs := make(Precompiles)
252322
for _, module := range modules.RegisteredModules() {
253-
if config := c.GetActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() {
323+
if config := c.getActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() {
254324
statefulPrecompileConfigs[module.ConfigKey] = config
255325
}
256326
}

params/precompile_upgrade_test.go

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,7 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) {
7878
deployerallowlist.ConfigKey: deployerallowlist.NewConfig(big.NewInt(10), admins, nil),
7979
}
8080

81-
type test struct {
82-
configs []*UpgradeConfig
83-
startTimestamps []*big.Int
84-
expectedErrorString string
85-
}
86-
87-
tests := map[string]test{
81+
tests := map[string]upgradeCompatibilityTest{
8882
"disable and re-enable": {
8983
startTimestamps: []*big.Int{big.NewInt(5)},
9084
configs: []*UpgradeConfig{
@@ -252,32 +246,39 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) {
252246

253247
for name, tt := range tests {
254248
t.Run(name, func(t *testing.T) {
255-
// make a local copy of the chainConfig
256-
chainConfig := chainConfig
249+
tt.run(t, chainConfig)
250+
})
251+
}
252+
}
257253

258-
// apply all the upgrade bytes specified in order
259-
for i, upgrade := range tt.configs {
260-
newCfg := chainConfig
261-
newCfg.UpgradeConfig = *upgrade
254+
type upgradeCompatibilityTest struct {
255+
configs []*UpgradeConfig
256+
startTimestamps []*big.Int
257+
expectedErrorString string
258+
}
262259

263-
err := chainConfig.checkCompatible(&newCfg, nil, tt.startTimestamps[i])
260+
func (tt *upgradeCompatibilityTest) run(t *testing.T, chainConfig ChainConfig) {
261+
// apply all the upgrade bytes specified in order
262+
for i, upgrade := range tt.configs {
263+
newCfg := chainConfig
264+
newCfg.UpgradeConfig = *upgrade
264265

265-
// if this is not the final upgradeBytes, continue applying
266-
// the next upgradeBytes. (only check the result on the last apply)
267-
if i != len(tt.configs)-1 {
268-
if err != nil {
269-
t.Fatalf("expecting ApplyUpgradeBytes call %d to return nil, got %s", i+1, err)
270-
}
271-
chainConfig = newCfg
272-
continue
273-
}
266+
err := chainConfig.checkCompatible(&newCfg, nil, tt.startTimestamps[i])
274267

275-
if tt.expectedErrorString != "" {
276-
require.ErrorContains(t, err, tt.expectedErrorString)
277-
} else {
278-
require.Nil(t, err)
279-
}
268+
// if this is not the final upgradeBytes, continue applying
269+
// the next upgradeBytes. (only check the result on the last apply)
270+
if i != len(tt.configs)-1 {
271+
if err != nil {
272+
t.Fatalf("expecting checkCompatible call %d to return nil, got %s", i+1, err)
280273
}
281-
})
274+
chainConfig = newCfg
275+
continue
276+
}
277+
278+
if tt.expectedErrorString != "" {
279+
require.ErrorContains(t, err, tt.expectedErrorString)
280+
} else {
281+
require.Nil(t, err)
282+
}
282283
}
283284
}

0 commit comments

Comments
 (0)