Skip to content

Commit

Permalink
feat(gnoclient): support fetching blocks, block results, latest block…
Browse files Browse the repository at this point in the history
… number (gnolang#1910)

<!-- please provide a detailed description of the changes made in this
pull request. -->

## Description

This PR adds gnoclient support for fetching blocks, block results, and
the latest block number.

<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [x] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>
  • Loading branch information
leohhhn authored Apr 29, 2024
1 parent 3832b13 commit cb90aaa
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
24 changes: 24 additions & 0 deletions docs/reference/gnoclient/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ func (c *Client) AddPackage(cfg BaseTxCfg, msgs ...MsgAddPackage) (*ctypes.Resul

`AddPackage` executes one or more [AddPackage](#type-msgaddpackage) calls on the blockchain.

### func \(\*Client\) [Block](<https://github.com/gnolang/gno/blob/master/gno.land/pkg/gnoclient/client_queries.go#L131>)

```go
func (c *Client) Block(height int64) (*ctypes.ResultBlock, error)
```

`Block` gets the latest block at height, if any. Height must be larger than 0.

### func \(\*Client\) [BlockResult](<https://github.com/gnolang/gno/blob/master/gno.land/pkg/gnoclient/client_queries.go#L150>)

```go
func (c *Client) BlockResult(height int64) (*ctypes.ResultBlockResults, error)
```

`BlockResult` gets the block results at height, if any. Height must be larger than 0.

### func \(\*Client\) [LatestBlockHeight](<https://github.com/gnolang/gno/blob/master/gno.land/pkg/gnoclient/client_queries.go#L168>)

```go
func (c *Client) LatestBlockHeight() (int64, error)
```

`LatestBlockHeight` gets the latest block height on the chain.

### func \(\*Client\) [Call](<https://github.com/gnolang/gno/blob/master/gno.land/pkg/gnoclient/client_txs.go#L62>)

```go
Expand Down
54 changes: 54 additions & 0 deletions gno.land/pkg/gnoclient/client_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/gnolang/gno/tm2/pkg/std"
)

var ErrInvalidBlockHeight = errors.New("invalid block height provided")

// QueryCfg contains configuration options for performing ABCI queries.
type QueryCfg struct {
Path string // Query path
Expand Down Expand Up @@ -123,3 +125,55 @@ func (c *Client) QEval(pkgPath string, expression string) (string, *ctypes.Resul

return string(qres.Response.Data), qres, nil
}

// Block gets the latest block at height, if any
// Height must be larger than 0
func (c *Client) Block(height int64) (*ctypes.ResultBlock, error) {
if err := c.validateRPCClient(); err != nil {
return nil, ErrMissingRPCClient
}

if height <= 0 {
return nil, ErrInvalidBlockHeight
}

block, err := c.RPCClient.Block(&height)
if err != nil {
return nil, fmt.Errorf("block query failed: %w", err)
}

return block, nil
}

// BlockResult gets the block results at height, if any
// Height must be larger than 0
func (c *Client) BlockResult(height int64) (*ctypes.ResultBlockResults, error) {
if err := c.validateRPCClient(); err != nil {
return nil, ErrMissingRPCClient
}

if height <= 0 {
return nil, ErrInvalidBlockHeight
}

blockResults, err := c.RPCClient.BlockResults(&height)
if err != nil {
return nil, fmt.Errorf("block query failed: %w", err)
}

return blockResults, nil
}

// LatestBlockHeight gets the latest block height on the chain
func (c *Client) LatestBlockHeight() (int64, error) {
if err := c.validateRPCClient(); err != nil {
return 0, ErrMissingRPCClient
}

status, err := c.RPCClient.Status()
if err != nil {
return 0, fmt.Errorf("block number query failed: %w", err)
}

return status.SyncInfo.LatestBlockHeight, nil
}
187 changes: 187 additions & 0 deletions gno.land/pkg/gnoclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1079,3 +1079,190 @@ func TestAddPackageErrors(t *testing.T) {
})
}
}

// Block tests
func TestBlock(t *testing.T) {
t.Parallel()

height := int64(5)
client := &Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{
block: func(height *int64) (*ctypes.ResultBlock, error) {
return &ctypes.ResultBlock{
BlockMeta: &types.BlockMeta{
BlockID: types.BlockID{},
Header: types.Header{},
},
Block: &types.Block{
Header: types.Header{
Height: *height,
},
Data: types.Data{},
LastCommit: nil,
},
}, nil
},
},
}

block, err := client.Block(height)
require.NoError(t, err)
assert.Equal(t, height, block.Block.GetHeight())
}

func TestBlockResults(t *testing.T) {
t.Parallel()

height := int64(5)
client := &Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{
blockResults: func(height *int64) (*ctypes.ResultBlockResults, error) {
return &ctypes.ResultBlockResults{
Height: *height,
Results: nil,
}, nil
},
},
}

blockResult, err := client.BlockResult(height)
require.NoError(t, err)
assert.Equal(t, height, blockResult.Height)
}

func TestLatestBlockHeight(t *testing.T) {
t.Parallel()

latestHeight := int64(5)

client := &Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{
status: func() (*ctypes.ResultStatus, error) {
return &ctypes.ResultStatus{
SyncInfo: ctypes.SyncInfo{
LatestBlockHeight: latestHeight,
},
}, nil
},
},
}

head, err := client.LatestBlockHeight()
require.NoError(t, err)
assert.Equal(t, latestHeight, head)
}

func TestBlockErrors(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
client Client
height int64
expectedError error
}{
{
name: "Invalid RPCClient",
client: Client{
&mockSigner{},
nil,
},
height: 1,
expectedError: ErrMissingRPCClient,
},
{
name: "Invalid height",
client: Client{
&mockSigner{},
&mockRPCClient{},
},
height: 0,
expectedError: ErrInvalidBlockHeight,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

res, err := tc.client.Block(tc.height)
assert.Nil(t, res)
assert.ErrorIs(t, err, tc.expectedError)
})
}
}

func TestBlockResultErrors(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
client Client
height int64
expectedError error
}{
{
name: "Invalid RPCClient",
client: Client{
&mockSigner{},
nil,
},
height: 1,
expectedError: ErrMissingRPCClient,
},
{
name: "Invalid height",
client: Client{
&mockSigner{},
&mockRPCClient{},
},
height: 0,
expectedError: ErrInvalidBlockHeight,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

res, err := tc.client.BlockResult(tc.height)
assert.Nil(t, res)
assert.ErrorIs(t, err, tc.expectedError)
})
}
}

func TestLatestBlockHeightErrors(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
client Client
expectedError error
}{
{
name: "Invalid RPCClient",
client: Client{
&mockSigner{},
nil,
},
expectedError: ErrMissingRPCClient,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

res, err := tc.client.LatestBlockHeight()
assert.Equal(t, int64(0), res)
assert.ErrorIs(t, err, tc.expectedError)
})
}
}

0 comments on commit cb90aaa

Please sign in to comment.