Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rpc/backend/comet.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (b *Backend) CometBlockResultByNumber(ctx context.Context, height *int64) (
}
res, err := b.RPCClient.BlockResults(ctx, height)
if err != nil {
return nil, fmt.Errorf("failed to fetch block result from CometBFT %d: %w", *height, err)
return nil, fmt.Errorf("failed to fetch block result from CometBFT %d: %w", heightAttr, err)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ambiguous height in error message when caller passes nil

When CometBlockResultByNumber is called with a nil height (i.e., "fetch latest block"), heightAttr defaults to 0. If BlockResults then fails, the error message will read "failed to fetch block result from CometBFT 0: ...", which is indistinguishable from a caller that explicitly requested block 0 (which is also remapped to nil). This is a pre-existing ambiguity but the fix keeps it intact.

Consider using a more descriptive format string to differentiate the two cases, for example:

heightStr := "latest"
if heightAttr != 0 {
    heightStr = fmt.Sprintf("%d", heightAttr)
}
return nil, fmt.Errorf("failed to fetch block result from CometBFT (height=%s): %w", heightStr, err)

This is a minor style concern and does not affect correctness.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

}

return res, nil
Expand Down
79 changes: 79 additions & 0 deletions rpc/backend/comet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package backend

import (
"context"
"errors"
"testing"

"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"

"github.com/cosmos/evm/rpc/backend/mocks"
)

func TestCometBlockResultByNumber_NilHeightErrorPath(t *testing.T) {
testCases := []struct {
name string
height *int64
rpcErr error
expectErr bool
}{
{
name: "nil height, BlockResults succeeds",
height: nil,
rpcErr: nil,
expectErr: false,
},
{
name: "non-zero height, BlockResults succeeds",
height: func() *int64 { h := int64(5); return &h }(),
rpcErr: nil,
expectErr: false,
},
{
name: "zero height remapped to nil, BlockResults errors - must not panic",
height: func() *int64 { h := int64(0); return &h }(),
rpcErr: errors.New("connection timeout"),
expectErr: true,
},
{
name: "nil height, BlockResults errors",
height: nil,
rpcErr: errors.New("pruned state"),
expectErr: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
backend := setupMockBackend(t)
mockClient := backend.ClientCtx.Client.(*mocks.Client)

// The function remaps height=0 to nil before calling BlockResults,
// so always mock with nil when input is 0.
var mockHeight *int64
if tc.height != nil && *tc.height != 0 {
mockHeight = tc.height
}

if tc.rpcErr != nil {
mockClient.On("BlockResults", mock.Anything, mockHeight).
Return((*tmrpctypes.ResultBlockResults)(nil), tc.rpcErr).Once()
} else {
mockClient.On("BlockResults", mock.Anything, mockHeight).
Return(&tmrpctypes.ResultBlockResults{Height: 1}, nil).Once()
}

require.NotPanics(t, func() {
_, err := backend.CometBlockResultByNumber(context.Background(), tc.height)
if tc.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
})
}
}