-
Notifications
You must be signed in to change notification settings - Fork 276
state modifications as network upgrade #549
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
Changes from 1 commit
d6d999a
f8edd2c
e18dec2
0c1eedd
6afe5fc
2a59979
5c617bc
d02102f
a97ee33
7ea5d94
3a1cf82
6e921be
c6bb954
62760ee
bc3a8c7
fd25109
ca2c889
fae7aa3
3db8adc
5624d17
9ef281e
a111d22
2351e24
0c6d8c3
f6e6c1b
7dc8742
0f3dedf
923ecf7
f03b40e
eca4551
0cb03a2
281d3f6
95bb290
c53b094
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,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. | ||
ceyonur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if previousUpgradeTimestamp != nil && upgradeTimestamp.Cmp(previousUpgradeTimestamp) <= 0 { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
|
@@ -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 | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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"` | ||
ceyonur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| 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) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why did we remove these functions from precompile?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They were not used, so we should keep the interfaces narrower when possible.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, makes sense but what if future precompiles want to use these functions? I'm ok either way |
||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we could also move stateupgrade under precompile or reuse interfaces there but I think it is slightly better to keep them separate |
||
| 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) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.