diff --git a/accounts/abi/bind/precompilebind/precompile_config_template.go b/accounts/abi/bind/precompilebind/precompile_config_template.go index 69c4fcbcc9..ffb6679d81 100644 --- a/accounts/abi/bind/precompilebind/precompile_config_template.go +++ b/accounts/abi/bind/precompilebind/precompile_config_template.go @@ -12,6 +12,9 @@ package {{.Package}} import ( "github.com/ava-labs/subnet-evm/precompile/precompileconfig" + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/docker/docker/pkg/units" + {{- if .Contract.AllowList}} "github.com/ava-labs/subnet-evm/precompile/allowlist" @@ -48,6 +51,50 @@ func NewConfig(blockTimestamp *uint64{{if .Contract.AllowList}}, admins []common } } +func (c * Config) MarshalBinary() ([]byte, error) { + p := wrappers.Packer { + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + {{- if .Contract.AllowList}} + allowBytes, err := c.AllowListConfig.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(allowBytes) + if p.Err != nil { + return nil, p.Err + } + {{- end}} + upgradeBytes, err := c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(upgradeBytes) + return p.Bytes, p.Err +} + +func (c * Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer { + Bytes: bytes, + } + {{- if .Contract.AllowList}} + allowList := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.AllowListConfig.UnmarshalBinary(allowList); err != nil { + return err + } + {{- end}} + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + + return c.Upgrade.UnmarshalBinary(upgrade) +} + // NewDisableConfig returns config for a network upgrade at [blockTimestamp] // that disables {{.Contract.Type}}. func NewDisableConfig(blockTimestamp *uint64) *Config { diff --git a/accounts/abi/bind/precompilebind/precompile_config_test_template.go b/accounts/abi/bind/precompilebind/precompile_config_test_template.go index 3c03732fd4..096e880468 100644 --- a/accounts/abi/bind/precompilebind/precompile_config_test_template.go +++ b/accounts/abi/bind/precompilebind/precompile_config_test_template.go @@ -13,6 +13,7 @@ package {{.Package}} import ( "testing" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/precompile/testutils" "github.com/ava-labs/subnet-evm/utils" @@ -22,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" {{- end}} "go.uber.org/mock/gomock" + "github.com/stretchr/testify/require" ) // TestVerify tests the verification of Config. @@ -61,6 +63,35 @@ func TestVerify(t *testing.T) { {{- end}} } +func TestSerialize(t *testing.T) { + var t0 uint64 = 2 + var t1 uint64 = 1001 + + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, + {{- if .Contract.AllowList}} + []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000030")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")), + }, + {{- end}} + ), + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + require.NotNil(t, config) + params.AssertConfigHashesAndSerialization(t, &config) +} + // TestEqual tests the equality of Config with other precompile configs. func TestEqual(t *testing.T) { {{- if .Contract.AllowList}} diff --git a/commontype/fee_config.go b/commontype/fee_config.go index 3089df5d9c..81b54a9a4a 100644 --- a/commontype/fee_config.go +++ b/commontype/fee_config.go @@ -7,7 +7,9 @@ import ( "fmt" "math/big" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/utils" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" ) @@ -144,6 +146,98 @@ func (f *FeeConfig) checkByteLens() error { return nil } +func (c *FeeConfig) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + if err := utils.PackBigInt(&p, c.GasLimit); err != nil { + return nil, err + } + + if err := utils.PackBigInt(&p, c.MinBaseFee); err != nil { + return nil, err + } + + if err := utils.PackBigInt(&p, c.TargetGas); err != nil { + return nil, err + } + + if err := utils.PackBigInt(&p, c.BaseFeeChangeDenominator); err != nil { + return nil, err + } + + if err := utils.PackBigInt(&p, c.MinBlockGasCost); err != nil { + return nil, err + } + + if err := utils.PackBigInt(&p, c.MaxBlockGasCost); err != nil { + return nil, err + } + + if err := utils.PackBigInt(&p, c.BlockGasCostStep); err != nil { + return nil, err + } + + p.PackLong(c.TargetBlockRate) + if p.Err != nil { + return nil, p.Err + } + + return p.Bytes, nil +} + +func (c *FeeConfig) UnmarshalBinary(data []byte) error { + p := wrappers.Packer{ + Bytes: data, + } + + var err error + + c.GasLimit, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.MinBaseFee, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.TargetGas, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.BaseFeeChangeDenominator, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.MinBlockGasCost, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.MaxBlockGasCost, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.BlockGasCostStep, err = utils.UnpackBigInt(&p) + if err != nil { + return err + } + + c.TargetBlockRate = p.UnpackLong() + if p.Err != nil { + return p.Err + } + + return nil +} + func isBiggerThanHashLen(bigint *big.Int) bool { buf := bigint.Bytes() isBigger := len(buf) > common.HashLength diff --git a/params/config.go b/params/config.go index c027badfba..327a6e9e95 100644 --- a/params/config.go +++ b/params/config.go @@ -31,13 +31,18 @@ import ( "errors" "fmt" "math/big" + "testing" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/precompile/modules" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/utils" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" ) const maxJSONLen = 64 * 1024 * 1024 // 64MB @@ -67,6 +72,10 @@ var ( MaxBlockGasCost: big.NewInt(1_000_000), BlockGasCostStep: big.NewInt(200_000), } + + // For UpgradeConfig Marshal/Unmarshal + ErrUnknowPrecompile = errors.New("unknown precompile config") + MaxMessageSize = 1 * units.MiB ) var ( @@ -171,6 +180,125 @@ type UpgradeConfig struct { PrecompileUpgrades []PrecompileUpgrade `json:"precompileUpgrades,omitempty"` } +func (c *UpgradeConfig) Hash() (common.Hash, error) { + bytes, err := c.MarshalBinary() + if err != nil { + return common.Hash{}, err + } + return crypto.Keccak256Hash(bytes), nil +} + +func (c *UpgradeConfig) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: MaxMessageSize, + } + + p.PackBool(c.OptionalNetworkUpgrades == nil) + if p.Err != nil { + return nil, p.Err + } + if c.OptionalNetworkUpgrades != nil { + bytes, err := c.OptionalNetworkUpgrades.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + } + + p.PackInt(uint32(len(c.PrecompileUpgrades))) + for _, precompileConfig := range c.PrecompileUpgrades { + bytes, err := precompileConfig.Config.MarshalBinary() + if err != nil { + return nil, err + } + p.PackStr(precompileConfig.Key()) + if p.Err != nil { + return nil, p.Err + } + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + } + + p.PackInt(uint32(len(c.StateUpgrades))) + for _, config := range c.StateUpgrades { + bytes, err := config.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + } + + return p.Bytes, nil +} + +func (c *UpgradeConfig) UnmarshalBinary(data []byte) error { + p := wrappers.Packer{ + Bytes: data, + } + isNil := p.UnpackBool() + if !isNil { + c.OptionalNetworkUpgrades = &OptionalNetworkUpgrades{} + bytes := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.OptionalNetworkUpgrades.UnmarshalBinary(bytes); err != nil { + return err + } + } + + len := p.UnpackInt() + if p.Err != nil { + return p.Err + } + + for i := uint32(0); i < len; i++ { + key := p.UnpackStr() + if p.Err != nil { + return p.Err + } + config := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + module, ok := modules.GetPrecompileModule(key) + if !ok { + return ErrUnknowPrecompile + } + preCompile := module.MakeConfig() + err := preCompile.UnmarshalBinary(config) + if err != nil { + return err + } + c.PrecompileUpgrades = append(c.PrecompileUpgrades, PrecompileUpgrade{Config: preCompile}) + } + + len = p.UnpackInt() + if p.Err != nil { + return p.Err + } + + for i := uint32(0); i < len; i++ { + config := p.UnpackBytes() + stateUpgrade := StateUpgrade{} + if err := stateUpgrade.UnmarshalBinary(config); err != nil { + return err + } + c.StateUpgrades = append(c.StateUpgrades, stateUpgrade) + } + + return nil +} + // AvalancheContext provides Avalanche specific context directly into the EVM. type AvalancheContext struct { SnowCtx *snow.Context @@ -865,3 +993,24 @@ func (c *ChainConfig) ToWithUpgradesJSON() *ChainConfigWithUpgradesJSON { UpgradeConfig: c.UpgradeConfig, } } + +// Take a config and serialize / deserialize it. +// +// `originalConfig` is a config given to this function. This function then will +// serialize it to bytes and create a new object from the bytes +// (`newConfigFromBytes`). +func AssertConfigHashesAndSerialization(t *testing.T, originalConfig *UpgradeConfig) { + bytes, err := originalConfig.MarshalBinary() + require.NoError(t, err) + + newConfigFromBytes := UpgradeConfig{} + require.NoError(t, newConfigFromBytes.UnmarshalBinary(bytes)) + + hash1, err := originalConfig.Hash() + require.NoError(t, err) + hash2, err := newConfigFromBytes.Hash() + require.NoError(t, err) + + //require.Equal(t, newConfigFromBytes, originalConfig) + require.Equal(t, hash1, hash2) +} diff --git a/params/config_test.go b/params/config_test.go index 066cc21192..7612e9dfce 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -328,3 +328,73 @@ func TestChainConfigMarshalWithUpgrades(t *testing.T) { require.NoError(t, err) require.Equal(t, config, unmarshalled) } + +func TestChainConfigMarshalWithUpgradesAndOptionalUpgrade(t *testing.T) { + config := ChainConfigWithUpgradesJSON{ + ChainConfig: ChainConfig{ + ChainID: big.NewInt(1), + FeeConfig: DefaultFeeConfig, + AllowFeeRecipients: false, + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + MandatoryNetworkUpgrades: MandatoryNetworkUpgrades{ + SubnetEVMTimestamp: utils.NewUint64(0), + }, + GenesisPrecompiles: Precompiles{}, + }, + UpgradeConfig: UpgradeConfig{ + PrecompileUpgrades: []PrecompileUpgrade{ + { + Config: txallowlist.NewConfig(utils.NewUint64(100), nil, nil, nil), + }, + }, + }, + } + result, err := json.Marshal(&config) + require.NoError(t, err) + expectedJSON := `{ + "chainId": 1, + "feeConfig": { + "gasLimit": 8000000, + "targetBlockRate": 2, + "minBaseFee": 25000000000, + "targetGas": 15000000, + "baseFeeChangeDenominator": 36, + "minBlockGasCost": 0, + "maxBlockGasCost": 1000000, + "blockGasCostStep": 200000 + }, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "subnetEVMTimestamp": 0, + "upgrades": { + "precompileUpgrades": [ + { + "txAllowListConfig": { + "blockTimestamp": 100 + } + } + ] + } + }` + require.JSONEq(t, expectedJSON, string(result)) + + var unmarshalled ChainConfigWithUpgradesJSON + err = json.Unmarshal(result, &unmarshalled) + require.NoError(t, err) + require.Equal(t, config, unmarshalled) +} diff --git a/params/network_upgrades.go b/params/network_upgrades.go index 71a52f0a02..59906f3444 100644 --- a/params/network_upgrades.go +++ b/params/network_upgrades.go @@ -4,6 +4,8 @@ package params import ( + "errors" + "github.com/ava-labs/subnet-evm/utils" ) @@ -64,16 +66,25 @@ func (m *MandatoryNetworkUpgrades) mandatoryForkOrder() []fork { } } -// OptionalNetworkUpgrades includes overridable and optional Subnet-EVM network upgrades. -// These can be specified in genesis and upgrade configs. -// Timestamps can be different for each subnet network. -// TODO: once we add the first optional upgrade here, we should uncomment TestVMUpgradeBytesOptionalNetworkUpgrades -type OptionalNetworkUpgrades struct{} +type OptionalNetworkUpgrades struct { + // This is an example of a configuration. + //FeatureConfig *uint64 `json:"test,omitempty"` +} + +func (m *OptionalNetworkUpgrades) MarshalBinary() ([]byte, error) { + return nil, errors.New("implement MarshalBinary() for OptionalNetworkUpgrades") +} + +func (m *OptionalNetworkUpgrades) UnmarshalBinary(data []byte) error { + return errors.New("implement UnmarshalBinary() for OptionalNetworkUpgrades") +} func (n *OptionalNetworkUpgrades) CheckOptionalCompatible(newcfg *OptionalNetworkUpgrades, time uint64) *ConfigCompatError { return nil } func (n *OptionalNetworkUpgrades) optionalForkOrder() []fork { - return []fork{} + return []fork{ + // {name: "foo", timestamp: n.FooBar}, + } } diff --git a/params/state_upgrade.go b/params/state_upgrade.go index 3041d656d4..06dd4a35b9 100644 --- a/params/state_upgrade.go +++ b/params/state_upgrade.go @@ -4,9 +4,14 @@ package params import ( + "bytes" "fmt" + "math/big" "reflect" + "sort" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -30,6 +35,155 @@ type StateUpgradeAccount struct { BalanceChange *math.HexOrDecimal256 `json:"balanceChange,omitempty"` } +func (s *StateUpgrade) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + + isNil := p.UnpackBool() + if p.Err != nil { + return p.Err + } + if !isNil { + blockTimestamp := p.UnpackLong() + if p.Err != nil { + return p.Err + } + s.BlockTimestamp = &blockTimestamp + } + + elements := p.UnpackInt() + if p.Err != nil { + return p.Err + } + s.StateUpgradeAccounts = make(map[common.Address]StateUpgradeAccount, elements) + for i := uint32(0); i < elements; i++ { + address := common.BytesToAddress(p.UnpackFixedBytes(common.AddressLength)) + if p.Err != nil { + return p.Err + } + + stateUpgradeAccount := StateUpgradeAccount{} + stateUpgradeAccount.Code = p.UnpackBytes() + if p.Err != nil { + return p.Err + } + isNil := p.UnpackBool() + if p.Err != nil { + return p.Err + } + if !isNil { + value := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + stateUpgradeAccount.BalanceChange = (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(value)) + } + + storageElements := p.UnpackInt() + if p.Err != nil { + return p.Err + } + storage := make(map[common.Hash]common.Hash, storageElements) + for e := uint32(0); e < storageElements; e++ { + key := common.BytesToHash(p.UnpackFixedBytes(common.HashLength)) + if p.Err != nil { + return p.Err + } + value := common.BytesToHash(p.UnpackFixedBytes(common.HashLength)) + if p.Err != nil { + return p.Err + } + storage[key] = value + } + + stateUpgradeAccount.Storage = storage + s.StateUpgradeAccounts[address] = stateUpgradeAccount + } + + return nil +} + +func (s *StateUpgrade) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + p.PackBool(s.BlockTimestamp == nil) + if p.Err != nil { + return nil, p.Err + } + if s.BlockTimestamp != nil { + p.PackLong(*s.BlockTimestamp) + if p.Err != nil { + return nil, p.Err + } + } + + p.PackInt(uint32(len(s.StateUpgradeAccounts))) + if p.Err != nil { + return nil, p.Err + } + + var addresses []common.Address + + for address := range s.StateUpgradeAccounts { + addresses = append(addresses, address) + } + + sort.Slice(addresses, func(i, j int) bool { + return bytes.Compare(addresses[i][:], addresses[j][:]) < 0 + }) + + for _, address := range addresses { + p.PackFixedBytes(address[:]) + if p.Err != nil { + return nil, p.Err + } + p.PackBytes(s.StateUpgradeAccounts[address].Code) + if p.Err != nil { + return nil, p.Err + } + p.PackBool(s.StateUpgradeAccounts[address].BalanceChange == nil) + if p.Err != nil { + return nil, p.Err + } + if s.StateUpgradeAccounts[address].BalanceChange != nil { + p.PackBytes((*big.Int)(s.StateUpgradeAccounts[address].BalanceChange).Bytes()) + if p.Err != nil { + return nil, p.Err + } + } + + p.PackInt(uint32(len(s.StateUpgradeAccounts[address].Storage))) + if p.Err != nil { + return nil, p.Err + } + + var hashes []common.Hash + for hash := range s.StateUpgradeAccounts[address].Storage { + hashes = append(hashes, hash) + } + sort.Slice(hashes, func(i, j int) bool { + return bytes.Compare(hashes[i][:], hashes[j][:]) < 0 + }) + + for _, hash := range hashes { + p.PackFixedBytes(hash[:]) + if p.Err != nil { + return nil, p.Err + } + bytes := s.StateUpgradeAccounts[address].Storage[hash] + p.PackFixedBytes(bytes[:]) + if p.Err != nil { + return nil, p.Err + } + } + } + + return p.Bytes, nil +} + func (s *StateUpgrade) Equal(other *StateUpgrade) bool { return reflect.DeepEqual(s, other) } diff --git a/params/state_upgrade_test.go b/params/state_upgrade_test.go index 6ee4094fc0..a3c2264535 100644 --- a/params/state_upgrade_test.go +++ b/params/state_upgrade_test.go @@ -185,3 +185,42 @@ func TestUnmarshalStateUpgradeJSON(t *testing.T) { require.NoError(t, err) require.Equal(t, upgradeConfig, unmarshaledConfig) } + +func TestSerialize(t *testing.T) { + var t0 uint64 = 2 + var t1 uint64 = 1001 + config := UpgradeConfig{ + StateUpgrades: []StateUpgrade{ + { + BlockTimestamp: &t0, + StateUpgradeAccounts: map[common.Address]StateUpgradeAccount{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")): StateUpgradeAccount{ + Code: []byte{1, 2, 3, 4, 5, 6}, + BalanceChange: math.NewHexOrDecimal256(99), + Storage: map[common.Hash]common.Hash{ + common.BytesToHash([]byte{1, 2, 4, 5}): common.BytesToHash([]byte{1, 2, 3}), + }, + }, + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001000")): StateUpgradeAccount{ + Code: []byte{1, 2, 9, 93, 4, 5, 6}, + BalanceChange: math.NewHexOrDecimal256(92312319), + Storage: map[common.Hash]common.Hash{ + common.BytesToHash([]byte{11, 21, 99, 5}): common.BytesToHash([]byte{1, 2, 3}), + common.BytesToHash([]byte{1, 21, 99, 5}): common.BytesToHash([]byte{1, 2, 3}), + common.BytesToHash([]byte{1, 2, 99, 5}): common.BytesToHash([]byte{1, 2, 3}), + common.BytesToHash([]byte{1, 2, 4, 5}): common.BytesToHash([]byte{1, 2, 3}), + }, + }, + }, + }, + { + BlockTimestamp: &t1, + StateUpgradeAccounts: map[common.Address]StateUpgradeAccount{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001000")): StateUpgradeAccount{}, + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")): StateUpgradeAccount{}, + }, + }, + }, + } + AssertConfigHashesAndSerialization(t, &config) +} diff --git a/precompile/allowlist/allowlist_test.go b/precompile/allowlist/allowlist_test.go index ebbcf6b69e..ced0700149 100644 --- a/precompile/allowlist/allowlist_test.go +++ b/precompile/allowlist/allowlist_test.go @@ -39,6 +39,14 @@ func (d *dummyConfig) Equal(cfg precompileconfig.Config) bool { return d.AllowListConfig.Equal(&other.AllowListConfig) } +func (*dummyConfig) MarshalBinary() ([]byte, error) { + return nil, nil +} + +func (*dummyConfig) UnmarshalBinary([]byte) error { + return nil +} + type dummyConfigurator struct{} func (d *dummyConfigurator) MakeConfig() precompileconfig.Config { diff --git a/precompile/allowlist/config.go b/precompile/allowlist/config.go index 520021f511..894aed4409 100644 --- a/precompile/allowlist/config.go +++ b/precompile/allowlist/config.go @@ -6,8 +6,11 @@ package allowlist import ( "fmt" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" + "github.com/ava-labs/subnet-evm/utils" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" ) @@ -110,3 +113,44 @@ func (c *AllowListConfig) Verify(chainConfig precompileconfig.ChainConfig, upgra return nil } + +func (c *AllowListConfig) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + if err := utils.PackAddresses(&p, c.AdminAddresses); err != nil { + return nil, err + } + if err := utils.PackAddresses(&p, c.ManagerAddresses); err != nil { + return nil, err + } + if err := utils.PackAddresses(&p, c.EnabledAddresses); err != nil { + return nil, err + } + return p.Bytes, nil +} + +func (c *AllowListConfig) UnmarshalBinary(data []byte) error { + p := &wrappers.Packer{ + Bytes: data, + } + admins, err := utils.UnpackAddresses(p) + if err != nil { + return err + } + managers, err := utils.UnpackAddresses(p) + if err != nil { + return err + } + enableds, err := utils.UnpackAddresses(p) + if err != nil { + return err + } + + c.AdminAddresses = admins + c.ManagerAddresses = managers + c.EnabledAddresses = enableds + + return nil +} diff --git a/precompile/contracts/deployerallowlist/config.go b/precompile/contracts/deployerallowlist/config.go index a588101dc3..288cc16de9 100644 --- a/precompile/contracts/deployerallowlist/config.go +++ b/precompile/contracts/deployerallowlist/config.go @@ -4,8 +4,10 @@ package deployerallowlist import ( + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" ) @@ -57,3 +59,55 @@ func (c *Config) Equal(cfg precompileconfig.Config) bool { func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error { return c.AllowListConfig.Verify(chainConfig, c.Upgrade) } + +func (c *Config) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + bytes, err := c.AllowListConfig.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + bytes, err = c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + return p.Bytes, nil +} + +func (c *Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + + allowList := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + + if err := c.AllowListConfig.UnmarshalBinary(allowList); err != nil { + return err + } + if err := c.Upgrade.UnmarshalBinary(upgrade); err != nil { + return err + } + return nil +} diff --git a/precompile/contracts/feemanager/config.go b/precompile/contracts/feemanager/config.go index 9dcfc307d2..77ed5802fd 100644 --- a/precompile/contracts/feemanager/config.go +++ b/precompile/contracts/feemanager/config.go @@ -4,9 +4,11 @@ package feemanager import ( + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" ) @@ -80,3 +82,81 @@ func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error { return c.InitialFeeConfig.Verify() } + +func (c *Config) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + allowList, err := c.AllowListConfig.MarshalBinary() + if err != nil { + return nil, err + } + + upgrade, err := c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(allowList) + if p.Err != nil { + return nil, p.Err + } + p.PackBytes(upgrade) + if p.Err != nil { + return nil, p.Err + } + + if c.InitialFeeConfig == nil { + p.PackBool(true) + if p.Err != nil { + return nil, p.Err + } + } else { + p.PackBool(false) + if p.Err != nil { + return nil, p.Err + } + bytes, err := c.InitialFeeConfig.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(bytes) + } + + return p.Bytes, p.Err +} + +func (c *Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + allowList := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.AllowListConfig.UnmarshalBinary(allowList); err != nil { + return err + } + if err := c.Upgrade.UnmarshalBinary(upgrade); err != nil { + return err + } + isNil := p.UnpackBool() + if !isNil { + c.InitialFeeConfig = &commontype.FeeConfig{} + bytes := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.InitialFeeConfig.UnmarshalBinary(bytes); err != nil { + return err + } + } + + return nil +} diff --git a/precompile/contracts/feemanager/config_test.go b/precompile/contracts/feemanager/config_test.go index 4182ec4716..4f187eba80 100644 --- a/precompile/contracts/feemanager/config_test.go +++ b/precompile/contracts/feemanager/config_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/precompile/testutils" @@ -88,3 +89,39 @@ func TestEqual(t *testing.T) { } allowlist.EqualPrecompileWithAllowListTests(t, Module, tests) } + +func TestSerialize(t *testing.T) { + var t0 uint64 = 2 + var t1 uint64 = 1001 + var validFeeConfig = commontype.FeeConfig{ + GasLimit: big.NewInt(8_000_000), + TargetBlockRate: 2, // in seconds + + MinBaseFee: big.NewInt(25_000_000_000), + TargetGas: big.NewInt(15_000_000), + BaseFeeChangeDenominator: big.NewInt(36), + + MinBlockGasCost: big.NewInt(0), + MaxBlockGasCost: big.NewInt(1_000_000), + BlockGasCostStep: big.NewInt(200_000), + } + + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000030")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")), + }, &validFeeConfig), // enable at genesis + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + params.AssertConfigHashesAndSerialization(t, &config) +} diff --git a/precompile/contracts/nativeminter/config.go b/precompile/contracts/nativeminter/config.go index 38a65ee6c8..bc474fb013 100644 --- a/precompile/contracts/nativeminter/config.go +++ b/precompile/contracts/nativeminter/config.go @@ -4,12 +4,16 @@ package nativeminter import ( + "bytes" "fmt" "math/big" + "sort" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/utils" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" ) @@ -98,3 +102,90 @@ func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error { } return c.AllowListConfig.Verify(chainConfig, c.Upgrade) } + +func (c *Config) MarshalBinary() ([]byte, error) { + keys := make([]common.Address, 0) + for key := range c.InitialMint { + keys = append(keys, key) + } + + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i][:], keys[j][:]) < 0 + }) + + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + bytes, err := c.AllowListConfig.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + bytes, err = c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + + p.PackInt(uint32(len(keys))) + if p.Err != nil { + return nil, p.Err + } + + for _, key := range keys { + p.PackFixedBytes(key[:]) + if p.Err != nil { + return nil, p.Err + } + p.PackBytes((*big.Int)(c.InitialMint[key]).Bytes()) + if p.Err != nil { + return nil, p.Err + } + } + + return p.Bytes, nil +} + +func (c *Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + allowList := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.AllowListConfig.UnmarshalBinary(allowList); err != nil { + return err + } + if err := c.Upgrade.UnmarshalBinary(upgrade); err != nil { + return err + } + len := p.UnpackInt() + c.InitialMint = make(map[common.Address]*math.HexOrDecimal256, len) + + for i := uint32(0); i < len; i++ { + key := common.BytesToAddress(p.UnpackFixedBytes(common.AddressLength)) + if p.Err != nil { + return p.Err + } + value := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + c.InitialMint[key] = (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(value)) + } + + return nil +} diff --git a/precompile/contracts/nativeminter/config_test.go b/precompile/contracts/nativeminter/config_test.go index ca0a63ce4a..d8c6c75397 100644 --- a/precompile/contracts/nativeminter/config_test.go +++ b/precompile/contracts/nativeminter/config_test.go @@ -6,6 +6,7 @@ package nativeminter import ( "testing" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/precompile/testutils" @@ -61,6 +62,70 @@ func TestVerify(t *testing.T) { allowlist.VerifyPrecompileWithAllowListTests(t, Module, tests) } +func TestSerialize(t *testing.T) { + var t0 uint64 = 0 + var t1 uint64 = 1 + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, nil, nil, nil, nil), // enable at genesis + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + params.AssertConfigHashesAndSerialization(t, &config) +} +func TestSerializeWithAddresses(t *testing.T) { + var t0 uint64 = 1 + var t1 uint64 = 11 + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000030")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")), + }, nil), // enable at genesis + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + params.AssertConfigHashesAndSerialization(t, &config) +} + +func TestSerializeWithAddressAndMint(t *testing.T) { + var t0 uint64 = 2 + var t1 uint64 = 1001 + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000030")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")), + }, map[common.Address]*math.HexOrDecimal256{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000010")): math.NewHexOrDecimal256(64), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000090")): math.NewHexOrDecimal256(6402100201021), + }), // enable at genesis + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + params.AssertConfigHashesAndSerialization(t, &config) +} + func TestEqual(t *testing.T) { admins := []common.Address{allowlist.TestAdminAddr} enableds := []common.Address{allowlist.TestEnabledAddr} diff --git a/precompile/contracts/rewardmanager/config.go b/precompile/contracts/rewardmanager/config.go index 49949cac1a..9bf53a0242 100644 --- a/precompile/contracts/rewardmanager/config.go +++ b/precompile/contracts/rewardmanager/config.go @@ -7,10 +7,11 @@ package rewardmanager import ( + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" - + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" ) @@ -21,6 +22,34 @@ type InitialRewardConfig struct { RewardAddress common.Address `json:"rewardAddress,omitempty"` } +func (u *InitialRewardConfig) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + p.PackBool(u.AllowFeeRecipients) + if p.Err != nil { + return nil, p.Err + } + p.PackFixedBytes(u.RewardAddress[:]) + return p.Bytes, p.Err +} + +func (u *InitialRewardConfig) UnmarshalBinary(data []byte) error { + p := wrappers.Packer{ + Bytes: data, + } + u.AllowFeeRecipients = p.UnpackBool() + if p.Err != nil { + return p.Err + } + u.RewardAddress = common.BytesToAddress(p.UnpackFixedBytes(common.AddressLength)) + if p.Err != nil { + return p.Err + } + return nil +} + func (i *InitialRewardConfig) Equal(other *InitialRewardConfig) bool { if other == nil { return false @@ -119,3 +148,76 @@ func (c *Config) Equal(cfg precompileconfig.Config) bool { return c.Upgrade.Equal(&other.Upgrade) && c.AllowListConfig.Equal(&other.AllowListConfig) } + +func (c *Config) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + bytes, err := c.AllowListConfig.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + bytes, err = c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + p.PackBool(c.InitialRewardConfig == nil) + if p.Err != nil { + return nil, p.Err + } + + if c.InitialRewardConfig != nil { + bytes, err := c.InitialRewardConfig.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(bytes) + } + + return p.Bytes, p.Err +} + +func (c *Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + allowList := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.AllowListConfig.UnmarshalBinary(allowList); err != nil { + return err + } + if err := c.Upgrade.UnmarshalBinary(upgrade); err != nil { + return err + } + + isNil := p.UnpackBool() + if p.Err == nil && !isNil { + c.InitialRewardConfig = &InitialRewardConfig{} + bytes := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + return c.InitialRewardConfig.UnmarshalBinary(bytes) + } + + return p.Err +} diff --git a/precompile/contracts/rewardmanager/config_test.go b/precompile/contracts/rewardmanager/config_test.go index 958eb000d9..a2b9768565 100644 --- a/precompile/contracts/rewardmanager/config_test.go +++ b/precompile/contracts/rewardmanager/config_test.go @@ -6,6 +6,7 @@ package rewardmanager import ( "testing" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/precompile/testutils" @@ -79,3 +80,53 @@ func TestEqual(t *testing.T) { } allowlist.EqualPrecompileWithAllowListTests(t, Module, tests) } + +func TestSerialize(t *testing.T) { + var t0 uint64 = 2 + var t1 uint64 = 1001 + + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000030")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")), + }, nil), // enable at genesis + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + params.AssertConfigHashesAndSerialization(t, &config) +} + +func TestSerializeWithNil(t *testing.T) { + var t0 uint64 = 2 + var t1 uint64 = 1001 + + config := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + { + Config: NewConfig(&t0, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")), + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000030")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")), + }, []common.Address{ + common.BytesToAddress(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000050")), + }, &InitialRewardConfig{ + AllowFeeRecipients: true, + }), + }, + { + Config: NewDisableConfig(&t1), // disable at timestamp 1 + }, + }, + } + params.AssertConfigHashesAndSerialization(t, &config) +} diff --git a/precompile/contracts/txallowlist/config.go b/precompile/contracts/txallowlist/config.go index f5656d9c78..89acb47af7 100644 --- a/precompile/contracts/txallowlist/config.go +++ b/precompile/contracts/txallowlist/config.go @@ -4,8 +4,10 @@ package txallowlist import ( + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" ) @@ -57,3 +59,48 @@ func (c *Config) Equal(cfg precompileconfig.Config) bool { func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error { return c.AllowListConfig.Verify(chainConfig, c.Upgrade) } + +func (c *Config) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + bytes, err := c.AllowListConfig.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + bytes, err = c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + + p.PackBytes(bytes) + + return p.Bytes, nil +} + +func (c *Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + + allowList := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.AllowListConfig.UnmarshalBinary(allowList); err != nil { + return err + } + return c.Upgrade.UnmarshalBinary(upgrade) +} diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 5275671e6a..6dddbb2bd9 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -8,11 +8,13 @@ import ( "errors" "fmt" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/predicate" warpValidators "github.com/ava-labs/subnet-evm/warp/validators" + "github.com/docker/docker/pkg/units" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/log" @@ -215,3 +217,43 @@ func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateCon return nil } + +func (c *Config) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + + bytes, err := c.Upgrade.MarshalBinary() + if err != nil { + return nil, err + } + p.PackBytes(bytes) + if p.Err != nil { + return nil, p.Err + } + + p.PackLong(c.QuorumNumerator) + if p.Err != nil { + return nil, p.Err + } + + return p.Bytes, nil +} + +func (c *Config) UnmarshalBinary(bytes []byte) error { + p := wrappers.Packer{ + Bytes: bytes, + } + upgrade := p.UnpackBytes() + if p.Err != nil { + return p.Err + } + if err := c.Upgrade.UnmarshalBinary(upgrade); err != nil { + return err + } + + c.QuorumNumerator = p.UnpackLong() + + return p.Err +} diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 05d204de45..a03e293f7f 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -5,6 +5,8 @@ package precompileconfig import ( + "encoding" + "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" @@ -17,6 +19,9 @@ import ( // StatefulPrecompileConfig defines the interface for a stateful precompile to // be enabled via a network upgrade. type Config interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + // Key returns the unique key for the stateful precompile. Key() string // Timestamp returns the timestamp at which this stateful precompile should be enabled. diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index 8d425daefa..bc980c5880 100644 --- a/precompile/precompileconfig/mocks.go +++ b/precompile/precompileconfig/mocks.go @@ -129,6 +129,21 @@ func (mr *MockConfigMockRecorder) Key() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Key", reflect.TypeOf((*MockConfig)(nil).Key)) } +// MarshalBinary mocks base method. +func (m *MockConfig) MarshalBinary() ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MarshalBinary") + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MarshalBinary indicates an expected call of MarshalBinary. +func (mr *MockConfigMockRecorder) MarshalBinary() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarshalBinary", reflect.TypeOf((*MockConfig)(nil).MarshalBinary)) +} + // Timestamp mocks base method. func (m *MockConfig) Timestamp() *uint64 { m.ctrl.T.Helper() @@ -143,6 +158,20 @@ func (mr *MockConfigMockRecorder) Timestamp() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Timestamp", reflect.TypeOf((*MockConfig)(nil).Timestamp)) } +// UnmarshalBinary mocks base method. +func (m *MockConfig) UnmarshalBinary(arg0 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnmarshalBinary", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnmarshalBinary indicates an expected call of UnmarshalBinary. +func (mr *MockConfigMockRecorder) UnmarshalBinary(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmarshalBinary", reflect.TypeOf((*MockConfig)(nil).UnmarshalBinary), arg0) +} + // Verify mocks base method. func (m *MockConfig) Verify(arg0 ChainConfig) error { m.ctrl.T.Helper() diff --git a/precompile/precompileconfig/upgradeable.go b/precompile/precompileconfig/upgradeable.go index f26bc37a33..a2e005592d 100644 --- a/precompile/precompileconfig/upgradeable.go +++ b/precompile/precompileconfig/upgradeable.go @@ -3,7 +3,11 @@ package precompileconfig -import "github.com/ava-labs/subnet-evm/utils" +import ( + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/subnet-evm/utils" + "github.com/docker/docker/pkg/units" +) // Upgrade contains the timestamp for the upgrade along with // a boolean [Disable]. If [Disable] is set, the upgrade deactivates @@ -31,3 +35,37 @@ func (u *Upgrade) Equal(other *Upgrade) bool { } return u.Disable == other.Disable && utils.Uint64PtrEqual(u.BlockTimestamp, other.BlockTimestamp) } + +func (u *Upgrade) MarshalBinary() ([]byte, error) { + p := wrappers.Packer{ + Bytes: []byte{}, + MaxSize: 1 * units.MiB, + } + if u.BlockTimestamp == nil { + p.PackBool(true) + } else { + p.PackBool(false) + if p.Err != nil { + return nil, p.Err + } + p.PackLong(*u.BlockTimestamp) + } + if p.Err != nil { + return nil, p.Err + } + p.PackBool(u.Disable) + return p.Bytes, p.Err +} + +func (u *Upgrade) UnmarshalBinary(data []byte) error { + p := wrappers.Packer{ + Bytes: data, + } + isNil := p.UnpackBool() + if !isNil { + timestamp := p.UnpackLong() + u.BlockTimestamp = ×tamp + } + u.Disable = p.UnpackBool() + return nil +} diff --git a/utils/bytes.go b/utils/bytes.go index 54258b20f4..18c20084c0 100644 --- a/utils/bytes.go +++ b/utils/bytes.go @@ -3,7 +3,14 @@ package utils -import "github.com/ethereum/go-ethereum/common" +import ( + "math/big" + + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ethereum/go-ethereum/common" +) + +const BigIntBytesLength = 32 // IncrOne increments bytes value by one func IncrOne(bytes []byte) { @@ -42,3 +49,62 @@ func BytesToHashSlice(b []byte) []common.Hash { } return hashes } + +func PackBigInt(p *wrappers.Packer, number *big.Int) error { + p.PackBool(number == nil) + if p.Err == nil && number != nil { + p.PackFixedBytes(number.FillBytes(make([]byte, BigIntBytesLength))) + } + + return p.Err +} + +func UnpackBigInt(p *wrappers.Packer) (*big.Int, error) { + isNil := p.UnpackBool() + if p.Err != nil || isNil { + return nil, p.Err + } + + number := big.NewInt(0).SetBytes(p.UnpackFixedBytes(BigIntBytesLength)) + return number, p.Err +} + +func PackAddresses(p *wrappers.Packer, addresses []common.Address) error { + p.PackBool(addresses == nil) + if addresses == nil { + return nil + } + p.PackInt(uint32(len(addresses))) + if p.Err != nil { + return p.Err + } + for _, address := range addresses { + p.PackFixedBytes(address[:]) + if p.Err != nil { + return p.Err + } + } + return nil +} + +func UnpackAddresses(p *wrappers.Packer) ([]common.Address, error) { + isNil := p.UnpackBool() + if isNil || p.Err != nil { + return nil, p.Err + } + length := p.UnpackInt() + if p.Err != nil { + return nil, p.Err + } + + addresses := make([]common.Address, 0, length) + for i := uint32(0); i < length; i++ { + bytes := p.UnpackFixedBytes(common.AddressLength) + addresses = append(addresses, common.BytesToAddress(bytes)) + if p.Err != nil { + return nil, p.Err + } + } + + return addresses, nil +}