Skip to content

Conversation

@randygrok
Copy link
Contributor

@randygrok randygrok commented Dec 3, 2025

Parent Epic: #2796

  • In order to advance the removing of core/da the next step is to create a client that won't be wired in this PR that consumes the new blob API.

New client (namespace blob) - New client not yet wired

  • Json RPC: da/jsonrpc/blob/client.go opens JSON-RPC to Celestia’s blob namespace.
  • Business wrapper: block/internal/da/blob_client.go wraps the jsonrpc to add additional logic for our business logic, some things that were done on the old da namespace is not done anymore and is expected to be done by the client.

Both clients coexist for now (old and new), but the new one is not wired.

The idea is to wire the New client in another PR, because it is the biggest part and it will generate a lot of code changes.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 3, 2025

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedDec 11, 2025, 7:43 AM

@claude
Copy link
Contributor

claude bot commented Dec 3, 2025

Claude finished @randygrok's task in 4m 38s —— View job


Comprehensive Code Review

I've completed a thorough analysis of this PR. Here are my findings organized by priority and category:


🔴 Critical Issues

1. Code Duplication - Errors and Types (HIGH PRIORITY)

Location: block/internal/da/blob_client.go:16-25 and block/internal/da/types.go

This PR duplicates error definitions and type definitions that already exist in core/da:

  • Errors (blob_client.go:16-25): Duplicates all error variables from core/da/errors.go
  • Types (types.go): Duplicates StatusCode, ResultSubmit, ResultRetrieve, BaseResult, and type aliases

Impact: This violates DRY principles and creates maintenance burden. When errors or types change in core/da, both locations must be updated.

Recommendation:

  • Remove block/internal/da/types.go entirely
  • Remove error declarations from block/internal/da/blob_client.go:16-25
  • Import and use types from core/da package (e.g., coreda "github.com/evstack/ev-node/core/da")
  • Update all references to use coreda.StatusCode, coreda.ResultSubmit, etc.

Note: I see from the review comments that you mentioned "this is temporary, will be deleted when wiring new code." However, this creates technical debt that shouldn't exist even temporarily. Consider importing from core/da now to avoid confusion and potential bugs.


2. Package Structure and Multiple Clients (HIGH PRIORITY)

Locations:

  • pkg/da/jsonrpc/client.go (new JSON-RPC client)
  • block/internal/da/blob_client.go (new business wrapper)
  • block/internal/da/client.go (existing DA client wrapper)

Issue: Multiple reviewers (julienrbrt, tac0turtle) raised concerns about having three different client implementations coexisting:

  1. Existing block/internal/da/client.go (wraps core/da.DA interface)
  2. New pkg/da/jsonrpc/client.go (JSON-RPC client)
  3. New block/internal/da/blob_client.go (wraps JSON-RPC client)

Concerns:

  • Confusing naming (client, blob_client, celestia_client references)
  • Unclear separation of responsibilities
  • Public API exposure in pkg/da/jsonrpc when this might be better kept internal
  • The transition path is not clear from the PR description

Recommendation:

  • Enhance the PR description with a clear architecture diagram showing:
    • Current state (what exists now)
    • This PR's changes (what's being added)
    • Future state (what will remain after part 3)
  • Consider whether pkg/da/jsonrpc needs to be public or should be internal
  • Clarify naming: why "blob_client" vs "client"? Consider more descriptive names that indicate their purpose

3. Module Dependencies and Circular Dependency Risk (MEDIUM-HIGH PRIORITY)

Location: go.mod:40, da/go.mod

Issue:

// go.mod:40
replace github.com/evstack/ev-node/da => ./da

Multiple reviewers (tac0turtle, julienrbrt) flagged concerns:

  • ev-node imports ev-node/da which imports ev-node/core - creates complex dependency chain
  • The comment "for reviewer, this module is planned to be removed in part 3" indicates this is temporary technical debt
  • Makes versioning and tagging difficult

Concerns:

  • Circular dependency potential
  • Tagging complexity (as mentioned by tac0turtle)
  • Why does da/ need its own go.mod if it will be removed?

Recommendation:

  • If the da/ module is truly temporary (removed in part 3), consider whether a separate go.mod is necessary
  • If it is necessary, add a more detailed explanation of why this structure is needed temporarily
  • Consider whether the code in pkg/da/jsonrpc can live directly in the main module to avoid the separate go.mod

