-
Notifications
You must be signed in to change notification settings - Fork 18
/
api_basic.go
467 lines (431 loc) · 16 KB
/
api_basic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
package client
import (
"context"
"fmt"
"io"
"os"
"strings"
"time"
"cosmossdk.io/errors"
"github.com/cometbft/cometbft/proto/tendermint/p2p"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
bfttypes "github.com/cometbft/cometbft/types"
"github.com/cometbft/cometbft/votepool"
"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
"google.golang.org/grpc"
gosdktypes "github.com/bnb-chain/greenfield-go-sdk/types"
"github.com/bnb-chain/greenfield/sdk/types"
storageTypes "github.com/bnb-chain/greenfield/x/storage/types"
)
// IBasicClient interface defines basic functions of greenfield Client.
type IBasicClient interface {
EnableTrace(outputStream io.Writer, onlyTraceErr bool)
GetNodeInfo(ctx context.Context) (*p2p.DefaultNodeInfo, *tmservice.VersionInfo, error)
GetStatus(ctx context.Context) (*ctypes.ResultStatus, error)
GetCommit(ctx context.Context, height int64) (*ctypes.ResultCommit, error)
GetLatestBlockHeight(ctx context.Context) (int64, error)
GetLatestBlock(ctx context.Context) (*bfttypes.Block, error)
GetSyncing(ctx context.Context) (bool, error)
GetBlockByHeight(ctx context.Context, height int64) (*bfttypes.Block, error)
GetBlockResultByHeight(ctx context.Context, height int64) (*ctypes.ResultBlockResults, error)
GetValidatorSet(ctx context.Context) (int64, []*bfttypes.Validator, error)
GetValidatorsByHeight(ctx context.Context, height int64) ([]*bfttypes.Validator, error)
WaitForBlockHeight(ctx context.Context, height int64) error
WaitForTx(ctx context.Context, hash string) (*ctypes.ResultTx, error)
WaitForNBlocks(ctx context.Context, n int64) error
WaitForNextBlock(ctx context.Context) error
SimulateTx(ctx context.Context, msgs []sdk.Msg, txOpt types.TxOption, opts ...grpc.CallOption) (*tx.SimulateResponse, error)
SimulateRawTx(ctx context.Context, txBytes []byte, opts ...grpc.CallOption) (*tx.SimulateResponse, error)
BroadcastTx(ctx context.Context, msgs []sdk.Msg, txOpt *types.TxOption, opts ...grpc.CallOption) (*tx.BroadcastTxResponse, error)
BroadcastRawTx(ctx context.Context, txBytes []byte, sync bool) (*sdk.TxResponse, error)
BroadcastVote(ctx context.Context, vote votepool.Vote) error
QueryVote(ctx context.Context, eventType int, eventHash []byte) (*ctypes.ResultQueryVote, error)
SetTag(ctx context.Context, resourceGRN string, tags storageTypes.ResourceTags, opts gosdktypes.SetTagsOptions) (string, error)
}
// EnableTrace support trace error info the request and the response
func (c *Client) EnableTrace(output io.Writer, onlyTraceErr bool) {
if output == nil {
output = os.Stdout
}
c.onlyTraceError = onlyTraceErr
c.traceOutput = output
c.isTraceEnabled = true
}
// GetNodeInfo - Get the current node info of the greenfield that the Client is connected to.
//
// - ctx: Context variables for the current API call.
//
// - ret1: The Node info.
//
// - ret2: The Version info.
//
// - ret3: Return error when the request failed, otherwise return nil.
func (c *Client) GetNodeInfo(ctx context.Context) (*p2p.DefaultNodeInfo, *tmservice.VersionInfo, error) {
nodeInfoResponse, err := c.chainClient.TmClient.GetNodeInfo(ctx, &tmservice.GetNodeInfoRequest{})
if err != nil {
return nil, nil, err
}
return nodeInfoResponse.DefaultNodeInfo, nodeInfoResponse.ApplicationVersion, nil
}
// GetStatus - Get the status of connected Node.
//
// - ctx: Context variables for the current API call.
//
// - ret1: The detail of Node status.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetStatus(ctx context.Context) (*ctypes.ResultStatus, error) {
return c.chainClient.GetStatus(ctx)
}
// GetCommit - Get the block commit detail.
//
// - ctx: Context variables for the current API call.
//
// - height: The block height.
//
// - ret1: The commit result.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetCommit(ctx context.Context, height int64) (*ctypes.ResultCommit, error) {
return c.chainClient.GetCommit(ctx, height)
}
// BroadcastRawTx - Broadcast raw transaction bytes to a Tendermint node.
//
// - ctx: Context variables for the current API call.
//
// - txBytes: The transaction bytes.
//
// - sync: A flag to specify the transaction mode. If it is true, the transaction is broadcast synchronously. If it is false, the transaction is broadcast asynchronously.
//
// - ret1: Transaction response, it can indicate both success and failed transaction.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) BroadcastRawTx(ctx context.Context, txBytes []byte, sync bool) (*sdk.TxResponse, error) {
var mode tx.BroadcastMode
if sync {
mode = tx.BroadcastMode_BROADCAST_MODE_SYNC
} else {
mode = tx.BroadcastMode_BROADCAST_MODE_ASYNC
}
broadcastTxResponse, err := c.chainClient.TxClient.BroadcastTx(ctx, &tx.BroadcastTxRequest{TxBytes: txBytes, Mode: mode})
if err != nil {
return nil, err
}
return broadcastTxResponse.TxResponse, nil
}
// SimulateRawTx - Simulate the execution of a raw transaction on the blockchain without broadcasting it to the network.
//
// - ctx: Context variables for the current API call.
//
// - txBytes: The transaction bytes.
//
// - opts: The grpc option(s) if Client is using grpc connection.
//
// - ret1: The simulation result.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) SimulateRawTx(ctx context.Context, txBytes []byte, opts ...grpc.CallOption) (*tx.SimulateResponse, error) {
simulateResponse, err := c.chainClient.TxClient.Simulate(
ctx,
&tx.SimulateRequest{
TxBytes: txBytes,
},
opts...,
)
if err != nil {
return nil, err
}
return simulateResponse, nil
}
// GetLatestBlock - Get the latest block from the chain.
//
// - ctx: Context variables for the current API call.
//
// - ret1: The block result.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetLatestBlock(ctx context.Context) (*bfttypes.Block, error) {
res, err := c.chainClient.GetBlock(ctx, nil)
if err != nil {
return nil, err
}
return res.Block, nil
}
// GetLatestBlockHeight - Get the height of the latest block from the chain.
//
// - ctx: Context variables for the current API call.
//
// - ret1: The block height.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetLatestBlockHeight(ctx context.Context) (int64, error) {
resp, err := c.chainClient.GetStatus(ctx)
if err != nil {
return 0, nil
}
return resp.SyncInfo.LatestBlockHeight, nil
}
// WaitForBlockHeight - Wait until a specified block height is committed.
//
// - ctx: Context variables for the current API call.
//
// - ret: Return error when the request failed, otherwise return nil.
func (c *Client) WaitForBlockHeight(ctx context.Context, h int64) error {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
latestBlockHeight, err := c.GetLatestBlockHeight(ctx)
if err != nil {
return err
}
if latestBlockHeight >= h {
return nil
}
select {
case <-ctx.Done():
return errors.Wrap(ctx.Err(), "timeout exceeded waiting for block")
case <-ticker.C:
}
}
}
// WaitForNextBlock - Wait until the next block is committed since current block.
//
// - ctx: Context variables for the current API call.
//
// - ret: Return error when the request failed, otherwise return nil.
func (c *Client) WaitForNextBlock(ctx context.Context) error {
return c.WaitForNBlocks(ctx, 1)
}
// WaitForNBlocks - Wait for another n blocks to be committed since current block.
//
// - ctx: Context variables for the current API call.
//
// - n: number of blocks to be waited.
//
// - ret: Return error when the request failed, otherwise return nil.
func (c *Client) WaitForNBlocks(ctx context.Context, n int64) error {
start, err := c.GetLatestBlock(ctx)
if err != nil {
return err
}
return c.WaitForBlockHeight(ctx, start.Header.Height+n)
}
// WaitForTx - Wait for a transaction to be confirmed onchian, if transaction not found in current block, wait for the next block. API ends when a transaction is found or context is canceled.
//
// - ctx: Context variables for the current API call.
//
// - hash: The hex representation of transaction hash.
//
// - ret1: The transaction result details.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) WaitForTx(ctx context.Context, hash string) (*ctypes.ResultTx, error) {
for {
var (
txResponse *ctypes.ResultTx
err error
waitTxCtx context.Context
cancelFunc context.CancelFunc
)
// when websocket conn is used, use a short timeout context to achieve the retry mechanism
if c.useWebsocketConn {
waitTxCtx, cancelFunc = context.WithTimeout(context.Background(), gosdktypes.WaitTxContextTimeOut)
txResponse, err = c.chainClient.Tx(waitTxCtx, hash)
cancelFunc()
} else {
txResponse, err = c.chainClient.Tx(ctx, hash)
}
if err != nil {
// Tx not found, wait for next block and try again
// If websocket conn is enabled, we also want to re-try the GetTx calls by having a timeout context
if strings.Contains(err.Error(), "not found") || (c.useWebsocketConn && (waitTxCtx.Err() == context.DeadlineExceeded)) {
err := c.WaitForNextBlock(ctx)
if err != nil {
return nil, errors.Wrap(err, "waiting for next block")
}
continue
}
return nil, errors.Wrapf(err, "fetching tx '%s'", hash)
}
// `nil` could mean the transaction is in the mempool, invalidated, or was not sent in the first place.
if txResponse == nil {
err := c.WaitForNextBlock(ctx)
if err != nil {
return nil, errors.Wrap(err, "waiting for next block")
}
continue
}
// Tx found
return txResponse, nil
}
}
// BroadcastTx - Broadcast a transaction containing the provided message(s) to the chain.
//
// - ctx: Context variables for the current API call.
//
// - msgs: Message(s) to be broadcast to blockchain.
//
// - txOpt: txOpt contains options for customizing the transaction.
//
// - opts: The grpc option(s) if Client is using grpc connection.
//
// - ret1: transaction response, it can indicate both success and failed transaction.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) BroadcastTx(ctx context.Context, msgs []sdk.Msg, txOpt *types.TxOption, opts ...grpc.CallOption) (*tx.BroadcastTxResponse, error) {
if len(msgs) == 0 {
return nil, fmt.Errorf("msg is not provided in the transaction")
}
for _, msg := range msgs {
if err := msg.ValidateBasic(); err != nil {
return nil, err
}
}
resp, err := c.chainClient.BroadcastTx(ctx, msgs, txOpt, opts...)
if err != nil {
return nil, err
}
if resp.TxResponse.Code != 0 {
return resp, fmt.Errorf("the tx has failed with response code: %d, codespace:%s", resp.TxResponse.Code, resp.TxResponse.Codespace)
}
return resp, nil
}
// SimulateTx - Simulate a transaction containing the provided message(s) on the chain.
//
// - ctx: Context variables for the current API call.
//
// - msgs: Message(s) to be broadcast to blockchain.
//
// - txOpt: TxOpt contains options for customizing the transaction.
//
// - opts: The grpc option(s) if Client is using grpc connection.
//
// - ret1: The simulation result.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) SimulateTx(ctx context.Context, msgs []sdk.Msg, txOpt types.TxOption, opts ...grpc.CallOption) (*tx.SimulateResponse, error) {
return c.chainClient.SimulateTx(ctx, msgs, &txOpt, opts...)
}
// GetSyncing - Retrieve the syncing status of the node.
//
// - ctx: Context variables for the current API call.
//
// - ret1: The boolean value which indicates whether the node has caught up the latest block.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetSyncing(ctx context.Context) (bool, error) {
syncing, err := c.chainClient.GetSyncing(ctx, &tmservice.GetSyncingRequest{})
if err != nil {
return false, err
}
return syncing.Syncing, nil
}
// GetBlockByHeight - Retrieve the block at the given height from the chain.
//
// - ctx: Context variables for the current API call.
//
// - height: The block height.
//
// - ret1: The boolean value which indicates whether the node has caught up the latest block.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetBlockByHeight(ctx context.Context, height int64) (*bfttypes.Block, error) {
blockByHeight, err := c.chainClient.GetBlock(ctx, &height)
if err != nil {
return nil, err
}
return blockByHeight.Block, nil
}
// GetBlockResultByHeight - Retrieve the block result at the given height from the chain.
//
// - ctx: Context variables for the current API call.
//
// - height: The block height.
//
// - ret1: The boolean value which indicates whether the node has caught up the latest block.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetBlockResultByHeight(ctx context.Context, height int64) (*ctypes.ResultBlockResults, error) {
return c.chainClient.GetBlockResults(ctx, &height)
}
// GetValidatorSet - Retrieve the latest validator set from the chain.
//
// - ctx: Context variables for the current API call.
//
// - ret1: The latest height of block that validators set info retrieved from.
//
// - ret2: The list of validators.
//
// - ret3: Return error when the request failed, otherwise return nil.
func (c *Client) GetValidatorSet(ctx context.Context) (int64, []*bfttypes.Validator, error) {
validatorSetResponse, err := c.chainClient.GetValidators(ctx, nil)
if err != nil {
return 0, nil, err
}
return validatorSetResponse.BlockHeight, validatorSetResponse.Validators, nil
}
// GetValidatorsByHeight - Retrieve the validator set at a given block height from the chain.
//
// - ctx: Context variables for the current API call.
//
// - height: The block height.
//
// - ret1: The list of validators.
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) GetValidatorsByHeight(ctx context.Context, height int64) ([]*bfttypes.Validator, error) {
validatorSetResponse, err := c.chainClient.GetValidators(ctx, &height)
if err != nil {
return nil, err
}
return validatorSetResponse.Validators, nil
}
// BroadcastVote - Broadcast a vote to the Node's VotePool, it is used by Greenfield relayer and challengers by now.
//
// - ctx: Context variables for the current API call.
//
// - vote: Contains vote details.
//
// - ret: Return error when the request failed, otherwise return nil.
func (c *Client) BroadcastVote(ctx context.Context, vote votepool.Vote) error {
return c.chainClient.BroadcastVote(ctx, vote)
}
// QueryVote - Query a vote from the Node's VotePool, it is used by Greenfield relayer and challengers by now.
//
// - ctx: Context variables for the current API call.
//
// - eventType: The type of vote to be queried.
//
// - eventHash: The hash bytes of vote
//
// - ret1: The vote result
//
// - ret2: Return error when the request failed, otherwise return nil.
func (c *Client) QueryVote(ctx context.Context, eventType int, eventHash []byte) (*ctypes.ResultQueryVote, error) {
return c.chainClient.QueryVote(ctx, eventType, eventHash)
}
// SetTag - Set tag for a given existing resource GRN (a bucket, a object or a group)
//
// This API sends a request to the greenfield chain to set tags for the given resource.
//
// - ctx: Context variables for the current API call.
//
// - resourceGRN: The GRN of resource that needs to set tags
//
// - tags: the tags to be set for the given resource
//
// - opts: The Options indicates the meta to construct setTag msg and the way to send transaction
//
// - ret1: Transaction hash return from blockchain.
//
// - ret2: Return error if SetTag failed, otherwise return nil.
func (c *Client) SetTag(ctx context.Context, resourceGRN string, tags storageTypes.ResourceTags, opts gosdktypes.SetTagsOptions) (string, error) {
msgSetTag := storageTypes.NewMsgSetTag(c.MustGetDefaultAccount().GetAddress(), resourceGRN, &tags)
resp, err := c.BroadcastTx(ctx, []sdk.Msg{msgSetTag}, opts.TxOpts)
if err != nil {
return "", err
}
return resp.TxResponse.TxHash, err
}