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 }