🟡 High Priority Issues

4. Testing Approach - Hand-written vs Mockery Mocks (HIGH PRIORITY)

Location: pkg/da/jsonrpc/client_test.go

Issue: tac0turtle requested using mockery-generated mocks instead of hand-written ones.

Current state:

  • block/internal/da/blob_api_mock.go - Uses mockery ✅
  • pkg/da/jsonrpc/mocks/blob_module_mock.go - Uses mockery ✅
  • Test helpers in client_test.go create test servers manually

Assessment: The mockery configuration in .mockery.yaml is correctly set up. The tests use mockery-generated mocks appropriately. The newTestServer helper is a legitimate test utility, not a hand-written mock.

Recommendation: Current approach is acceptable. The tests properly use mockery mocks.


5. Error Handling Patterns (MEDIUM PRIORITY)

Location: block/internal/da/blob_client.go:129-145, blob_client.go:206-239

Issues:

a) String-based error matching (lines 131-145, 209-227):

case strings.Contains(err.Error(), ErrTxTimedOut.Error()):
case strings.Contains(err.Error(), ErrBlobNotFound.Error()):

Problem: This is fragile. If the RPC layer wraps errors differently, matching will fail. The code comment acknowledges this: "Handle known errors by substring because RPC may wrap them."

Better approach:

  • Use errors.Is() or errors.As() with proper error wrapping
  • Define custom error types with Is() methods
  • Have the RPC client properly wrap errors using %w

b) Mixed error handling approaches (Submit vs Retrieve):

  • Submit uses errors.Is() for context.Canceled (line 133) but string matching for everything else
  • Retrieve only uses string matching

Recommendation: Standardize on sentinel errors with proper wrapping. Update pkg/da/jsonrpc/client.go to wrap errors properly using fmt.Errorf("...: %w", err).


6. Context Timeout Management (MEDIUM PRIORITY)

Location: block/internal/da/blob_client.go:203-204

Issue:

getCtx, cancel := context.WithTimeout(ctx, c.defaultTimeout)
defer cancel()

Observations:

  • Retrieve creates a timeout context with defaultTimeout (30s)
  • Submit does NOT create a timeout context - it passes the raw context through
  • Inconsistent timeout handling between operations

Concerns:

  • Why does Retrieve need explicit timeout but Submit doesn't?
  • If the passed-in ctx already has a deadline, this creates a nested timeout which may not be intended
  • No timeout for Submit could lead to indefinite hangs

Recommendation:

  • Document why Submit doesn't use explicit timeouts (maybe the RPC layer handles it?)
  • Consider consistent timeout handling across both operations
  • Use context.WithTimeoutCause() (Go 1.21+) to provide better error context

🟢 Medium Priority Issues

7. Resource Management and Cleanup (MEDIUM PRIORITY)

Location: pkg/da/jsonrpc/client.go:19-23

Issue:

func (c *Client) Close() {
    if c != nil && c.closer != nil {
        c.closer()
    }
}

Concerns:

  • The c != nil check is redundant (method receivers can't be nil when called)
  • No error return from Close(), can't report cleanup failures
  • BlobClient in block/internal/da/blob_client.go has no Close() method at all

Recommendations:

  • Remove the c != nil check: func (c *Client) Close() { if c.closer != nil { c.closer() } }
  • Consider making Close() return error for better cleanup error handling
  • Add a Close() method to BlobClient that calls the underlying API's Close if it has one
  • Document lifecycle expectations

8. Configuration Validation (MEDIUM PRIORITY)

Location: block/internal/da/blob_client.go:56-76

Issues:

a) Silent nil return:

func NewBlobClient(api BlobAPI, cfg BlobConfig) *BlobClient {
    if api == nil {
        return nil  // Silent failure
    }
    // ...
}

Should return error or panic with clear message.

b) Namespace validation:

namespaceBz:     share.MustNewV0Namespace([]byte(cfg.Namespace)).Bytes(),
dataNamespaceBz: share.MustNewV0Namespace([]byte(cfg.DataNamespace)).Bytes(),

Uses Must* functions that panic. If config has invalid namespace, entire process crashes.

c) Missing validation:

  • No validation that cfg.Namespace != cfg.DataNamespace
  • No length checks on namespace strings
  • Logger can be zero value (would cause issues)

Recommendation:

