code.vegaprotocol.io/vega@v0.79.0/core/blockchain/client.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 blockchain 17 18 import ( 19 "context" 20 "errors" 21 "sync" 22 "time" 23 24 "code.vegaprotocol.io/vega/commands" 25 "code.vegaprotocol.io/vega/libs/proto" 26 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 27 28 "github.com/cometbft/cometbft/libs/bytes" 29 tmctypes "github.com/cometbft/cometbft/rpc/core/types" 30 tmtypes "github.com/cometbft/cometbft/types" 31 ) 32 33 var ErrClientNotReady = errors.New("tendermint client is not ready") 34 35 // nolint: interfacebloat 36 type ChainClientImpl interface { 37 GetGenesisTime(context.Context) (time.Time, error) 38 GetChainID(context.Context) (string, error) 39 GetStatus(context.Context) (*tmctypes.ResultStatus, error) 40 GetNetworkInfo(context.Context) (*tmctypes.ResultNetInfo, error) 41 GetUnconfirmedTxCount(context.Context) (int, error) 42 Health(context.Context) (*tmctypes.ResultHealth, error) 43 SendTransactionAsync(context.Context, []byte) (*tmctypes.ResultBroadcastTx, error) 44 SendTransactionSync(context.Context, []byte) (*tmctypes.ResultBroadcastTx, error) 45 CheckTransaction(context.Context, []byte) (*tmctypes.ResultCheckTx, error) 46 SendTransactionCommit(context.Context, []byte) (*tmctypes.ResultBroadcastTxCommit, error) 47 GenesisValidators(context.Context) ([]*tmtypes.Validator, error) 48 Validators(context.Context, *int64) ([]*tmtypes.Validator, error) 49 Subscribe(context.Context, func(tmctypes.ResultEvent) error, ...string) error 50 Start() error 51 } 52 53 // Client abstract all communication to the blockchain. 54 type Client struct { 55 *Config 56 clt ChainClientImpl 57 mempoolSize int64 58 mu sync.RWMutex 59 } 60 61 // NewClient instantiate a new blockchain client. 62 func NewClient() *Client { 63 return &Client{ 64 clt: nil, 65 } 66 } 67 68 func NewClientWithImpl(clt ChainClientImpl) *Client { 69 return &Client{ 70 clt: clt, 71 } 72 } 73 74 func (c *Client) Set(clt ChainClientImpl, mempoolSize int64) { 75 c.mu.Lock() 76 defer c.mu.Unlock() 77 c.clt = clt 78 c.mempoolSize = mempoolSize 79 } 80 81 func (c *Client) isReady() bool { 82 c.mu.RLock() 83 defer c.mu.RUnlock() 84 return c.clt != nil 85 } 86 87 func (c *Client) CheckRawTransaction(ctx context.Context, tx []byte) (*tmctypes.ResultCheckTx, error) { 88 if !c.isReady() { 89 return nil, ErrClientNotReady 90 } 91 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 92 defer cancel() 93 94 return c.clt.CheckTransaction(ctx, tx) 95 } 96 97 func (c *Client) CheckTransaction(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultCheckTx, error) { 98 if !c.isReady() { 99 return nil, ErrClientNotReady 100 } 101 102 chainID, err := c.clt.GetChainID(ctx) 103 if err != nil { 104 return nil, err 105 } 106 107 if _, err := commands.CheckTransaction(tx, chainID); err != nil { 108 return &tmctypes.ResultCheckTx{ 109 ResponseCheckTx: *NewResponseCheckTxError(AbciTxnDecodingFailure, err), 110 }, nil 111 } 112 113 marshalledTx, err := proto.Marshal(tx) 114 if err != nil { 115 return nil, err 116 } 117 118 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 119 defer cancel() 120 121 return c.clt.CheckTransaction(ctx, marshalledTx) 122 } 123 124 func (c *Client) SubmitTransactionSync(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultBroadcastTx, error) { 125 if !c.isReady() { 126 return nil, ErrClientNotReady 127 } 128 129 marshalledTx, err := proto.Marshal(tx) 130 if err != nil { 131 return nil, err 132 } 133 134 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 135 defer cancel() 136 137 t, err := c.clt.SendTransactionSync(ctx, marshalledTx) 138 139 return t, err 140 } 141 142 func (c *Client) SubmitTransactionCommit(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultBroadcastTxCommit, error) { 143 if !c.isReady() { 144 return nil, ErrClientNotReady 145 } 146 147 chainID, err := c.clt.GetChainID(ctx) 148 if err != nil { 149 return nil, err 150 } 151 152 if _, err := commands.CheckTransaction(tx, chainID); err != nil { 153 return &tmctypes.ResultBroadcastTxCommit{ 154 CheckTx: *NewResponseCheckTxError(AbciTxnDecodingFailure, err), 155 }, nil 156 } 157 158 marshalledTx, err := proto.Marshal(tx) 159 if err != nil { 160 return nil, err 161 } 162 163 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 164 defer cancel() 165 166 t, err := c.clt.SendTransactionCommit(ctx, marshalledTx) 167 168 return t, err 169 } 170 171 func (c *Client) SubmitTransactionAsync(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultBroadcastTx, error) { 172 if !c.isReady() { 173 return nil, ErrClientNotReady 174 } 175 176 chainID, err := c.clt.GetChainID(ctx) 177 if err != nil { 178 return nil, err 179 } 180 181 if _, err := commands.CheckTransaction(tx, chainID); err != nil { 182 return &tmctypes.ResultBroadcastTx{ //nolint:nilerr 183 Code: AbciTxnDecodingFailure, 184 Data: bytes.HexBytes(err.Error()), 185 }, nil 186 } 187 188 marshalledTx, err := proto.Marshal(tx) 189 if err != nil { 190 return nil, err 191 } 192 193 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 194 defer cancel() 195 196 t, err := c.clt.SendTransactionAsync(ctx, marshalledTx) 197 198 return t, err 199 } 200 201 func (c *Client) SubmitRawTransactionSync(ctx context.Context, tx []byte) (*tmctypes.ResultBroadcastTx, error) { 202 if !c.isReady() { 203 return nil, ErrClientNotReady 204 } 205 timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 206 defer cancel() 207 208 return c.clt.SendTransactionSync(timeoutCtx, tx) 209 } 210 211 func (c *Client) SubmitRawTransactionAsync(ctx context.Context, tx []byte) (*tmctypes.ResultBroadcastTx, error) { 212 if !c.isReady() { 213 return nil, ErrClientNotReady 214 } 215 timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 216 defer cancel() 217 218 return c.clt.SendTransactionAsync(timeoutCtx, tx) 219 } 220 221 func (c *Client) SubmitRawTransactionCommit(ctx context.Context, tx []byte) (*tmctypes.ResultBroadcastTxCommit, error) { 222 if !c.isReady() { 223 return nil, ErrClientNotReady 224 } 225 timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 226 defer cancel() 227 228 return c.clt.SendTransactionCommit(timeoutCtx, tx) 229 } 230 231 // GetGenesisTime retrieves the genesis time from the blockchain. 232 func (c *Client) GetGenesisTime(ctx context.Context) (genesisTime time.Time, err error) { 233 if !c.isReady() { 234 return time.Time{}, ErrClientNotReady 235 } 236 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 237 defer cancel() 238 239 return c.clt.GetGenesisTime(ctx) 240 } 241 242 // GetChainID retrieves the chainID from the blockchain. 243 func (c *Client) GetChainID(ctx context.Context) (chainID string, err error) { 244 if !c.isReady() { 245 return "", ErrClientNotReady 246 } 247 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 248 defer cancel() 249 250 return c.clt.GetChainID(ctx) 251 } 252 253 // GetStatus returns the current status of the chain. 254 func (c *Client) GetStatus(ctx context.Context) (status *tmctypes.ResultStatus, err error) { 255 if !c.isReady() { 256 return nil, ErrClientNotReady 257 } 258 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 259 defer cancel() 260 261 return c.clt.GetStatus(ctx) 262 } 263 264 // GetNetworkInfo return information of the current network. 265 func (c *Client) GetNetworkInfo(ctx context.Context) (netInfo *tmctypes.ResultNetInfo, err error) { 266 if !c.isReady() { 267 return nil, ErrClientNotReady 268 } 269 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 270 defer cancel() 271 272 return c.clt.GetNetworkInfo(ctx) 273 } 274 275 // GetUnconfirmedTxCount return the current count of unconfirmed transactions. 276 func (c *Client) GetUnconfirmedTxCount(ctx context.Context) (count int, err error) { 277 if !c.isReady() { 278 return 0, ErrClientNotReady 279 } 280 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 281 defer cancel() 282 283 return c.clt.GetUnconfirmedTxCount(ctx) 284 } 285 286 // Health returns the result of the health endpoint of the chain. 287 func (c *Client) Health() (*tmctypes.ResultHealth, error) { 288 if !c.isReady() { 289 return nil, ErrClientNotReady 290 } 291 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 292 defer cancel() 293 294 return c.clt.Health(ctx) 295 } 296 297 func (c *Client) GenesisValidators() ([]*tmtypes.Validator, error) { 298 if !c.isReady() { 299 return nil, ErrClientNotReady 300 } 301 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 302 defer cancel() 303 return c.clt.GenesisValidators(ctx) 304 } 305 306 func (c *Client) MaxMempoolSize() int64 { 307 return c.mempoolSize 308 } 309 310 func (c *Client) Validators(height *int64) ([]*tmtypes.Validator, error) { 311 if !c.isReady() { 312 return nil, ErrClientNotReady 313 } 314 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 315 defer cancel() 316 317 return c.clt.Validators(ctx, height) 318 } 319 320 func (c *Client) Subscribe(ctx context.Context, fn func(tmctypes.ResultEvent) error, queries ...string) error { 321 if !c.isReady() { 322 return ErrClientNotReady 323 } 324 return c.clt.Subscribe(ctx, fn, queries...) 325 } 326 327 func (c *Client) Start() error { 328 return c.clt.Start() 329 }