code.vegaprotocol.io/vega@v0.79.0/core/blockchain/abci/local_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 abci
    17  
    18  import (
    19  	"context"
    20  	"time"
    21  
    22  	tmquery "github.com/cometbft/cometbft/libs/pubsub/query"
    23  	"github.com/cometbft/cometbft/libs/service"
    24  	nm "github.com/cometbft/cometbft/node"
    25  	"github.com/cometbft/cometbft/rpc/client/local"
    26  	tmctypes "github.com/cometbft/cometbft/rpc/core/types"
    27  	tmtypes "github.com/cometbft/cometbft/types"
    28  )
    29  
    30  type LocalClient struct {
    31  	node       *local.Local
    32  	genesisDoc *cachedGenesisDoc
    33  }
    34  
    35  func newLocalClient(node service.Service) (*LocalClient, error) {
    36  	localNode := local.New(node.(*nm.Node))
    37  	return &LocalClient{
    38  		node:       localNode,
    39  		genesisDoc: newCachedGenesisDoc(),
    40  	}, nil
    41  }
    42  
    43  func (c *LocalClient) SendTransactionAsync(ctx context.Context, bytes []byte) (*tmctypes.ResultBroadcastTx, error) {
    44  	// Fire off the transaction for consensus
    45  	res, err := c.node.BroadcastTxAsync(ctx, bytes)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return res, nil
    50  }
    51  
    52  func (c *LocalClient) SendTransactionSync(ctx context.Context, bytes []byte) (*tmctypes.ResultBroadcastTx, error) {
    53  	// Fire off the transaction for consensus
    54  	return c.node.BroadcastTxSync(ctx, bytes)
    55  }
    56  
    57  func (c *LocalClient) SendTransactionCommit(ctx context.Context, bytes []byte) (*tmctypes.ResultBroadcastTxCommit, error) {
    58  	// Fire off the transaction for consensus
    59  	return c.node.BroadcastTxCommit(ctx, bytes)
    60  }
    61  
    62  func (c *LocalClient) CheckTransaction(ctx context.Context, bytes []byte) (*tmctypes.ResultCheckTx, error) {
    63  	return c.node.CheckTx(ctx, bytes)
    64  }
    65  
    66  // GetGenesisTime retrieves the genesis time from the blockchain.
    67  func (c *LocalClient) GetGenesisTime(ctx context.Context) (genesisTime time.Time, err error) {
    68  	res, err := c.genesisDoc.Get(ctx, c.node)
    69  	if err != nil {
    70  		return time.Time{}, err
    71  	}
    72  	return res.GenesisTime.UTC(), nil
    73  }
    74  
    75  // GetChainID retrieves the chainID from the blockchain.
    76  func (c *LocalClient) GetChainID(ctx context.Context) (chainID string, err error) {
    77  	res, err := c.genesisDoc.Get(ctx, c.node)
    78  	if err != nil {
    79  		return "", err
    80  	}
    81  	return res.ChainID, nil
    82  }
    83  
    84  // GetStatus returns the current status of the chain.
    85  func (c *LocalClient) GetStatus(ctx context.Context) (status *tmctypes.ResultStatus, err error) {
    86  	return c.node.Status(ctx)
    87  }
    88  
    89  // GetNetworkInfo return information of the current network.
    90  func (c *LocalClient) GetNetworkInfo(ctx context.Context) (netInfo *tmctypes.ResultNetInfo, err error) {
    91  	return c.node.NetInfo(ctx)
    92  }
    93  
    94  // GetUnconfirmedTxCount return the current count of unconfirmed transactions.
    95  func (c *LocalClient) GetUnconfirmedTxCount(ctx context.Context) (count int, err error) {
    96  	res, err := c.node.NumUnconfirmedTxs(ctx)
    97  	if err != nil {
    98  		return 0, err
    99  	}
   100  	return res.Count, err
   101  }
   102  
   103  // Health returns the result of the health endpoint of the chain.
   104  func (c *LocalClient) Health(ctx context.Context) (*tmctypes.ResultHealth, error) {
   105  	return c.node.Health(ctx)
   106  }
   107  
   108  func (c *LocalClient) Validators(ctx context.Context, height *int64) ([]*tmtypes.Validator, error) {
   109  	res, err := c.node.Validators(ctx, height, nil, nil)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return res.Validators, nil
   114  }
   115  
   116  func (c *LocalClient) Genesis(ctx context.Context) (*tmtypes.GenesisDoc, error) {
   117  	res, err := c.genesisDoc.Get(ctx, c.node)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return res, nil
   122  }
   123  
   124  func (c *LocalClient) GenesisValidators(ctx context.Context) ([]*tmtypes.Validator, error) {
   125  	gen, err := c.Genesis(ctx)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	validators := make([]*tmtypes.Validator, 0, len(gen.Validators))
   131  	for _, v := range gen.Validators {
   132  		validators = append(validators, &tmtypes.Validator{
   133  			Address:     v.Address,
   134  			PubKey:      v.PubKey,
   135  			VotingPower: v.Power,
   136  		})
   137  	}
   138  
   139  	return validators, nil
   140  }
   141  
   142  // Subscribe subscribes to any event matching query (https://godoc.org/github.com/cometbft/cometbft/types#pkg-constants).
   143  // Subscribe will call fn each time it receives an event from the node.
   144  // The function returns nil when the context is canceled or when fn returns an error.
   145  func (c *LocalClient) Subscribe(ctx context.Context, fn func(tmctypes.ResultEvent) error, queries ...string) error {
   146  	if err := c.node.Start(); err != nil {
   147  		return err
   148  	}
   149  	defer c.node.Stop()
   150  
   151  	errCh := make(chan error)
   152  
   153  	for _, query := range queries {
   154  		q, err := tmquery.New(query)
   155  		if err != nil {
   156  			return err
   157  		}
   158  
   159  		// For subscription we use "vega" as the client name but it's ignored by the implementation.
   160  		// 10 is the channel capacity which is absolutely arbitraty.
   161  		out, err := c.node.Subscribe(ctx, "vega", q.String(), 10)
   162  		if err != nil {
   163  			return err
   164  		}
   165  
   166  		go func() {
   167  			for res := range out {
   168  				if err := fn(res); err != nil {
   169  					errCh <- err
   170  					return
   171  				}
   172  			}
   173  		}()
   174  	}
   175  	defer c.node.UnsubscribeAll(context.Background(), "vega")
   176  
   177  	return <-errCh
   178  }
   179  
   180  func (c *LocalClient) Start() error {
   181  	return nil // Nothing to do for this client type.
   182  }