Skip to content

chore: Use stored block data instead #90

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

chore: Use stored block data instead #90

wants to merge 3 commits into from

Conversation

alpe
Copy link
Contributor

@alpe alpe commented May 12, 2025

After #88 to avoid merge conflicts in their branch

  • Re-use existing logic to build comet abci types
  • Create common package to avoid cyclic dependency

Overview

Summary by CodeRabbit

  • New Features
    • Introduced utilities for converting Rollkit blockchain data structures to ABCI types, enabling easier integration with CometBFT.
    • Added transaction proof generation, providing Merkle proofs for transactions on demand.
  • Refactor
    • Simplified and centralized block and header conversion logic by moving it to a shared utility package.
    • Updated tests to use helper functions for mock creation and improved test robustness with stricter assertions.
    • Refactored transaction-related code to modularize proof construction and improve error handling.
  • Chores
    • Removed unused imports and redundant code for cleaner, more maintainable files.

Copy link
Contributor

coderabbitai bot commented May 12, 2025

Walkthrough

The changes refactor ABCI conversion utilities by moving them from pkg/rpc/core/utils.go to a new pkg/common/convert.go file, updating all internal references accordingly. Test code is improved for maintainability, and test assertions are strengthened. Unused imports are removed, and helper functions are introduced for mock and fixture creation. Transaction proof generation logic is added and tested with mock stores.

Changes

File(s) Change Summary
pkg/common/convert.go New file providing conversion utilities between Rollkit and ABCI types, including functions for converting headers, blocks, block metas, and commits.
pkg/rpc/core/utils.go Removed all ABCI conversion functions and related imports; now delegates block meta conversion to the new common package.
pkg/rpc/core/blocks.go Updated all calls to ABCI conversion functions to use the new common package implementations. No logic changes.
pkg/adapter/adapter_test.go Refactored test to use a new mock app helper, simplified adapter setup, added a block header fixture, removed unused imports, and improved test clarity.
pkg/rpc/core/utils_test.go Replaced assert with require in node ID truncation tests for stricter error handling and improved test robustness.
pkg/rpc/core/tx.go Added transaction proof generation logic with helper functions to build Merkle proofs for transactions.
pkg/rpc/core/tx_test.go Enhanced tests to cover transaction proof generation scenarios using a new mock Rollkit store.

Sequence Diagram(s)

sequenceDiagram
    participant RPC as RPC Layer
    participant Common as Common Convert Utilities
    participant RollkitStore as Rollkit Store
    participant Merkle as Merkle Utilities
    participant ABCI as ABCI Types

    RPC->>Common: ToABCIBlockMeta(header, data)
    Common->>Common: ToABCIBlock(header, data)
    Common->>Common: ToABCIHeader(header)
    Common->>ABCI: Construct ABCI BlockMeta
    Common-->>RPC: Return ABCI BlockMeta

    RPC->>RollkitStore: GetBlockData(height)
    RollkitStore-->>RPC: Return block data with txs
    RPC->>Merkle: Compute Merkle root and proof for tx
    Merkle-->>RPC: Return TxProof
    RPC-->>Client: Return Tx with proof (if requested)
Loading

Possibly related PRs

  • feat: use datastore directly for state store #66: Changes Adapter struct and constructor to use a new Store type wrapping a datastore and adds a RollkitStore field, altering state loading and saving. Related to adapter and datastore handling in this PR.
  • chore: fix rollkit store creation #86: Fixes rollkit store creation by adding key prefix wrapping inside NewABCIExecutor, related to the adapter and store setup simplifications in this PR.

Suggested reviewers

  • tac0turtle

Poem

🐇 Hopping through code with a twitch of my nose,
Moved conversions where the common path goes.
Proofs now arise from the Merkle tree's root,
Tests tighter than carrots, no room for dispute.
With helpers and fixtures, the changes take flight—
This bunny's refactor is out of sight! 🌿✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@alpe alpe force-pushed the alex/event_data branch from 3fb3266 to 6454728 Compare May 14, 2025 10:21
if len(invalidHexStr) < NodeIDByteLength*2 {
invalidHexStr = strings.Repeat("x", NodeIDByteLength*2) // Make it long enough but still invalid hex
}
require.GreaterOrEqual(t, len(invalidHexStr), NodeIDByteLength*2)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

