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