Skip to content

Conversation

@shyam-patel-kira
Copy link
Contributor

@shyam-patel-kira shyam-patel-kira commented Dec 17, 2025

Add E2E Tests and Disperser Mode Support for EigenDA V2

  • Implements dual-mode testing for EigenDA V2 integration

@github-actions
Copy link

github-actions bot commented Dec 17, 2025

❌ 9 Tests Failed:

Tests completed Failed Passed Skipped
2213 9 2204 0
View the top 3 failed tests by shortest run time
TestBatchPosterDelayBufferEnabled
Stack Traces | -0.000s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
INFO [01-09|10:54:10.389] New Key                                  name=Sequencer         Address=0xb386a74Dcab67b66F8AC07B4f08365d37495Dd23
INFO [01-09|10:54:10.389] New Key                                  name=Validator         Address=0x83FFCFaCE2Fb0E1286686815503608A16EF41e47
INFO [01-09|10:54:10.389] New Key                                  name=User              Address=0x7E23C8862920797d81916d62c274dd9217113e28
INFO [01-09|10:54:10.389] Allocated trie memory caches             clean=154.00MiB dirty=256.00MiB
INFO [01-09|10:54:10.390] State schema set to default              scheme=path
INFO [01-09|10:54:10.389] New Key                                  name=Sequencer         Address=0xb386a74Dcab67b66F8AC07B4f08365d37495Dd23
INFO [01-09|10:54:10.390] New Key                                  name=Validator         Address=0x83FFCFaCE2Fb0E1286686815503608A16EF41e47
INFO [01-09|10:54:10.390] New Key                                  name=User              Address=0x7E23C8862920797d81916d62c274dd9217113e28
INFO [01-09|10:54:10.391] Allocated trie memory caches             clean=154.00MiB dirty=256.00MiB
INFO [01-09|10:54:10.391] State schema set to default              scheme=path
INFO [01-09|10:54:10.391] Initialising Ethereum protocol           network=412,346 dbversion=<nil>
INFO [01-09|10:54:10.391] Load database journal from disk
WARN [01-09|10:54:10.390] Head block is not reachable
DEBUG[01-09|10:54:10.391] Current full block not old enough to freeze err="freezing threshold is not available"
WARN [01-09|10:54:10.391] Head block is not reachable
DEBUG[01-09|10:54:10.391] Current full block not old enough to freeze err="freezing threshold is not available"
INFO [01-09|10:54:10.392] State snapshot generator is not found
INFO [01-09|10:54:10.392] Starting snapshot generation             root=56e81f..63b421 accounts=0  slots=0     storage=0.00B   dangling=0 elapsed="4.508µs"
INFO [01-09|10:54:10.392] Initialized path database                triecache=154.00MiB statecache=102.00MiB buffer=256.00MiB state-history="last 90000 blocks"
INFO [01-09|10:54:10.392] Writing custom genesis block
TestBlockValidatorSimpleOnchainWithPublishedMachine
Stack Traces | -0.000s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=10925 duration=691ns
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=11150 duration=981ns
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=632   duration="1.372µs"
DEBUG[01-09|10:55:47.037] Served eth_getTransactionCount           reqid=10926 duration="28.654µs"
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=5758  duration="1.183µs"
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=10926 duration=591ns
DEBUG[01-09|10:55:47.037] Served eth_call                          reqid=11149 duration="353.128µs"
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=6     duration="1.412µs"
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=5155  duration="21.38µs"
TRACE[01-09|10:55:47.037] Handled RPC response                     reqid=5154  duration="1.232µs"
TRACE[01-09|10:55:47.038] Handled RPC response                     reqid=7     duration="2.384µs"
DEBUG[01-09|10:55:47.038] Executing EVM call finished              runtime="202.036µs"
DEBUG[01-09|10:55:47.038] Served eth_call                          reqid=11151 duration="243.854µs"
TRACE[01-09|10:55:47.038] Handled RPC response                     reqid=11149 duration=882ns
DEBUG[01-09|10:55:47.038] Executing EVM call finished              runtime="281.445µs"
DEBUG[01-09|10:55:47.038] Served eth_call                          reqid=5759  duration="322.02µs"
DEBUG[01-09|10:55:47.038] Executing EVM call finished              runtime="156.722µs"
DEBUG[01-09|10:55:47.038] Served eth_call                          reqid=5156  duration="196.165µs"
TRACE[01-09|10:55:47.038] Handled RPC response                     reqid=5759  duration="1.032µs"
TRACE[01-09|10:55:47.038] Handled RPC response                     reqid=11151 duration="1.122µs"
TestMockChallengeManagerAsserterCorrect
Stack Traces | 4.070s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
    full_challenge_impl_test.go:210: goroutine 13 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.5/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x434fd50, 0xc000483340}, {0x430baa0, 0xc00316c960}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/system_tests.Require(0xc000483340, {0x430baa0, 0xc00316c960}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:1759 +0x5d
        github.com/offchainlabs/nitro/system_tests.makeBatchEigenDA(0xc000483340, 0xc006bceb40, 0xc003d37900, 0xc0039b2310, 0xc000bb7b40, 0xc0045c7a28, {0x4b, 0xf7, 0x52, 0x57, ...}, ...)
        	/home/runner/work/nitro/nitro/system_tests/full_challenge_impl_test.go:210 +0x408
        github.com/offchainlabs/nitro/system_tests.RunChallengeTest(0xc000483340, 0x1, 0x1, 0x1, 0x1, {0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/full_challenge_impl_test.go:428 +0xd25
        github.com/offchainlabs/nitro/system_tests.TestMockChallengeManagerAsserterCorrect(0xc000483340)
        	/home/runner/work/nitro/nitro/system_tests/full_challenge_mock_test.go:20 +0x54
        testing.tRunner(0xc000483340, 0x3f8d900)
        	/opt/hostedtoolcache/go/1.25.5/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.5/x64/src/testing/testing.go:1997 +0x465
        
    full_challenge_impl_test.go:210: �[31;1m [] EigenDA V2 detected but accessed through deprecated V1 interface - use DAProvider.Enable for V2 �[0;0m
--- FAIL: TestMockChallengeManagerAsserterCorrect (4.07s)

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Comment on lines 17 to 25
# V2 uses latest image (v2.5.0+) with V2 backend support
# V2 implements OP Alt-DA spec with Optimism routes
PROXY_IMAGE="ghcr.io/layr-labs/eigenda-proxy:latest"
CONTAINER_NAME="eigenda-proxy-v2-nitro-test-instance"
STORAGE_BACKENDS="V2"
DISPERSAL_BACKEND="V2"
# Enable admin API for runtime backend switching
ENABLE_ADMIN_API="true"
;;
Copy link
Collaborator

Choose a reason for hiding this comment

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

what exactly are you testing? I'd imagine we'd need to configure these server specific env vars as well as pass in arb as a supported option via the APIS_ENABLED user flag

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is correct, the latest changes I pushed has updated this script

…port

This commit fixes ReferenceDA certificate validation failures by:

1. **Contract Support for ALT DA Certificates (0x01)**:
   - Added DAC_CERTIFICATE_MESSAGE_HEADER_FLAG (0x01) to SequencerInbox.sol
   - Updated isValidCallDataFlag() to accept 0x01 header byte
   - Added flag to ISequencerInbox.sol interface
   - Added DACertificateMessageHeaderFlag to KnownHeaderBits in daprovider/util.go

2. **ReferenceDAProofValidator Contract Deployment**:
   - Modified setupReferenceDAServer() to deploy ReferenceDAProofValidator contract with trusted signer
   - Updated setupReferenceDAServerForFallback() with same deployment logic
   - Added l1info parameter to both functions to get transaction options for deployment
   - Added import for solgen/go/localgen to access contract deployment bindings

3. **Test Improvements**:
   - Both TestReferenceDAIntegration and TestEigenDAV2WithReferenceDAFallback now pass
   - Certificate signer is properly registered as a trusted signer in deployed validator contract
   - Contract address is used for on-chain validation during certificate verification

Previous errors resolved:
- ❌ "certificate validation failed: no contract code at given address" → ✅ Fixed
- ❌ "InvalidHeaderFlag(0x01)" from SequencerInbox contract → ✅ Fixed

Tests passing locally:
- ✅ TestReferenceDAIntegration (22.12s)
- ✅ TestEigenDAV2WithReferenceDAFallback (22.07s)
@shyam-patel-kira shyam-patel-kira marked this pull request as ready for review January 5, 2026 16:30
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is the submodule changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it was updated to add DAC_CERTIFICATE_MESSAGE_HEADER_FLAG (0x01) support in SequencerInbox.sol. This allows the contract to accept ReferenceDA certificates which use the ALT-DA spec. Without this, ReferenceDA batches are rejected as invalid.

Copy link
Collaborator

Choose a reason for hiding this comment

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

still a bit fuzzy why contract submodules need to be updated here at all given the upstream repo has golang tests for their CustomDA feature which asserts to correctness of batch posting / derivation logics

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, upstream has CustomDA support and the golang daprovider/ code already supports 0x01 (we inherit that). However, the L1 contracts must also accept 0x01 headers for batch posting to work. Without this update, batch posting would fail with InvalidHeaderFlag(0x01).

The update adds one line to SequencerInbox.sol to validate the 0x01 header byte - same as upstream's CustomDA support, just using our fork's naming (DAC_CERTIFICATE_MESSAGE_HEADER_FLAG vs upstream's CUSTOM_DA_MESSAGE_HEADER_FLAG).

Comment on lines 30 to 33
# Version parameter: v1 or v2 (default: v1)
VERSION="${1:-v1}"
# Mode parameter: memstore or disperser (default: memstore)
MODE="${2:-memstore}"
Copy link
Collaborator

Choose a reason for hiding this comment

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

why are we supporting version switching? I thought the idea was to nuke all V1 codepaths entirely

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yea, I kept it in case we wanted to do an incremental rollout, can nuke it completely, don't have a strong opinion on this one

Copy link
Collaborator

@ethenotethan ethenotethan Jan 7, 2026

Choose a reason for hiding this comment

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

vs overloading the existing start-eigenda-proxy.sh why not add a separate one start-eigenda-proxy-customda.sh to easier support followup deletion of V1 related logics?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that's a good idea, create a different setup script for v2 testing: a37f34d

Comment on lines 31 to 43
if len(cert) == 0 {
return nil, fmt.Errorf("received empty certificate from proxy")
}

// Check version byte to determine certificate format
version := cert[0]

// V2 certificate (version 0x02): Not supported through V1 code path
// V2 uses ALT-DA spec and should be accessed through DAProvider interface, not EigenDA.Enable
// Returning ErrServiceUnavailable will trigger failover to DAProvider if configured
if version == 0x02 {
return nil, standard_client.ErrServiceUnavailable
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this check being done?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This detects V2 certificates in the V1 code path and returns ErrServiceUnavailable to trigger failover to ReferenceDA. However, if we're removing V1 entirely, this check may no longer be needed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. this implementation deviates from the initial spec proposal where we'd have a separate branch that doesn't contain any V1 code changes or we'd nuke v1 code paths. If we're doing the approach of cannibalizing the existing eigenda branch then we'll need a followup PR that nukes all V1 references.
  2. V2 uses CustomDA which is provided for us by OCL. These EigenDA specific write/read system paths would never be touched for EigenDA V2 x CustomDA and leverage the existing CustomDA writer/reader provided to us in the software.

proxyV2E2EURL = "http://127.0.0.1:4242"
)

// TestEigenDAV2E2EConnectivity verifies the proxy is properly configured for real disperser
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need this test right now? this PR feels like its scope creep limits. I'd prefer if we more incremental with this rollout where we first introduce just memstore vs having context switching for different EigenDA backends

// Setup second node for sync testing
l1NodeConfigB := arbnode.ConfigDefaultL1NonSequencerTest()
l1NodeConfigB.BlockValidator.Enable = false
l1NodeConfigB.EigenDA.Enable = true
Copy link
Collaborator

Choose a reason for hiding this comment

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

why are we touching this configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're configuring both because this PR tests EigenDA V2 (primary) with ReferenceDA as fallback. The EigenDA.Enable config points to the V2 proxy using ALT-DA spec, while DAProvider provides the fallback mechanism. However if we want to do an incremental rollout I can remove it, not sure if this scope is too large.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The EigenDA.Enable config points to the V2 proxy using ALT-DA spec

right V2 proxy uses the ALT-DA spec which assumes the CustomDA server feature of proxy which lives independent of the REST API that this EigenDA config expects communication with. All nitro client related communication paths for CustomDA write/read are supported in the software:
https://github.com/Layr-Labs/nitro/blob/eigenda/daprovider/daclient/daclient.go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 31 to 43
if len(cert) == 0 {
return nil, fmt.Errorf("received empty certificate from proxy")
}

// Check version byte to determine certificate format
version := cert[0]

// V2 certificate (version 0x02): Not supported through V1 code path
// V2 uses ALT-DA spec and should be accessed through DAProvider interface, not EigenDA.Enable
// Returning ErrServiceUnavailable will trigger failover to DAProvider if configured
if version == 0x02 {
return nil, standard_client.ErrServiceUnavailable
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. this implementation deviates from the initial spec proposal where we'd have a separate branch that doesn't contain any V1 code changes or we'd nuke v1 code paths. If we're doing the approach of cannibalizing the existing eigenda branch then we'll need a followup PR that nukes all V1 references.
  2. V2 uses CustomDA which is provided for us by OCL. These EigenDA specific write/read system paths would never be touched for EigenDA V2 x CustomDA and leverage the existing CustomDA writer/reader provided to us in the software.

Copy link
Collaborator

Choose a reason for hiding this comment

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

still a bit fuzzy why contract submodules need to be updated here at all given the upstream repo has golang tests for their CustomDA feature which asserts to correctness of batch posting / derivation logics

Comment on lines 198 to 219
// Configure server with generous limits for testing
providerServerConfig := dapserver.ServerConfig{
Addr: "localhost",
Port: 0, // Auto-assign port
JWTSecret: "",
EnableDAWriter: true,
ServerTimeouts: genericconf.HTTPServerTimeoutConfig{},
RPCServerBodyLimit: 256 * 1024 * 1024, // 256MB for testing
}

// Create the DA provider server
server, err := dapserver.NewServerWithDAPProvider(
ctx,
&providerServerConfig,
reader,
writer,
validator,
headerBytes,
data_streaming.PayloadCommitmentVerifier(),
)
Require(t, err)

Copy link
Collaborator

Choose a reason for hiding this comment

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

what server are you spinning up here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it doesn't exist anymore but this was for referenceDA server we're not touching it anymore

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.

3 participants