more readable and fails fast when const is changed

@alpe alpe requested a review from randygrok May 14, 2025 10:22
@alpe alpe marked this pull request as ready for review May 14, 2025 10:26
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
pkg/common/convert.go (5)

14-16: Documentation mismatch with implementation.

The function comment states that the caller should fill fields like ChainID, but the implementation already sets ChainID at line 38 from header.ChainID(). Please update the documentation to reflect the actual behavior.

// ToABCIHeader converts Rollkit header to Header format defined in ABCI.
-// Caller should fill all the fields that are not available in Rollkit header (like ChainID).
+// The function attempts to fill all fields from the Rollkit header.

39-40: Consider documenting why ValidatorsHash and NextValidatorsHash use the same value.

Both fields are set to the same value (header.ValidatorHash). While this might be intentional for your use case, it's worth documenting why, especially since in typical blockchain implementations these could be different when validator sets change.


52-55: Verify the error handling for missing proposer address.

Good defensive programming by checking for an empty proposer address. Consider adding a more specific error message that includes context about which block height is affected to aid in debugging.

if len(header.ProposerAddress) == 0 {
-  return nil, errors.New("proposer address is not set")
+  return nil, fmt.Errorf("proposer address is not set for block at height %d", header.Height())
}

95-112: Documentation doesn't match implementation.

The function comment states that the caller needs to fill fields like ValidatorAddress and Timestamp, but the implementation already sets these fields. Please update the documentation to accurately reflect what the function does vs. what the caller needs to provide.

// ToABCICommit returns a commit format defined by ABCI.
-// Other fields (especially ValidatorAddress and Timestamp of Signature) have to be filled by caller.
+// The function sets ValidatorAddress and Timestamp based on the provided parameters.

46-93: Consider adding nil checks for pointer parameters.

The ToABCIBlock and ToABCIBlockMeta functions take pointer parameters (header and data) but don't check if they're nil. Consider adding defensive nil checks to avoid potential panics.