func NewBlobClient(api BlobAPI, cfg BlobConfig) (*BlobClient, error) {
    if api == nil {
        return nil, errors.New("BlobAPI cannot be nil")
    }
    if cfg.Logger == (zerolog.Logger{}) {
        return nil, errors.New("Logger must be configured")
    }
    // Validate and create namespaces with error handling
    ns, err := share.NewV0Namespace([]byte(cfg.Namespace))
    if err != nil {
        return nil, fmt.Errorf("invalid namespace: %w", err)
    }
    // ... rest of validation
    return &BlobClient{...}, nil
}

9. Test Coverage Concerns (MEDIUM PRIORITY)

Location: Overall test coverage

Issue: Codecov reports coverage dropped from 65.94% to 63.21% (-2.74%)

Analysis:

  • New file pkg/da/jsonrpc/mocks/blob_module_mock.go: 0% coverage (322 lines) - Expected, it's generated mock code
  • New file block/internal/da/blob_client.go: 58.78% coverage (61 missing, 7 partial)
  • New file pkg/da/jsonrpc/client.go: 72.41% coverage (6 missing, 2 partial)

Missing coverage in blob_client.go:

  • Error paths for blob size limits
  • Error paths for namespace parsing
  • Various error condition branches

Recommendation:

  • Add tests for error cases:
    • Blob exceeds maxBlobSize
    • Invalid namespace bytes
    • Context cancellation scenarios
    • Future height errors
    • Timeout scenarios
  • Add integration test coverage for the full flow
  • Mock coverage doesn't count toward quality metrics - that's expected

10. Logging Practices (LOW-MEDIUM PRIORITY)

Location: block/internal/da/blob_client.go (multiple locations)

Observations:

Good practices:

  • Structured logging with context fields ✅
  • Appropriate log levels (Debug for not-found, Error for failures) ✅
  • Special handling for expected errors (StatusTooBig uses Debug) ✅

Potential improvements:

  • Line 147: Logs at Debug level for StatusTooBig but this might hide legitimate issues. Consider: is this size check expected/normal, or is it an error condition?
  • Line 149: All other submission errors use Error level - good
  • Missing context in some log messages:
    • Line 229: "failed to get blobs" - could add namespace info
    • Line 147: Could log the actual blob size vs limit

Recommendation:

if code == StatusTooBig {
    c.logger.Debug().
        Err(err).
        Uint64("blob_size", blobSize).
        Uint64("max_blob_size", c.maxBlobSize).
        Uint64("status", uint64(code)).
        Msg("Blob exceeds size limit")
} else {
    c.logger.Error().Err(err).Uint64("status", uint64(code)).Msg("DA submission failed")
}

🔵 Low Priority Issues & Observations

11. Documentation and Comments

Good:

  • Package-level documentation exists
  • Complex logic has explanatory comments
  • Function signatures are clear

Could improve:

  • pkg/da/jsonrpc/client.go: Document what "blob" namespace means
  • BlobAPI interface in blob_client.go: No godoc comments explaining its purpose vs the RPC client's BlobAPI
  • Missing examples of how to use the client
  • No migration guide for when this replaces the old client

12. Code Style and Go Idioms

Generally good adherence to Go conventions:

  • Proper use of context
  • Structured errors
  • Table-driven tests ✅
  • Proper use of testify/mock ✅

Minor observations:

  • blob_client.go:163: Empty blob check happens after submission. Consider checking before?
    if len(blobs) == 0 {
        return ResultSubmit{
            BaseResult: BaseResult{Code: StatusSuccess, BlobSize: blobSize, Height: height},
        }
    }
    This happens after a successful submit with height. Is it possible to have a successful submit with 0 blobs? If not, this is unreachable code.

13. Integration Test Quality

Location: test/e2e/da_client_integration_test.go

Positive aspects:

  • Uses Tastora framework for realistic Celestia node testing ✅
  • Tests against real Celestia node (not just mocks) ✅
  • Proper setup/teardown with Docker ✅

Observations:

  • Only read first 100 lines, but structure looks solid
  • Tests require full Celestia stack (appropriate for integration tests)
  • Skipped in short mode (good practice) ✅

14. Security Considerations

Reviewed for common security issues:

No sensitive data exposure:

  • Auth tokens properly handled via headers
  • Bearer token format correct
  • No credentials in logs

Input validation:

  • Namespace validation exists
  • Blob size limits enforced
  • Options JSON parsing with error handling

