Skip to content

Commit 86cb190

Browse files
committed
Add handshake codec
The goal of the this codec is to serialize `params.UpgradeConfig` in a deterministic way, to be hashed later. This hash is going to be share by nodes at handshake, so they can determine if they have the same upgrade config. This PR leverages the newly introduced support to serilize Maps ava-labs/avalanchego#1790, which are deterministic (the values are sorted by keys before serializing)
1 parent a439381 commit 86cb190

File tree

11 files changed

+218
-33
lines changed

11 files changed

+218
-33
lines changed

params/config.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,17 +444,17 @@ func (c *ChainConfig) Verify() error {
444444
return nil
445445
}
446446

447-
type fork struct {
448-
name string
449-
block *big.Int // some go-ethereum forks use block numbers
450-
timestamp *uint64 // Avalanche forks use timestamps
451-
optional bool // if true, the fork may be nil and next fork is still allowed
447+
type Fork struct {
448+
name string `serialize:"true"`
449+
block *big.Int `serialize:"true"` // some go-ethereum forks use block numbers
450+
timestamp *uint64 `serialize:"true"` // Avalanche forks use timestamps
451+
optional bool `serialize:"true"` // if true, the fork may be nil and next fork is still allowed
452452
}
453453

454454
// CheckConfigForkOrder checks that we don't "skip" any forks, geth isn't pluggable enough
455455
// to guarantee that forks can be implemented in a different order than on official networks
456456
func (c *ChainConfig) CheckConfigForkOrder() error {
457-
ethForks := []fork{
457+
ethForks := []Fork{
458458
{name: "homesteadBlock", block: c.HomesteadBlock},
459459
{name: "eip150Block", block: c.EIP150Block},
460460
{name: "eip155Block", block: c.EIP155Block},
@@ -493,8 +493,8 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
493493

494494
// checkForks checks that forks are enabled in order and returns an error if not
495495
// [blockFork] is true if the fork is a block number fork, false if it is a timestamp fork
496-
func checkForks(forks []fork, blockFork bool) error {
497-
lastFork := fork{}
496+
func checkForks(forks []Fork, blockFork bool) error {
497+
lastFork := Fork{}
498498
for _, cur := range forks {
499499
if blockFork && cur.block != nil && common.Big0.Cmp(cur.block) != 0 {
500500
return errNonGenesisForkByHeight

params/network_upgrades.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,21 @@ func (m *MandatoryNetworkUpgrades) CheckMandatoryCompatible(newcfg *MandatoryNet
5050
return nil
5151
}
5252

53-
func (m *MandatoryNetworkUpgrades) mandatoryForkOrder() []fork {
54-
return []fork{
53+
func (m *MandatoryNetworkUpgrades) mandatoryForkOrder() []Fork {
54+
return []Fork{
5555
{name: "subnetEVMTimestamp", timestamp: m.SubnetEVMTimestamp},
5656
{name: "dUpgradeTimestamp", timestamp: m.DUpgradeTimestamp},
5757
}
5858
}
5959

60-
// OptionalNetworkUpgrades includes overridable and optional Subnet-EVM network upgrades.
61-
// These can be specified in genesis and upgrade configs.
62-
// Timestamps can be different for each subnet network.
63-
// TODO: once we add the first optional upgrade here, we should uncomment TestVMUpgradeBytesOptionalNetworkUpgrades
64-
type OptionalNetworkUpgrades struct{}
60+
type OptionalNetworkUpgrades struct {
61+
Updates []Fork `json:"serialize,omitempty" serialize:"true"`
62+
}
6563

6664
func (n *OptionalNetworkUpgrades) CheckOptionalCompatible(newcfg *OptionalNetworkUpgrades, time uint64) *ConfigCompatError {
6765
return nil
6866
}
6967

70-
func (n *OptionalNetworkUpgrades) optionalForkOrder() []fork {
71-
return []fork{}
68+
func (n *OptionalNetworkUpgrades) optionalForkOrder() []Fork {
69+
return n.Updates
7270
}

params/state_upgrade.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ import (
1616
// StateUpgrade describes the modifications to be made to the state during
1717
// a state upgrade.
1818
type StateUpgrade struct {
19-
BlockTimestamp *uint64 `json:"blockTimestamp,omitempty"`
19+
BlockTimestamp *uint64 `json:"blockTimestamp,omitempty" serialize:"true"`
2020

2121
// map from account address to the modification to be made to the account.
22-
StateUpgradeAccounts map[common.Address]StateUpgradeAccount `json:"accounts"`
22+
StateUpgradeAccounts map[common.Address]StateUpgradeAccount `json:"accounts" serialize:"true"`
2323
}
2424

2525
// StateUpgradeAccount describes the modifications to be made to an account during
2626
// a state upgrade.
2727
type StateUpgradeAccount struct {
28-
Code hexutil.Bytes `json:"code,omitempty"`
29-
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
30-
BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"`
28+
Code hexutil.Bytes `json:"code,omitempty" serialize:"true"`
29+
Storage map[common.Hash]common.Hash `json:"storage,omitempty" serialize:"true"`
30+
BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty" serialize:"true"`
3131
}
3232

3333
func (s *StateUpgrade) Equal(other *StateUpgrade) bool {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// (c) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package handshake
5+
6+
import (
7+
"github.com/ava-labs/avalanchego/codec"
8+
"github.com/ava-labs/avalanchego/codec/linearcodec"
9+
"github.com/ava-labs/avalanchego/utils/units"
10+
"github.com/ava-labs/avalanchego/utils/wrappers"
11+
)
12+
13+
const (
14+
Version = uint16(0)
15+
maxMessageSize = 1 * units.MiB
16+
)
17+
18+
var (
19+
Codec codec.Manager
20+
)
21+
22+
func init() {
23+
Codec = codec.NewManager(maxMessageSize)
24+
c := linearcodec.NewDefault()
25+
26+
errs := wrappers.Errs{}
27+
errs.Add(
28+
c.RegisterType(UpgradeConfig{}),
29+
30+
Codec.RegisterCodec(Version, c),
31+
)
32+
33+
if errs.Errored() {
34+
panic(errs.Err)
35+
}
36+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package handshake
2+
3+
import (
4+
"crypto/sha256"
5+
"fmt"
6+
7+
"github.com/ava-labs/subnet-evm/params"
8+
"github.com/ava-labs/subnet-evm/precompile/modules"
9+
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
10+
)
11+
12+
type PrecompileUpgrade struct {
13+
StructName string `serialize:"true"`
14+
Bytes []byte `serialize:"true"`
15+
}
16+
17+
type UpgradeConfig struct {
18+
OptionalNetworkUpgrades []params.Fork `serialize:"true"`
19+
20+
// Config for modifying state as a network upgrade.
21+
StateUpgrades []params.StateUpgrade `serialize:"true"`
22+
23+
// Config for enabling and disabling precompiles as network upgrades.
24+
PrecompileUpgrades []PrecompileUpgrade `serialize:"true"`
25+
config params.UpgradeConfig
26+
bytes []byte
27+
}
28+
29+
func ParseUpgradeConfig(bytes []byte) (*UpgradeConfig, error) {
30+
var config UpgradeConfig
31+
version, err := Codec.Unmarshal(bytes, &config)
32+
if err != nil {
33+
return nil, err
34+
}
35+
if version != Version {
36+
return nil, fmt.Errorf("Invalid version")
37+
}
38+
39+
var PrecompileUpgrades []params.PrecompileUpgrade
40+
41+
for _, precompileUpgrade := range config.PrecompileUpgrades {
42+
module, ok := modules.GetPrecompileModule(precompileUpgrade.StructName)
43+
if !ok {
44+
return nil, fmt.Errorf("unknown precompile config: %s", precompileUpgrade.StructName)
45+
}
46+
preCompile := module.MakeConfig()
47+
48+
version, err := Codec.Unmarshal(precompileUpgrade.Bytes, preCompile)
49+
if version != Version {
50+
return nil, fmt.Errorf("Invalid version")
51+
}
52+
if err != nil {
53+
return nil, err
54+
}
55+
if Config, ok := preCompile.(precompileconfig.Config); ok {
56+
PrecompileUpgrades = append(PrecompileUpgrades, params.PrecompileUpgrade{Config: Config})
57+
} else {
58+
return nil, fmt.Errorf("Error deserializing precompile %s", precompileUpgrade.StructName)
59+
}
60+
}
61+
62+
config.config = params.UpgradeConfig{
63+
OptionalNetworkUpgrades: &params.OptionalNetworkUpgrades{config.OptionalNetworkUpgrades},
64+
StateUpgrades: config.StateUpgrades,
65+
PrecompileUpgrades: PrecompileUpgrades,
66+
}
67+
config.bytes = bytes
68+
69+
return &config, nil
70+
}
71+
72+
func NewUpgradeConfig(config params.UpgradeConfig) (*UpgradeConfig, error) {
73+
PrecompileUpgrades := make([]PrecompileUpgrade, 0)
74+
for _, precompileConfig := range config.PrecompileUpgrades {
75+
bytes, err := Codec.Marshal(Version, precompileConfig.Config)
76+
if err != nil {
77+
return nil, err
78+
}
79+
PrecompileUpgrades = append(PrecompileUpgrades, PrecompileUpgrade{
80+
StructName: precompileConfig.Key(),
81+
Bytes: bytes,
82+
})
83+
}
84+
85+
optionalNetworkUpgrades := make([]params.Fork, 0)
86+
if config.OptionalNetworkUpgrades != nil {
87+
optionalNetworkUpgrades = config.OptionalNetworkUpgrades.Updates
88+
}
89+
90+
wrappedConfig := UpgradeConfig{
91+
OptionalNetworkUpgrades: optionalNetworkUpgrades,
92+
StateUpgrades: config.StateUpgrades,
93+
PrecompileUpgrades: PrecompileUpgrades,
94+
config: config,
95+
bytes: make([]byte, 0),
96+
}
97+
bytes, err := Codec.Marshal(Version, wrappedConfig)
98+
if err != nil {
99+
return nil, err
100+
}
101+
wrappedConfig.bytes = bytes
102+
103+
return &wrappedConfig, nil
104+
}
105+
106+
func (r *UpgradeConfig) Config() params.UpgradeConfig {
107+
return r.config
108+
}
109+
110+
func (r *UpgradeConfig) Bytes() []byte {
111+
return r.bytes
112+
}
113+
114+
func (r *UpgradeConfig) Hash() [32]byte {
115+
return sha256.Sum256(r.bytes)
116+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package handshake
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ava-labs/subnet-evm/params"
7+
"github.com/ava-labs/subnet-evm/precompile/contracts/nativeminter"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestSerialize(t *testing.T) {
12+
var t0 uint64 = 0
13+
var t1 uint64 = 1
14+
config, err := NewUpgradeConfig(params.UpgradeConfig{
15+
PrecompileUpgrades: []params.PrecompileUpgrade{
16+
{
17+
Config: nativeminter.NewConfig(&t0, nil, nil, nil), // enable at genesis
18+
},
19+
{
20+
Config: nativeminter.NewDisableConfig(&t1), // disable at timestamp 1
21+
},
22+
},
23+
})
24+
assert.NoError(t, err)
25+
26+
config2, err := ParseUpgradeConfig(config.Bytes())
27+
assert.NoError(t, err)
28+
29+
config3, err := NewUpgradeConfig(config2.Config())
30+
assert.NoError(t, err)
31+
32+
assert.Equal(t, config2, config3)
33+
assert.Equal(t, config.Hash(), config2.Hash())
34+
assert.Equal(t, config.Hash(), config3.Hash())
35+
}

precompile/allowlist/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ var ErrCannotAddManagersBeforeDUpgrade = fmt.Errorf("cannot add managers before
1515

1616
// AllowListConfig specifies the initial set of addresses with Admin or Enabled roles.
1717
type AllowListConfig struct {
18-
AdminAddresses []common.Address `json:"adminAddresses,omitempty"` // initial admin addresses
19-
ManagerAddresses []common.Address `json:"managerAddresses,omitempty"` // initial manager addresses
20-
EnabledAddresses []common.Address `json:"enabledAddresses,omitempty"` // initial enabled addresses
18+
AdminAddresses []common.Address `json:"adminAddresses,omitempty" serialize:"true"` // initial admin addresses
19+
ManagerAddresses []common.Address `json:"managerAddresses,omitempty" serialize:"true"` // initial manager addresses
20+
EnabledAddresses []common.Address `json:"enabledAddresses,omitempty" serialize:"true"` // initial enabled addresses
2121
}
2222

2323
// Configure initializes the address space of [precompileAddr] by initializing the role of each of

precompile/contracts/feemanager/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var _ precompileconfig.Config = &Config{}
1717
type Config struct {
1818
allowlist.AllowListConfig // Config for the fee config manager allow list
1919
precompileconfig.Upgrade
20-
InitialFeeConfig *commontype.FeeConfig `json:"initialFeeConfig,omitempty"` // initial fee config to be immediately activated
20+
InitialFeeConfig *commontype.FeeConfig `json:"initialFeeConfig,omitempty" serialize:"true"` // initial fee config to be immediately activated
2121
}
2222

2323
// NewConfig returns a config for a network upgrade at [blockTimestamp] that enables

precompile/contracts/nativeminter/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ var _ precompileconfig.Config = &Config{}
1919
// Config implements the StatefulPrecompileConfig interface while adding in the
2020
// ContractNativeMinter specific precompile config.
2121
type Config struct {
22-
allowlist.AllowListConfig
23-
precompileconfig.Upgrade
24-
InitialMint map[common.Address]*math.HexOrDecimal256 `json:"initialMint,omitempty"` // addresses to receive the initial mint mapped to the amount to mint
22+
allowlist.AllowListConfig `serialize:"true"`
23+
precompileconfig.Upgrade `serialize:"true"`
24+
InitialMint map[common.Address]*math.HexOrDecimal256 `json:"initialMint,omitempty" serialize:"true"` // addresses to receive the initial mint mapped to the amount to mint
2525
}
2626

2727
// NewConfig returns a config for a network upgrade at [blockTimestamp] that enables

precompile/contracts/rewardmanager/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import (
1616
var _ precompileconfig.Config = &Config{}
1717

1818
type InitialRewardConfig struct {
19-
AllowFeeRecipients bool `json:"allowFeeRecipients"`
20-
RewardAddress common.Address `json:"rewardAddress,omitempty"`
19+
AllowFeeRecipients bool `json:"allowFeeRecipients" serialize:"true"`
20+
RewardAddress common.Address `json:"rewardAddress,omitempty" serialize:"true"`
2121
}
2222

2323
func (i *InitialRewardConfig) Equal(other *InitialRewardConfig) bool {

0 commit comments

Comments
 (0)