Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gnoclient): support fetching blocks, block results, latest block number #1910

Merged
merged 15 commits into from
Apr 29, 2024
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 @@
"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 @@

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)

Check warning on line 142 in gno.land/pkg/gnoclient/client_queries.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_queries.go#L142

Added line #L142 was not covered by tests
}

return block, nil
}

// BlockResult gets the latest 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)

Check warning on line 161 in gno.land/pkg/gnoclient/client_queries.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_queries.go#L161

Added line #L161 was not covered by tests
}

return blockResults, nil
}

// Head gets the head of the chain (latest block height)
func (c *Client) Head() (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)

Check warning on line 175 in gno.land/pkg/gnoclient/client_queries.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_queries.go#L175

Added line #L175 was not covered by tests
}

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 TestHead(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.Head()
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 TestHeadErrors(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.Head()
assert.Equal(t, int64(0), res)
assert.ErrorIs(t, err, tc.expectedError)
})
}
}
Loading