⚠️ Areas to watch:

  • No explicit max size on options []byte parameter - could be DoS vector if extremely large JSON is passed
  • Consider adding max options size limit

📊 Architecture Assessment

Current State vs. PR Changes

Before this PR:

  • block/internal/da/client.go wraps core/da.DA interface
  • DA operations go through the abstraction

This PR adds:

  • pkg/da/jsonrpc - Direct JSON-RPC client for Celestia blob API
  • block/internal/da/blob_client.go - Business logic wrapper around JSON-RPC client
  • Both old and new clients coexist (new not wired)

Questions for maintainers:

  1. Why create a new client instead of implementing core/da.DA interface with blob API?
  2. Is the goal to eventually remove core/da.DA interface entirely?
  3. Why does blob client have different method signatures than the existing DA client?
    • Old: Submit(ctx, data, gasPrice, namespace, options)
    • New: Submit(ctx, data, namespace, options) - no gasPrice parameter

Recommendation: Update PR description to clearly explain:

  • Why new client vs implementing existing interface
  • Transition plan (what gets removed when)
  • Architecture diagram showing before/during/after states

🎯 Summary and Recommendations

Must Fix Before Merge:

  1. Remove duplicate errors and types - Import from core/da instead
  2. Clarify architecture and transition plan - Update PR description
  3. Address module dependency concerns - Explain temporary da/go.mod necessity

Should Fix Before Merge:

  1. ⚠️ Improve error handling - Use proper error wrapping instead of string matching
  2. ⚠️ Add configuration validation - Return errors instead of silent nil/panic
  3. ⚠️ Standardize context timeout handling - Consistent approach between Submit/Retrieve

Nice to Have:

  1. 💡 Improve test coverage for error paths
  2. 💡 Add resource cleanup (Close methods)
  3. 💡 Enhanced logging with more context
  4. 💡 Better documentation and examples

✅ What's Done Well

  • ✅ Mockery configuration and usage
  • ✅ Integration test setup with Tastora
  • ✅ Structured logging approach
  • ✅ Table-driven unit tests
  • ✅ Proper use of contexts
  • ✅ Security considerations (token handling)

Overall Assessment: This PR introduces solid infrastructure for the Celestia blob API client, but has some architectural clarity issues and code duplication that should be addressed. The code quality is generally good, but the transition plan and relationship to existing code needs better documentation.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @randygrok, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates the application with the Celestia Data Availability layer by introducing a dedicated client for its Blob API. This enhancement allows the system to efficiently submit and retrieve data, leveraging Celestia's capabilities for data availability. The changes include the core client implementation, necessary data structures, and robust testing infrastructure, ensuring reliable interaction with the DA layer.

Highlights

  • Celestia Blob API Client: Introduced a new client for the Celestia Blob API, enabling submission and retrieval of data from the Celestia Data Availability (DA) layer.
  • DA Layer Abstraction: Created a new internal da package to encapsulate Celestia-specific logic and types, providing a structured way to interact with the DA layer.
  • Dependency Management Updates: Updated Go module dependencies across apps/evm, apps/grpc, apps/testapp, and the da module, including new Celestia-related libraries and utility packages.
  • Testing Utilities: Added an in-memory LocalBlobAPI implementation for testing purposes, along with comprehensive unit tests for the new Celestia blob client.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@randygrok randygrok changed the title feat: add client for celestia blob api feat: DA Client part 2, add client for celestia blob api Dec 3, 2025
@randygrok randygrok changed the title feat: DA Client part 2, add client for celestia blob api feat: DA Client remove interface part 2, add client for celestia blob api Dec 3, 2025
@randygrok randygrok mentioned this pull request Dec 3, 2025
6 tasks
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a client for the Celestia blob API, which is a significant feature. The changes include new packages for the Celestia client (da/celestia) and a wrapper for its usage within the block package (block/internal/da), along with corresponding tests and dependency updates. The overall structure is reasonable, but there are a few key areas for improvement. Most notably, there is significant code duplication of types and error definitions in the block/internal/da package, which should be refactored to use the existing definitions from core/da to improve maintainability. Additionally, there's a minor issue in a test mock related to data cloning that could be improved for test robustness.

Comment on lines +1 to +70
package da

import "time"

// StatusCode mirrors DA status codes used in Celestia blob client.
type StatusCode uint64

