code.vegaprotocol.io/vega@v0.79.0/core/types/ethereum.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package types
    17  
    18  import (
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"time"
    23  
    24  	vgreflect "code.vegaprotocol.io/vega/libs/reflect"
    25  	proto "code.vegaprotocol.io/vega/protos/vega"
    26  
    27  	ethcmn "github.com/ethereum/go-ethereum/common"
    28  )
    29  
    30  var (
    31  	ErrMissingNetworkID                                   = errors.New("missing network ID in Ethereum config")
    32  	ErrMissingChainID                                     = errors.New("missing chain ID in Ethereum config")
    33  	ErrMissingCollateralBridgeAddress                     = errors.New("missing collateral bridge contract address in Ethereum config")
    34  	ErrMissingMultiSigControlAddress                      = errors.New("missing multisig control contract address in Ethereum config")
    35  	ErrUnsupportedCollateralBridgeDeploymentBlockHeight   = errors.New("setting collateral bridge contract deployment block height in Ethereum config is not supported")
    36  	ErrAtLeastOneOfStakingOrVestingBridgeAddressMustBeSet = errors.New("at least one of the stacking bridge or token vesting contract addresses must be specified")
    37  	ErrConfirmationsMustBeHigherThan0                     = errors.New("confirmation must be > 0 in Ethereum config")
    38  	ErrBlockIntervalMustBeHigherThan0                     = errors.New("block interval must be > 0 in Ethereum config")
    39  	ErrMissingNetworkName                                 = errors.New("missing network name")
    40  	ErrDuplicateNetworkName                               = errors.New("duplicate network name")
    41  	ErrDuplicateNetworkID                                 = errors.New("duplicate network ID name")
    42  	ErrDuplicateChainID                                   = errors.New("duplicate chain ID name")
    43  	ErrCannotRemoveL2Config                               = errors.New("L2 config cannot be removed")
    44  	ErrCanOnlyAmendedConfirmationsAndBlockInterval        = errors.New("can only amended L2 config confirmations and block interval")
    45  	ErrInvalidBlockLengthDuration                         = errors.New("block-length duration is invalid")
    46  )
    47  
    48  type EthereumConfig struct {
    49  	chainID          string
    50  	networkID        string
    51  	confirmations    uint64
    52  	collateralBridge EthereumContract
    53  	multiSigControl  EthereumContract
    54  	stakingBridge    EthereumContract
    55  	vestingBridge    EthereumContract
    56  	blockTime        time.Duration
    57  }
    58  
    59  func EthereumConfigFromUntypedProto(v interface{}) (*EthereumConfig, error) {
    60  	cfg, err := toEthereumConfigProto(v)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("couldn't convert untyped proto to EthereumConfig proto: %w", err)
    63  	}
    64  
    65  	ethConfig, err := EthereumConfigFromProto(cfg)
    66  	if err != nil {
    67  		return nil, fmt.Errorf("couldn't build EthereumConfig: %w", err)
    68  	}
    69  
    70  	return ethConfig, nil
    71  }
    72  
    73  func EthereumConfigFromProto(cfgProto *proto.EthereumConfig) (*EthereumConfig, error) {
    74  	if err := CheckEthereumConfig(cfgProto); err != nil {
    75  		return nil, fmt.Errorf("invalid Ethereum configuration: %w", err)
    76  	}
    77  
    78  	cfg := &EthereumConfig{
    79  		chainID:       cfgProto.ChainId,
    80  		networkID:     cfgProto.NetworkId,
    81  		confirmations: uint64(cfgProto.Confirmations),
    82  		collateralBridge: EthereumContract{
    83  			address: cfgProto.CollateralBridgeContract.Address,
    84  		},
    85  		multiSigControl: EthereumContract{
    86  			address:               cfgProto.MultisigControlContract.Address,
    87  			deploymentBlockHeight: cfgProto.MultisigControlContract.DeploymentBlockHeight,
    88  		},
    89  		blockTime: 12 * time.Second,
    90  	}
    91  
    92  	if cfgProto.StakingBridgeContract != nil {
    93  		cfg.stakingBridge = EthereumContract{
    94  			address:               cfgProto.StakingBridgeContract.Address,
    95  			deploymentBlockHeight: cfgProto.StakingBridgeContract.DeploymentBlockHeight,
    96  		}
    97  	}
    98  
    99  	if cfgProto.TokenVestingContract != nil {
   100  		cfg.vestingBridge = EthereumContract{
   101  			address:               cfgProto.TokenVestingContract.Address,
   102  			deploymentBlockHeight: cfgProto.TokenVestingContract.DeploymentBlockHeight,
   103  		}
   104  	}
   105  
   106  	if len(cfgProto.BlockTime) != 0 {
   107  		bl, err := time.ParseDuration(cfgProto.BlockTime)
   108  		if err != nil {
   109  			return nil, fmt.Errorf("invalid Ethereum chain configuration, block_length: %w", err)
   110  		}
   111  		cfg.blockTime = bl
   112  	}
   113  
   114  	return cfg, nil
   115  }
   116  
   117  func (c *EthereumConfig) ChainID() string {
   118  	return c.chainID
   119  }
   120  
   121  func (c *EthereumConfig) NetworkID() string {
   122  	return c.networkID
   123  }
   124  
   125  func (c *EthereumConfig) Confirmations() uint64 {
   126  	return c.confirmations
   127  }
   128  
   129  func (c *EthereumConfig) CollateralBridge() EthereumContract {
   130  	return c.collateralBridge
   131  }
   132  
   133  func (c *EthereumConfig) MultiSigControl() EthereumContract {
   134  	return c.multiSigControl
   135  }
   136  
   137  func (c *EthereumConfig) StakingBridge() EthereumContract {
   138  	return c.stakingBridge
   139  }
   140  
   141  func (c *EthereumConfig) VestingBridge() EthereumContract {
   142  	return c.vestingBridge
   143  }
   144  
   145  func (c *EthereumConfig) BlockTime() time.Duration {
   146  	return c.blockTime
   147  }
   148  
   149  // StakingBridgeAddresses returns the registered staking bridge addresses. It
   150  // might return the staking bridge, or the token vesting, or both contract
   151  // address. The vesting contract can also be used to get information needed by
   152  // the staking engine.
   153  func (c *EthereumConfig) StakingBridgeAddresses() []ethcmn.Address {
   154  	var addresses []ethcmn.Address
   155  
   156  	if c.stakingBridge.HasAddress() {
   157  		addresses = append(addresses, c.stakingBridge.Address())
   158  	}
   159  	if c.vestingBridge.HasAddress() {
   160  		addresses = append(addresses, c.vestingBridge.Address())
   161  	}
   162  
   163  	return addresses
   164  }
   165  
   166  type EthereumContract struct {
   167  	address               string
   168  	deploymentBlockHeight uint64
   169  }
   170  
   171  func (c EthereumContract) DeploymentBlockHeight() uint64 {
   172  	return c.deploymentBlockHeight
   173  }
   174  
   175  func (c EthereumContract) HasAddress() bool {
   176  	return len(c.address) > 0
   177  }
   178  
   179  func (c EthereumContract) Address() ethcmn.Address {
   180  	return ethcmn.HexToAddress(c.address)
   181  }
   182  
   183  func (c EthereumContract) HexAddress() string {
   184  	return c.address
   185  }
   186  
   187  // CheckUntypedEthereumConfig verifies the `v` parameter is a proto.EthereumConfig
   188  // struct and check if it's valid.
   189  func CheckUntypedEthereumConfig(v interface{}, _ interface{}) error {
   190  	cfg, err := toEthereumConfigProto(v)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	return CheckEthereumConfig(cfg)
   196  }
   197  
   198  func CheckUntypedEthereumL2Configs(v interface{}, o interface{}) error {
   199  	cfg, err := toEthereumL2ConfigsProto(v)
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	ocfg := &proto.EthereumL2Configs{}
   205  	json.Unmarshal([]byte(o.(string)), ocfg)
   206  	return CheckEthereumL2Configs(cfg, ocfg)
   207  }
   208  
   209  // CheckEthereumConfig verifies the proto.EthereumConfig is valid.
   210  func CheckEthereumConfig(cfgProto *proto.EthereumConfig) error {
   211  	if len(cfgProto.NetworkId) == 0 {
   212  		return ErrMissingNetworkID
   213  	}
   214  
   215  	if len(cfgProto.ChainId) == 0 {
   216  		return ErrMissingChainID
   217  	}
   218  
   219  	if cfgProto.Confirmations == 0 {
   220  		return ErrConfirmationsMustBeHigherThan0
   221  	}
   222  
   223  	noMultiSigControlSetUp := cfgProto.MultisigControlContract == nil || len(cfgProto.MultisigControlContract.Address) == 0
   224  	if noMultiSigControlSetUp {
   225  		return ErrMissingMultiSigControlAddress
   226  	}
   227  
   228  	noCollateralBridgeSetUp := cfgProto.CollateralBridgeContract == nil || len(cfgProto.CollateralBridgeContract.Address) == 0
   229  	if noCollateralBridgeSetUp {
   230  		return ErrMissingCollateralBridgeAddress
   231  	}
   232  	if cfgProto.CollateralBridgeContract.DeploymentBlockHeight != 0 {
   233  		return ErrUnsupportedCollateralBridgeDeploymentBlockHeight
   234  	}
   235  
   236  	noStakingBridgeSetUp := cfgProto.StakingBridgeContract == nil || len(cfgProto.StakingBridgeContract.Address) == 0
   237  	noVestingBridgeSetUp := cfgProto.TokenVestingContract == nil || len(cfgProto.TokenVestingContract.Address) == 0
   238  	if noStakingBridgeSetUp && noVestingBridgeSetUp {
   239  		return ErrAtLeastOneOfStakingOrVestingBridgeAddressMustBeSet
   240  	}
   241  
   242  	if len(cfgProto.BlockTime) != 0 {
   243  		_, err := time.ParseDuration(cfgProto.BlockTime)
   244  		if err != nil {
   245  			return ErrInvalidBlockLengthDuration
   246  		}
   247  	}
   248  
   249  	return nil
   250  }
   251  
   252  func toEthereumConfigProto(v interface{}) (*proto.EthereumConfig, error) {
   253  	cfg, ok := v.(*proto.EthereumConfig)
   254  	if !ok {
   255  		return nil, fmt.Errorf("type %q is not a EthereumConfig proto", vgreflect.TypeName(v))
   256  	}
   257  	return cfg, nil
   258  }
   259  
   260  type EthereumL2Configs struct {
   261  	Configs []EthereumL2Config
   262  }
   263  
   264  type EthereumL2Config struct {
   265  	ChainID       string
   266  	NetworkID     string
   267  	Confirmations uint64
   268  	Name          string
   269  	BlockInterval uint64
   270  }
   271  
   272  func toEthereumL2ConfigsProto(v interface{}) (*proto.EthereumL2Configs, error) {
   273  	cfg, ok := v.(*proto.EthereumL2Configs)
   274  	if !ok {
   275  		return nil, fmt.Errorf("type %q is not a EthereumL2Configs proto", vgreflect.TypeName(v))
   276  	}
   277  	return cfg, nil
   278  }
   279  
   280  func EthereumL2ConfigsFromUntypedProto(v interface{}) (*EthereumL2Configs, error) {
   281  	cfg, err := toEthereumL2ConfigsProto(v)
   282  	if err != nil {
   283  		return nil, fmt.Errorf("couldn't convert untyped proto to EthereumL2Configs proto: %w", err)
   284  	}
   285  
   286  	ethConfig, err := EthereumL2ConfigsFromProto(cfg)
   287  	if err != nil {
   288  		return nil, fmt.Errorf("couldn't build EthereumL2Configs: %w", err)
   289  	}
   290  
   291  	return ethConfig, nil
   292  }
   293  
   294  func EthereumL2ConfigsFromProto(cfgProto *proto.EthereumL2Configs) (*EthereumL2Configs, error) {
   295  	if err := CheckEthereumL2Configs(cfgProto, nil); err != nil {
   296  		return nil, fmt.Errorf("invalid Ethereum configuration: %w", err)
   297  	}
   298  
   299  	cfg := &EthereumL2Configs{}
   300  	for _, v := range cfgProto.Configs {
   301  		cfg.Configs = append(cfg.Configs, EthereumL2Config{
   302  			NetworkID:     v.NetworkId,
   303  			ChainID:       v.ChainId,
   304  			Name:          v.Name,
   305  			Confirmations: uint64(v.Confirmations),
   306  			BlockInterval: v.BlockInterval,
   307  		})
   308  	}
   309  
   310  	return cfg, nil
   311  }
   312  
   313  // CheckEthereumConfig verifies the proto.EthereumConfig is valid.
   314  func CheckEthereumL2Configs(cfgProto *proto.EthereumL2Configs, prev *proto.EthereumL2Configs) error {
   315  	names := map[string]*proto.EthereumL2Config{}
   316  	cids := map[string]*proto.EthereumL2Config{}
   317  	nids := map[string]*proto.EthereumL2Config{}
   318  
   319  	for _, v := range cfgProto.Configs {
   320  		// check network id and ensure no duplicates
   321  		if len(v.NetworkId) == 0 {
   322  			return ErrMissingNetworkID
   323  		}
   324  		if _, ok := nids[v.NetworkId]; ok {
   325  			return ErrDuplicateNetworkID
   326  		}
   327  		nids[v.NetworkId] = v
   328  
   329  		// check chain id and ensure no duplicates
   330  		if len(v.ChainId) == 0 {
   331  			return ErrMissingChainID
   332  		}
   333  		if _, ok := cids[v.ChainId]; ok {
   334  			return ErrDuplicateChainID
   335  		}
   336  		cids[v.ChainId] = v
   337  
   338  		// check network name and ensure no duplicates
   339  		if len(v.Name) == 0 {
   340  			return ErrMissingNetworkName
   341  		}
   342  		if _, ok := names[v.Name]; ok {
   343  			return ErrDuplicateNetworkName
   344  		}
   345  		names[v.Name] = v
   346  
   347  		if v.Confirmations == 0 {
   348  			return ErrConfirmationsMustBeHigherThan0
   349  		}
   350  
   351  		if v.BlockInterval == 0 {
   352  			return ErrBlockIntervalMustBeHigherThan0
   353  		}
   354  	}
   355  
   356  	// it wasn't previously set to anything (from genesis) so nothing to check
   357  	if prev == nil {
   358  		return nil
   359  	}
   360  
   361  	// compare against currently set configs - we make sure they only amend confirmations, or are new additions
   362  	// but for now nothing can bre removed.
   363  	for _, c := range prev.Configs {
   364  		v, ok := nids[c.NetworkId]
   365  		if !ok {
   366  			return ErrCannotRemoveL2Config
   367  		}
   368  
   369  		if !isUpdate(v, c) {
   370  			return ErrCanOnlyAmendedConfirmationsAndBlockInterval
   371  		}
   372  	}
   373  
   374  	return nil
   375  }
   376  
   377  func isUpdate(v, c *proto.EthereumL2Config) bool {
   378  	if v.ChainId != c.ChainId {
   379  		return false
   380  	}
   381  
   382  	if v.NetworkId != c.NetworkId {
   383  		return false
   384  	}
   385  
   386  	if v.Name != c.Name {
   387  		return false
   388  	}
   389  
   390  	return true
   391  }