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 }