// Data Availability return codes.
const (
StatusUnknown StatusCode = iota
StatusSuccess
StatusNotFound
StatusNotIncludedInBlock
StatusAlreadyInMempool
StatusTooBig
StatusContextDeadline
StatusError
StatusIncorrectAccountSequence
StatusContextCanceled
StatusHeightFromFuture
)

// Blob is the data submitted/received from the DA layer.
type Blob = []byte

// ID should contain serialized data required by the implementation to find blob in DA.
type ID = []byte

// Commitment should contain serialized cryptographic commitment to Blob value.
type Commitment = []byte

// Proof should contain serialized proof of inclusion (publication) of Blob in DA.
type Proof = []byte

// GetIDsResult holds the result of GetIDs call: IDs and timestamp of corresponding block.
type GetIDsResult struct {
IDs []ID
Timestamp time.Time
}

// ResultSubmit contains information returned from DA layer after block headers/data submission.
type ResultSubmit struct {
BaseResult
}

// ResultRetrieve contains batch of block data returned from DA layer client.
type ResultRetrieve struct {
BaseResult
// Data is the block data retrieved from Data Availability Layer.
// If Code is not equal to StatusSuccess, it has to be nil.
Data [][]byte
}

// BaseResult contains basic information returned by DA layer.
type BaseResult struct {
// Code is to determine if the action succeeded.
Code StatusCode
// Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc)
Message string
// Height is the height of the block on Data Availability Layer for given result.
Height uint64
// SubmittedCount is the number of successfully submitted blocks.
SubmittedCount uint64
// BlobSize is the size of the blob submitted.
BlobSize uint64
// IDs is the list of IDs of the blobs submitted.
IDs [][]byte
// Timestamp is the timestamp of the posted data on Data Availability Layer.
Timestamp time.Time
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

This file duplicates type definitions like StatusCode, ResultSubmit, ResultRetrieve, and BaseResult which are already defined in core/da/da.go. To avoid code duplication and improve maintainability, this file should be removed and the types from the core/da package should be imported and used instead.

@codecov
Copy link

codecov bot commented Dec 4, 2025

Codecov Report

❌ Patch coverage is 22.86822% with 398 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.27%. Comparing base (09b7efd) to head (4c6222f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/da/jsonrpc/mocks/blob_module_mock.go 0.00% 322 Missing ⚠️
block/internal/da/blob_client.go 58.78% 61 Missing and 7 partials ⚠️
pkg/da/jsonrpc/client.go 72.41% 6 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2909      +/-   ##
==========================================
- Coverage   65.94%   63.27%   -2.68%     
==========================================
  Files          87       90       +3     
  Lines        7990     8506     +516     
==========================================
+ Hits         5269     5382     +113     
- Misses       2147     2542     +395     
- Partials      574      582       +8     
Flag Coverage Δ
combined 63.27% <22.86%> (-2.68%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@randygrok randygrok marked this pull request as ready for review December 4, 2025 09:54
Base automatically changed from bring-pkg-blob-from-blob-rpc to main December 4, 2025 11:57
"time"

"github.com/celestiaorg/go-square/v3/share"
blobrpc "github.com/evstack/ev-node/da/jsonrpc/blob"
Copy link
Contributor

Choose a reason for hiding this comment

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

these imports were the reason we had core, now we import everything into ev-node. can this be an interface defined in block instead of relying on da?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but pkg da is not a go.mod, what is the benefit that I cant really catch?

Copy link
Contributor

@tac0turtle tac0turtle left a comment

Choose a reason for hiding this comment

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

looks better, ci is still red and we should use mockery everywhere

@tac0turtle tac0turtle merged commit 2bcd6f7 into main Dec 11, 2025
26 of 28 checks passed
@tac0turtle tac0turtle deleted the add-da-blob-api-client branch December 11, 2025 08:49
alpe added a commit that referenced this pull request Dec 15, 2025
* main:
  chore: execute goimports to format the code (#2924)
  refactor(block)!: remove GetLastState from components (#2923)
  feat(syncing): add grace period for missing force txs inclusion (#2915)
  chore: minor improvement for docs (#2918)
  feat: DA Client remove interface part 2,  add client for celestia blob api   (#2909)
  chore: update rust deps (#2917)
  feat(sequencers/based): add based batch time (#2911)
  build(deps): Bump golangci/golangci-lint-action from 9.1.0 to 9.2.0 (#2914)
  refactor(sequencers): implement batch position persistance (#2908)
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.

4 participants