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  }