Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

### Bug fixes

* [#2175](https://github.com/crypto-org-chain/cronos/pull/2175) fix(rpc): guard `TxsResults` length before indexing by block tx position.
* [#2176](https://github.com/crypto-org-chain/cronos/pull/2176) fix(app): retry block list decryption instead of caching the blob before it is applied.
* [#2155](https://github.com/crypto-org-chain/cronos/pull/2155) fix(mempool): size tx-cache-size and max-tx-bytes from mempool config directly.
* [#2169](https://github.com/crypto-org-chain/cronos/pull/2169) fix(cronos): add safe multiply int check during voucher conversion to EVM coins.
Expand Down
22 changes: 17 additions & 5 deletions x/cronos/rpc/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ func NewCronosAPI(
}
}

func checkTxsResultsLength(block *coretypes.ResultBlock, blockResults *coretypes.ResultBlockResults) error {
if len(blockResults.TxsResults) != len(block.Block.Txs) {
return fmt.Errorf(
"mismatched tx results length at height %d: block has %d txs, but got %d tx results",
block.Block.Height, len(block.Block.Txs), len(blockResults.TxsResults),
)
}
return nil
}

func (api *CronosAPI) getBlockDetail(blockNrOrHash rpctypes.BlockNumberOrHash) (
resBlock *coretypes.ResultBlock,
blockNumber int64,
Expand All @@ -94,22 +104,24 @@ func (api *CronosAPI) getBlockDetail(blockNrOrHash rpctypes.BlockNumberOrHash) (
baseFee *big.Int,
err error,
) {
var blockNum rpctypes.BlockNumber
resBlock, err = api.getBlock(blockNrOrHash)
if err != nil {
api.logger.Debug("block not found", "height", blockNrOrHash, "error", err.Error())
return resBlock, blockNumber, blockHash, blockRes, baseFee, err
return nil, 0, "", nil, nil, err
}
blockNumber = resBlock.Block.Height
blockHash = common.BytesToHash(resBlock.Block.Header.Hash()).Hex()
blockRes, err = api.backend.TendermintBlockResultByNumber(&blockNumber)
if err != nil {
api.logger.Debug("failed to retrieve block results", "height", blockNum, "error", err.Error())
return resBlock, blockNumber, blockHash, blockRes, baseFee, err
api.logger.Debug("failed to retrieve block results", "height", blockNumber, "error", err.Error())
return nil, 0, "", nil, nil, err
}
if err = checkTxsResultsLength(resBlock, blockRes); err != nil {

@thomas-nguy thomas-nguy Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You are still returning resBlock and blockRes which could be missuses, perhaps some refactoring could be done to clarify the semantic of resBlock and resBlock ?

Do we allow them to have different length or not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

renamed to block and blockResults, and for the returning only return the err info.
This is to match with the consensus raw data check, the length should always the same. if the data mismatch, meaning we need to migrate/patch the data like fix-unlucky-tx in v0.7 before.

return nil, 0, "", nil, nil, err
}
baseFee, err = api.backend.BaseFee(blockRes)
if err != nil {
return resBlock, blockNumber, blockHash, blockRes, baseFee, err
return nil, 0, "", nil, nil, err
}
return resBlock, blockNumber, blockHash, blockRes, baseFee, err
}
Expand Down
81 changes: 81 additions & 0 deletions x/cronos/rpc/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package rpc

import (
"testing"

abci "github.com/cometbft/cometbft/abci/types"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
cmttypes "github.com/cometbft/cometbft/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

func newResBlock(t *testing.T, numTxs int) *coretypes.ResultBlock {
t.Helper()
txs := make(cmttypes.Txs, numTxs)
for i := range txs {
txs[i] = cmttypes.Tx{byte(i)}
}
return &coretypes.ResultBlock{
Block: &cmttypes.Block{
Header: cmttypes.Header{Height: 100},
Data: cmttypes.Data{Txs: txs},
},
}
}

func newBlockRes(t *testing.T, numResults int) *coretypes.ResultBlockResults {
t.Helper()
results := make([]*abci.ExecTxResult, numResults)
for i := range results {
results[i] = &abci.ExecTxResult{}
}
return &coretypes.ResultBlockResults{TxsResults: results}
}

type CheckTxsResultsLengthTestSuite struct {
suite.Suite
}

func TestCheckTxsResultsLengthTestSuite(t *testing.T) {
suite.Run(t, new(CheckTxsResultsLengthTestSuite))
}

func (s *CheckTxsResultsLengthTestSuite) TestCheckTxsResultsLength() {
testCases := []struct {
name string
numTxs int
numRes int
wantErr string
}{
{
name: "matching lengths returns no error",
numTxs: 3,
numRes: 3,
},
{
name: "fewer tx results than txs returns error instead of panicking",
numTxs: 3,
numRes: 2,
wantErr: "mismatched tx results length at height 100: block has 3 txs, but got 2 tx results",
},
{
name: "more tx results than txs returns error",
numTxs: 2,
numRes: 3,
wantErr: "mismatched tx results length at height 100: block has 2 txs, but got 3 tx results",
},
}

for _, tc := range testCases {
s.Run(tc.name, func() {
t := s.T()
err := checkTxsResultsLength(newResBlock(t, tc.numTxs), newBlockRes(t, tc.numRes))
if tc.wantErr == "" {
require.NoError(t, err)
return
}
require.EqualError(t, err, tc.wantErr)
})
}
}
Loading