func ToABCIBlock(header *rlktypes.SignedHeader, data *rlktypes.Data) (*cmttypes.Block, error) {
+   if header == nil {
+       return nil, errors.New("header cannot be nil")
+   }
+   if data == nil {
+       return nil, errors.New("data cannot be nil")
+   }
    abciHeader, err := ToABCIHeader(&header.Header)
    // Rest of the function...
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01e9349 and 70515e7.

📒 Files selected for processing (5)
  • pkg/adapter/adapter_test.go (4 hunks)
  • pkg/common/convert.go (1 hunks)
  • pkg/rpc/core/blocks.go (7 hunks)
  • pkg/rpc/core/utils.go (2 hunks)
  • pkg/rpc/core/utils_test.go (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
pkg/rpc/core/utils_test.go (1)
pkg/rpc/core/utils.go (2)
  • NodeIDByteLength (14-14)
  • TruncateNodeID (97-106)
pkg/rpc/core/blocks.go (1)
pkg/common/convert.go (3)
  • ToABCIBlock (46-77)
  • ToABCICommit (97-112)
  • ToABCIBlockMeta (80-93)
pkg/rpc/core/utils.go (1)
pkg/common/convert.go (1)
  • ToABCIBlockMeta (80-93)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Test with Rollkit Chain
🔇 Additional comments (24)
pkg/rpc/core/utils_test.go (5)

11-11: LGTM: Adding require package for stronger test assertions

The addition of the require package is appropriate as it allows failing fast when assertions don't pass.


25-25: Stronger assertion improves test reliability

Changing from assert.NoError to require.NoError ensures the test fails immediately if this critical check doesn't pass, preventing misleading follow-up errors.


41-41: More readable assertion for input validation

Using require.GreaterOrEqual directly is cleaner than the previous conditional approach, making the minimum length requirement explicit and failing fast when the test constant is changed.


43-43: Consistent use of require for error checking

Good change to use require.Error for consistency with other assertions that use the require package.


57-57: Improved assertion for exact length test case

Using require.NoError is appropriate here as subsequent assertions depend on this check passing.

pkg/rpc/core/blocks.go (8)

17-18: LGTM: Using the common package for ABCI type conversion

Good refactoring to use the common package for ABCI type conversions, which helps prevent cyclic dependencies.


74-74: Proper update to use common package conversion function

Correctly updated to use common.ToABCIBlock instead of the local implementation.


119-119: Consistent conversion with common package

The conversion to ABCI block now uses the centralized implementation from the common package.


143-143: Using common conversion for block by hash

Updated BlockByHash to use the common implementation for proper consistency.


176-176: Consistent commit conversion using common package

The commit conversion is now using the centralized implementation from the common package.


178-178: Block conversion for commit also updated

Block conversion for commit response now uses the common implementation.


243-243: Updated header by hash to use common conversion

The block meta conversion in HeaderByHash now correctly uses the common implementation.


285-285: Blockchain info now uses common conversion

The block meta conversion in BlockchainInfo now uses the common implementation, maintaining consistency across all conversion points.

pkg/rpc/core/utils.go (2)

11-11: LGTM: Added common package import

Correctly added import for the common package where conversion functions have been moved.


55-55: Updated to use common package implementation

Good update to use common.ToABCIBlockMeta from the common package instead of the local implementation, centralizing conversion logic.

pkg/adapter/adapter_test.go (6)

23-23: LGTM: Added rollkit types import for header fixture

This import is needed for the new header fixture helper function.


77-77: Improved test readability with mockApp helper

Using the new mockApp helper function makes the test setup cleaner and more maintainable.


80-84: Simplified adapter setup for testing

The adapter setup is now more concise and clearer, with direct field assignments after initialization.


85-86: Added block data setup for tests

Good addition of saving block data using the headerFixture, making the test more realistic.


134-150: Well-structured mockApp helper function

The new helper function provides a clean way to create a mock ABCI app with default implementations that can be customized through the mutator functions. This improves code reuse and test readability.


179-187: Useful headerFixture for creating test headers

The new helper function creates a consistent test header fixture, improving code reuse and readability. Good use of the types imported from rollkit.

pkg/common/convert.go (3)

44-46: Good implementation with proper validation.

The function correctly converts Rollkit block components to ABCI block format and includes validation to ensure the returned block meets requirements.


79-93: Well-structured with good error handling.

The ToABCIBlockMeta function properly reuses the ToABCIBlock function and includes appropriate error handling. The implementation is clean and efficient.


3-12: Clean import organization.

The imports are well-organized with appropriate grouping between standard library packages, external dependencies, and internal packages. This follows Go best practices.

Comment on lines +59 to +62
// This assumes that we have only one signature
if len(abciCommit.Signatures) == 1 {
abciCommit.Signatures[0].ValidatorAddress = header.ProposerAddress
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Hardcoded assumption about signature count.

The code assumes there will only be one signature, which is currently correct but may not remain true if requirements change. Consider making this more robust by iterating through signatures or documenting this assumption clearly.


🏁 Script executed:

#!/bin/bash
# Check if there are any Rollkit files that handle multiple signatures
rg -A 2 -B 2 "[]signature" --type go -g "*.go" 

Length of output: 122


🏁 Script executed:

#!/bin/bash
# Search for all occurrences of the Signatures field across Go files
rg -n "Signatures" -g "*.go"

Length of output: 518


Avoid hardcoding single‐signature assumption

The code currently assumes there will only ever be one signature in Commit.Signatures, which could break if multi‐signature support is added. This pattern appears in:

  • pkg/common/convert.go

    • Lines 60–62:
      // This assumes that we have only one signature
      if len(abciCommit.Signatures) == 1 {
          abciCommit.Signatures[0].ValidatorAddress = header.ProposerAddress
      }
    • Line 105:
      Signatures: []cmttypes.CommitSig{{ /* … */ }},
  • pkg/rpc/utils.go

    • Lines 91–92:
      if len(abciCommit.Signatures) == 1 {
          abciCommit.Signatures[0].ValidatorAddress = header.ProposerAddress
      }
    • Line 136:
      Signatures: make([]cmttypes.CommitSig, 1),
    • Line 144:
      tmCommit.Signatures[0] = commitSig

Rather than special-casing a single signature, consider uniformly handling all signatures. For example, replace the one-off check/assignment with:

- if len(abciCommit.Signatures) == 1 {
-     abciCommit.Signatures[0].ValidatorAddress = header.ProposerAddress
- }
+ for i := range abciCommit.Signatures {
+     abciCommit.Signatures[i].ValidatorAddress = header.ProposerAddress
+ }

And update the static slice constructions to collect or initialize all signatures dynamically. If single-signature is an explicit protocol limitation, add a clear comment/TODO in one shared helper to document this behavior.

Comment on lines +135 to +143
func proofTXExists(txs []rlktypes.Tx, i uint32) cmttypes.TxProof {
hl := hashList(txs)
root, proofs := merkle.ProofsFromByteSlices(hl)

return cmttypes.TxProof{
RootHash: root,
Data: cmttypes.Tx(txs[i]),
Proof: *proofs[i],
}
Copy link

Choose a reason for hiding this comment

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

The proofTXExists function needs a bounds check before accessing txs[i] to prevent a panic when the transaction index is out of bounds. If a client requests a proof for a non-existent transaction index, the server would crash. Consider adding validation like:

if int(i) >= len(txs) {
    // Return an error or handle the out-of-bounds case appropriately
}

This would make the function more robust against invalid input parameters.

Suggested change
func proofTXExists(txs []rlktypes.Tx, i uint32) cmttypes.TxProof {
hl := hashList(txs)
root, proofs := merkle.ProofsFromByteSlices(hl)
return cmttypes.TxProof{
RootHash: root,
Data: cmttypes.Tx(txs[i]),
Proof: *proofs[i],
}
func proofTXExists(txs []rlktypes.Tx, i uint32) (cmttypes.TxProof, error) {
if int(i) >= len(txs) {
return cmttypes.TxProof{}, fmt.Errorf("transaction index %d out of bounds (len=%d)", i, len(txs))
}
hl := hashList(txs)
root, proofs := merkle.ProofsFromByteSlices(hl)
return cmttypes.TxProof{
RootHash: root,
Data: cmttypes.Tx(txs[i]),
Proof: *proofs[i],
}, nil
}

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
pkg/rpc/core/tx.go (1)

108-113: Avoid repeated GetBlockData calls for the same block

TxSearch invokes buildProof for every tx, which in turn hits the Rollkit store every time.
When a result set contains multiple transactions from the same height (common case), this becomes an O(n²) pattern on the store.

Consider memoising proofs or at least the Data by blockHeight within the loop:

dataCache := map[int64]*rlktypes.Data{}
...
if prove {
    d, ok := dataCache[r.Height]
    if !ok {
        _, d, err = env.Adapter.RollkitStore.GetBlockData(ctx.Context(), uint64(r.Height))
        if err != nil { ... }
        dataCache[r.Height] = d
    }
    proof = proofTXExists(d.Txs, r.Index)
}

This keeps complexity O(n) and drastically cuts I/O.

pkg/rpc/core/tx_test.go (2)

74-88: assert.Empty(result.Proof.Proof) may give false positives

merkle.Proof{} is not nil; assert.Empty only checks length/zero-value on certain types.
Use assert.Zero(result.Proof) or explicitly compare to the zero struct to avoid silent failures.


118-120: Stale TODO comment

The proof logic is now implemented and tested—this TODO is obsolete and could confuse future readers. Please remove it.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70515e7 and 5d76b9a.

📒 Files selected for processing (2)
  • pkg/rpc/core/tx.go (4 hunks)
  • pkg/rpc/core/tx_test.go (6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
pkg/rpc/core/tx_test.go (4)
pkg/rpc/core/mocks_test.go (1)
  • MockRollkitStore (113-115)
pkg/rpc/core/env.go (1)
  • Environment (27-33)
pkg/adapter/adapter.go (1)
  • Adapter (53-69)
pkg/rpc/core/tx.go (2)
  • Tx (22-49)
  • TxSearch (54-125)
pkg/rpc/core/tx.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (53-69)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: test / Run Unit Tests
  • GitHub Check: lint / golangci-lint
  • GitHub Check: Test with Rollkit Chain
🔇 Additional comments (1)
pkg/rpc/core/tx_test.go (1)

233-278: Test duplicates store calls; consider caching proof generation

The with proofs sub-test expects three separate GetBlockData calls, even two for the same height.
This mirrors the current implementation, but it couples the test to an inefficient API usage pattern.
Refactoring production code to cache data (see previous comment) will break this test.

Instead, set expectations per height, e.g.:

mockStore.
    On("GetBlockData", mock.Anything, uint64(10)).
    Return(nil, dataH10, nil).Twice() // if you really need two calls

or verify via AssertNumberOfCalls after the fact.
This keeps the test robust against internal optimisations.

Comment on lines +127 to +133
func buildProof(ctx *rpctypes.Context, blockHeight int64, txIndex uint32) (cmttypes.TxProof, error) {
_, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), uint64(blockHeight))
if err != nil {
return cmttypes.TxProof{}, fmt.Errorf("failed to get block data: %w", err)
}
return proofTXExists(data.Txs, txIndex), nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate fetched block data and index bounds to avoid panics

buildProof blindly assumes that

  1. the Rollkit store returns non-nil Data,
  2. Data.Txs is non-empty, and
  3. txIndex is within bounds.

Any of these assumptions being violated will lead to a runtime panic inside proofTXExists (slice index out of range). Please guard against these cases and surface a descriptive error instead.

 func buildProof(ctx *rpctypes.Context, blockHeight int64, txIndex uint32) (cmttypes.TxProof, error) {
-	_, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), uint64(blockHeight))
-	if err != nil {
-		return cmttypes.TxProof{}, fmt.Errorf("failed to get block data: %w", err)
-	}
-	return proofTXExists(data.Txs, txIndex), nil
+	_, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), uint64(blockHeight))
+	if err != nil {
+		return cmttypes.TxProof{}, fmt.Errorf("failed to get block data: %w", err)
+	}
+	if data == nil {
+		return cmttypes.TxProof{}, fmt.Errorf("block %d has no data section", blockHeight)
+	}
+	if txIndex >= uint32(len(data.Txs)) {
+		return cmttypes.TxProof{}, fmt.Errorf(
+			"tx index %d out of bounds (block contains %d txs)", txIndex, len(data.Txs),
+		)
+	}
+	return proofTXExists(data.Txs, txIndex), nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func buildProof(ctx *rpctypes.Context, blockHeight int64, txIndex uint32) (cmttypes.TxProof, error) {
_, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), uint64(blockHeight))
if err != nil {
return cmttypes.TxProof{}, fmt.Errorf("failed to get block data: %w", err)
}
return proofTXExists(data.Txs, txIndex), nil
}
func buildProof(ctx *rpctypes.Context, blockHeight int64, txIndex uint32) (cmttypes.TxProof, error) {
_, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), uint64(blockHeight))
if err != nil {
return cmttypes.TxProof{}, fmt.Errorf("failed to get block data: %w", err)
}
if data == nil {
return cmttypes.TxProof{}, fmt.Errorf("block %d has no data section", blockHeight)
}
if txIndex >= uint32(len(data.Txs)) {
return cmttypes.TxProof{}, fmt.Errorf(
"tx index %d out of bounds (block contains %d txs)", txIndex, len(data.Txs),
)
}
return proofTXExists(data.Txs, txIndex), nil
}

