github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/abci/client/client.go (about)

     1  package abcicli
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/badrootd/nibiru-cometbft/abci/types"
     8  	"github.com/badrootd/nibiru-cometbft/libs/service"
     9  	cmtsync "github.com/badrootd/nibiru-cometbft/libs/sync"
    10  )
    11  
    12  const (
    13  	dialRetryIntervalSeconds = 3
    14  	echoRetryIntervalSeconds = 1
    15  )
    16  
    17  //go:generate ../../scripts/mockery_generate.sh Client
    18  
    19  // Client defines an interface for an ABCI client.
    20  // All `Async` methods return a `ReqRes` object.
    21  // All `Sync` methods return the appropriate protobuf ResponseXxx struct and an error.
    22  // Note these are client errors, eg. ABCI socket connectivity issues.
    23  // Application-related errors are reflected in response via ABCI error codes and logs.
    24  type Client interface {
    25  	service.Service
    26  
    27  	SetResponseCallback(Callback)
    28  	Error() error
    29  
    30  	FlushAsync() *ReqRes
    31  	EchoAsync(msg string) *ReqRes
    32  	InfoAsync(types.RequestInfo) *ReqRes
    33  	DeliverTxAsync(types.RequestDeliverTx) *ReqRes
    34  	CheckTxAsync(types.RequestCheckTx) *ReqRes
    35  	QueryAsync(types.RequestQuery) *ReqRes
    36  	CommitAsync() *ReqRes
    37  	InitChainAsync(types.RequestInitChain) *ReqRes
    38  	PrepareProposalAsync(types.RequestPrepareProposal) *ReqRes
    39  	BeginBlockAsync(types.RequestBeginBlock) *ReqRes
    40  	EndBlockAsync(types.RequestEndBlock) *ReqRes
    41  	ListSnapshotsAsync(types.RequestListSnapshots) *ReqRes
    42  	OfferSnapshotAsync(types.RequestOfferSnapshot) *ReqRes
    43  	LoadSnapshotChunkAsync(types.RequestLoadSnapshotChunk) *ReqRes
    44  	ApplySnapshotChunkAsync(types.RequestApplySnapshotChunk) *ReqRes
    45  	ProcessProposalAsync(types.RequestProcessProposal) *ReqRes
    46  
    47  	FlushSync() error
    48  	EchoSync(msg string) (*types.ResponseEcho, error)
    49  	InfoSync(types.RequestInfo) (*types.ResponseInfo, error)
    50  	DeliverTxSync(types.RequestDeliverTx) (*types.ResponseDeliverTx, error)
    51  	CheckTxSync(types.RequestCheckTx) (*types.ResponseCheckTx, error)
    52  	QuerySync(types.RequestQuery) (*types.ResponseQuery, error)
    53  	CommitSync() (*types.ResponseCommit, error)
    54  	InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error)
    55  	PrepareProposalSync(types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error)
    56  	BeginBlockSync(types.RequestBeginBlock) (*types.ResponseBeginBlock, error)
    57  	EndBlockSync(types.RequestEndBlock) (*types.ResponseEndBlock, error)
    58  	ListSnapshotsSync(types.RequestListSnapshots) (*types.ResponseListSnapshots, error)
    59  	OfferSnapshotSync(types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error)
    60  	LoadSnapshotChunkSync(types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error)
    61  	ApplySnapshotChunkSync(types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error)
    62  	ProcessProposalSync(types.RequestProcessProposal) (*types.ResponseProcessProposal, error)
    63  }
    64  
    65  //----------------------------------------
    66  
    67  // NewClient returns a new ABCI client of the specified transport type.
    68  // It returns an error if the transport is not "socket" or "grpc"
    69  func NewClient(addr, transport string, mustConnect bool) (client Client, err error) {
    70  	switch transport {
    71  	case "socket":
    72  		client = NewSocketClient(addr, mustConnect)
    73  	case "grpc":
    74  		client = NewGRPCClient(addr, mustConnect)
    75  	default:
    76  		err = fmt.Errorf("unknown abci transport %s", transport)
    77  	}
    78  	return
    79  }
    80  
    81  type Callback func(*types.Request, *types.Response)
    82  
    83  type ReqRes struct {
    84  	*types.Request
    85  	*sync.WaitGroup
    86  	*types.Response // Not set atomically, so be sure to use WaitGroup.
    87  
    88  	mtx cmtsync.Mutex
    89  
    90  	// callbackInvoked as a variable to track if the callback was already
    91  	// invoked during the regular execution of the request. This variable
    92  	// allows clients to set the callback simultaneously without potentially
    93  	// invoking the callback twice by accident, once when 'SetCallback' is
    94  	// called and once during the normal request.
    95  	callbackInvoked bool
    96  	cb              func(*types.Response) // A single callback that may be set.
    97  }
    98  
    99  func NewReqRes(req *types.Request) *ReqRes {
   100  	return &ReqRes{
   101  		Request:   req,
   102  		WaitGroup: waitGroup1(),
   103  		Response:  nil,
   104  
   105  		callbackInvoked: false,
   106  		cb:              nil,
   107  	}
   108  }
   109  
   110  // Sets sets the callback. If reqRes is already done, it will call the cb
   111  // immediately. Note, reqRes.cb should not change if reqRes.done and only one
   112  // callback is supported.
   113  func (r *ReqRes) SetCallback(cb func(res *types.Response)) {
   114  	r.mtx.Lock()
   115  
   116  	if r.callbackInvoked {
   117  		r.mtx.Unlock()
   118  		cb(r.Response)
   119  		return
   120  	}
   121  
   122  	r.cb = cb
   123  	r.mtx.Unlock()
   124  }
   125  
   126  // InvokeCallback invokes a thread-safe execution of the configured callback
   127  // if non-nil.
   128  func (r *ReqRes) InvokeCallback() {
   129  	r.mtx.Lock()
   130  	defer r.mtx.Unlock()
   131  
   132  	if r.cb != nil {
   133  		r.cb(r.Response)
   134  	}
   135  	r.callbackInvoked = true
   136  }
   137  
   138  // GetCallback returns the configured callback of the ReqRes object which may be
   139  // nil. Note, it is not safe to concurrently call this in cases where it is
   140  // marked done and SetCallback is called before calling GetCallback as that
   141  // will invoke the callback twice and create a potential race condition.
   142  //
   143  // ref: https://github.com/tendermint/tendermint/issues/5439
   144  func (r *ReqRes) GetCallback() func(*types.Response) {
   145  	r.mtx.Lock()
   146  	defer r.mtx.Unlock()
   147  	return r.cb
   148  }
   149  
   150  func waitGroup1() (wg *sync.WaitGroup) {
   151  	wg = &sync.WaitGroup{}
   152  	wg.Add(1)
   153  	return
   154  }