code.vegaprotocol.io/vega@v0.79.0/core/client/eth/l2_clients.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 eth 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 23 "code.vegaprotocol.io/vega/core/types" 24 "code.vegaprotocol.io/vega/logging" 25 26 "github.com/ethereum/go-ethereum/ethclient" 27 ) 28 29 type L2Client struct { 30 ETHClient 31 } 32 33 type L2Clients struct { 34 ctx context.Context 35 log *logging.Logger 36 // map of chainID -> Client 37 clients map[string]*L2Client 38 confirmations map[string]*EthereumConfirmations 39 } 40 41 func NewL2Clients( 42 ctx context.Context, 43 log *logging.Logger, 44 cfg Config, 45 ) (*L2Clients, error) { 46 // setup logger 47 log = log.Named(namedLogger) 48 log.SetLevel(cfg.Level.Get()) 49 50 clients := map[string]*L2Client{} 51 confirmations := map[string]*EthereumConfirmations{} 52 53 for _, v := range cfg.EVMChainConfigs { 54 log.Info("starting L2 client", 55 logging.String("chain-id", v.ChainID), 56 logging.String("endpoint", v.RPCEndpoint), 57 ) 58 if len(v.ChainID) <= 0 || len(v.RPCEndpoint) <= 0 { 59 return nil, errors.New("l2 rpc endpoint configured with empty strings") 60 } 61 clt, err := DialL2(ctx, v.RPCEndpoint) 62 if err != nil { 63 return nil, err 64 } 65 66 chainID, err := clt.ChainID(ctx) 67 if err != nil { 68 return nil, fmt.Errorf("couldn't get chain id: %w", err) 69 } 70 71 if chainID.String() != v.ChainID { 72 return nil, fmt.Errorf("client retrieve different chain id: %v vs %v", chainID.String(), v.ChainID) 73 } 74 75 clients[v.ChainID] = clt 76 confirmations[v.ChainID] = NewEthereumConfirmations(cfg, clt, nil, FinalityStateLatest) 77 } 78 79 return &L2Clients{ 80 ctx: ctx, 81 log: log, 82 clients: clients, 83 confirmations: confirmations, 84 }, nil 85 } 86 87 func (e *L2Clients) UpdateConfirmations(ethCfg *types.EthereumL2Configs) { 88 for _, v := range ethCfg.Configs { 89 confs, ok := e.confirmations[v.ChainID] 90 if !ok { 91 e.log.Panic("ethereum client for L2 is not configured", logging.String("name", v.Name), logging.String("chain-id", v.ChainID)) 92 } 93 94 confs.UpdateConfirmations(v.Confirmations) 95 } 96 } 97 98 // ReloadConf updates the internal configuration of the execution 99 // engine and its dependencies. 100 func (e *L2Clients) ReloadConf(cfg Config) { 101 e.log.Debug("reloading configuration") 102 103 if e.log.GetLevel() != cfg.Level.Get() { 104 e.log.Info("updating log level", 105 logging.String("old", e.log.GetLevel().String()), 106 logging.String("new", cfg.Level.String()), 107 ) 108 e.log.SetLevel(cfg.Level.Get()) 109 } 110 111 e.log.Info("updating L2 clients") 112 for _, v := range cfg.EVMChainConfigs { 113 if _, ok := e.clients[v.ChainID]; ok { 114 e.log.Warn("L2 client already setted up, please stop and restart node to update existing configuration", 115 logging.String("chain-id", v.ChainID), 116 logging.String("endpoint", v.RPCEndpoint), 117 ) 118 continue 119 } 120 121 e.log.Info("starting L2 client", 122 logging.String("chain-id", v.ChainID), 123 logging.String("endpoint", v.RPCEndpoint), 124 ) 125 if len(v.ChainID) <= 0 || len(v.RPCEndpoint) <= 0 { 126 e.log.Warn("invalid L2 client configuration", 127 logging.String("chain-id", v.ChainID), 128 logging.String("endpoint", v.RPCEndpoint), 129 ) 130 continue 131 } 132 clt, err := DialL2(e.ctx, v.RPCEndpoint) 133 if err != nil { 134 e.log.Warn("couldn't start L2 client", 135 logging.String("chain-id", v.ChainID), 136 logging.String("endpoint", v.RPCEndpoint), 137 ) 138 continue 139 } 140 141 chainID, err := clt.ChainID(e.ctx) 142 if err != nil { 143 e.log.Warn("couldn't get chain id", logging.Error(err)) 144 continue 145 } 146 147 if chainID.String() != v.ChainID { 148 e.log.Warn("client retrieved different chain id", 149 logging.String("chain-id", chainID.String()), 150 logging.String("expected", v.ChainID), 151 ) 152 continue 153 } 154 155 e.clients[v.ChainID] = clt 156 e.confirmations[v.ChainID] = NewEthereumConfirmations(cfg, clt, nil, FinalityStateLatest) 157 } 158 } 159 160 func DialL2(ctx context.Context, endpoint string) (*L2Client, error) { 161 ethClient, err := ethclient.DialContext(ctx, endpoint) 162 if err != nil { 163 return nil, fmt.Errorf("couldn't instantiate Ethereum client: %w", err) 164 } 165 166 return &L2Client{ETHClient: newEthClientWrapper(ethClient)}, nil 167 } 168 169 func (c *L2Clients) Get(chainID string) (*L2Client, *EthereumConfirmations, bool) { 170 clt, ok1 := c.clients[chainID] 171 confs, ok2 := c.confirmations[chainID] 172 return clt, confs, ok1 && ok2 173 }