Comment on lines +135 to +144
func proofTXExists(txs []rlktypes.Tx, i uint32) cmttypes.TxProof {
hl := hashList(txs)
root, proofs := merkle.ProofsFromByteSlices(hl)

return cmttypes.TxProof{
RootHash: root,
Data: cmttypes.Tx(txs[i]),
Proof: *proofs[i],
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

proofTXExists still needs an internal bounds check

Even after external validation, this helper should remain defensive to prevent misuse from other call-sites that might appear in the future.

 func proofTXExists(txs []rlktypes.Tx, i uint32) cmttypes.TxProof {
+	if int(i) >= len(txs) {
+		return cmttypes.TxProof{} // defensive default
+	}
 	hl := hashList(txs)
 	root, proofs := merkle.ProofsFromByteSlices(hl)

 	return cmttypes.TxProof{
 		RootHash: root,
 		Data:     cmttypes.Tx(txs[i]),
 		Proof:    *proofs[i],
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func proofTXExists(txs []rlktypes.Tx, i uint32) cmttypes.TxProof {
hl := hashList(txs)
root, proofs := merkle.ProofsFromByteSlices(hl)
return cmttypes.TxProof{
RootHash: root,
Data: cmttypes.Tx(txs[i]),
Proof: *proofs[i],
}
}
func proofTXExists(txs []rlktypes.Tx, i uint32) cmttypes.TxProof {
if int(i) >= len(txs) {
return cmttypes.TxProof{} // defensive default
}
hl := hashList(txs)
root, proofs := merkle.ProofsFromByteSlices(hl)
return cmttypes.TxProof{
RootHash: root,
Data: cmttypes.Tx(txs[i]),
Proof: *proofs[i],
}
}

Comment on lines +53 to +69
t.Run("Success with proofs", func(t *testing.T) {
mockTxIndexer.On("Get", sampleHash).Return(sampleTxResult, nil).Once()
mockStore.On("GetBlockData", mock.Anything, uint64(sampleHeight)).Return(nil,
&rktypes.Data{Txs: rktypes.Txs{[]byte{0}, []byte{1}}}, nil).Once()
result, err := Tx(ctx, sampleHash, true)

result, err := Tx(ctx, sampleHash, false) // prove = false
require.NoError(err)
require.NotNil(result)
assert.Equal(sampleHash, []byte(result.Hash))
assert.Equal(sampleHeight, result.Height)
assert.Equal(sampleIndex, result.Index)
assert.Equal(sampleResult, result.TxResult)
assert.Equal(sampleTx, result.Tx)
assert.Equal(int64(2), result.Proof.Proof.Total)
assert.Equal(int64(1), result.Proof.Proof.Index)
assert.NotEmpty(result.Proof.Proof.LeafHash)

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Mocked block data inconsistent for the same height

Two different GetBlockData expectations are set for height 10 but they return different transactions sets (len=2 vs len=3).
A real block cannot change between calls; this hides potential bugs and weakens the proof verification.

Align both expectations to return identical data or cache the response once.

Comment on lines 26 to 35
mockTxIndexer := new(MockTxIndexer)
mockStore := new(MockRollkitStore)

env = &Environment{
TxIndexer: mockTxIndexer,
Logger: cmtlog.NewNopLogger(),
Adapter: &adapter.Adapter{}, // Minimal adapter needed? Add mocks if GetBlockData is used (for prove=true)
Adapter: &adapter.Adapter{
RollkitStore: mockStore,
},
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Global env mutation can introduce test races

All sub-tests mutate the package-level env singleton.
If other test files in the same package run in parallel (t.Parallel()), this leads to data races and flaky tests.

Prefer creating an Environment per test and injecting it through parameters, or guard access with a mutex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants