Skip to content
Merged
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
1 change: 1 addition & 0 deletions test/e2e/evm_full_node_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ func restartSequencerAndFullNode(t *testing.T, sut *SystemUnderTest, sequencerHo
"--evm.eth-url", endpoints.GetFullNodeEthURL(),
"--rollkit.da.address", endpoints.GetDAAddress(),
"--rollkit.da.block_time", DefaultDABlockTime,
"--rollkit.da.namespace", DefaultDANamespace,
)

// Give both nodes time to establish P2P connections
Expand Down
7 changes: 7 additions & 0 deletions test/e2e/evm_test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ const (
DefaultBlockTime = "150ms"
DefaultDABlockTime = "1s"
DefaultTestTimeout = 20 * time.Second
DefaultDANamespace = "evm-e2e"
DefaultChainID = "1234"
DefaultGasLimit = 22000

Expand Down Expand Up @@ -317,6 +318,7 @@ func setupSequencerNode(t *testing.T, sut *SystemUnderTest, sequencerHome, jwtSe
"--home", sequencerHome,
"--evnode.da.block_time", DefaultDABlockTime,
"--evnode.da.address", endpoints.GetDAAddress(),
"--evnode.da.namespace", DefaultDANamespace,
"--evnode.rpc.address", endpoints.GetRollkitRPCListen(),
"--evnode.p2p.listen_address", endpoints.GetRollkitP2PAddress(),
"--evm.engine-url", endpoints.GetSequencerEngineURL(),
Expand Down Expand Up @@ -360,6 +362,7 @@ func setupSequencerNodeLazy(t *testing.T, sut *SystemUnderTest, sequencerHome, j
"--home", sequencerHome,
"--evnode.da.block_time", DefaultDABlockTime,
"--evnode.da.address", endpoints.GetDAAddress(),
"--evnode.da.namespace", DefaultDANamespace,
"--evnode.rpc.address", endpoints.GetRollkitRPCListen(),
"--evnode.p2p.listen_address", endpoints.GetRollkitP2PAddress(),
"--evm.engine-url", endpoints.GetSequencerEngineURL(),
Expand Down Expand Up @@ -417,6 +420,7 @@ func setupFullNode(t *testing.T, sut *SystemUnderTest, fullNodeHome, sequencerHo
"--evm.eth-url", endpoints.GetFullNodeEthURL(),
"--rollkit.da.block_time", DefaultDABlockTime,
"--rollkit.da.address", endpoints.GetDAAddress(),
"--rollkit.da.namespace", DefaultDANamespace,
"--rollkit.rpc.address", endpoints.GetFullNodeRPCListen(),
"--rollkit.p2p.listen_address", endpoints.GetFullNodeP2PAddress(),
}
Expand Down Expand Up @@ -621,6 +625,7 @@ func restartDAAndSequencer(t *testing.T, sut *SystemUnderTest, sequencerHome, jw
"--home", sequencerHome,
"--evnode.da.address", endpoints.GetDAAddress(),
"--evnode.da.block_time", DefaultDABlockTime,
"--evnode.da.namespace", DefaultDANamespace,
"--evnode.rpc.address", endpoints.GetRollkitRPCListen(),
"--evnode.p2p.listen_address", endpoints.GetRollkitP2PAddress(),
"--evm.engine-url", endpoints.GetSequencerEngineURL(),
Expand Down Expand Up @@ -670,6 +675,7 @@ func restartDAAndSequencerLazy(t *testing.T, sut *SystemUnderTest, sequencerHome
"--home", sequencerHome,
"--evnode.da.address", endpoints.GetDAAddress(),
"--evnode.da.block_time", DefaultDABlockTime,
"--evnode.da.namespace", DefaultDANamespace,
"--evnode.rpc.address", endpoints.GetRollkitRPCListen(),
"--evnode.p2p.listen_address", endpoints.GetRollkitP2PAddress(),
"--evm.engine-url", endpoints.GetSequencerEngineURL(),
Expand Down Expand Up @@ -707,6 +713,7 @@ func restartSequencerNode(t *testing.T, sut *SystemUnderTest, sequencerHome, jwt
"--home", sequencerHome,
"--evnode.da.address", DAAddress,
"--evnode.da.block_time", DefaultDABlockTime,
"--evnode.da.namespace", DefaultDANamespace,
)

time.Sleep(SlowPollingInterval)
Expand Down
68 changes: 62 additions & 6 deletions test/testda/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,27 @@ const (
DefaultMaxBlobSize = 2 * 1024 * 1024
)

// Header contains DA layer header information for a given height.
// This mirrors the structure used by real DA clients like Celestia.
type Header struct {
Height uint64
Timestamp time.Time
}

// Time returns the block time from the header.
// This mirrors the jsonrpc.Header.Time() method.
func (h *Header) Time() time.Time {
return h.Timestamp
}

// DummyDA is a test implementation of the DA client interface.
// It supports blob storage, height simulation, and failure injection.
// It supports blob storage, height simulation, failure injection, and header retrieval.
type DummyDA struct {
mu sync.Mutex
height atomic.Uint64
maxBlobSz uint64
blobs map[uint64]map[string][][]byte // height -> namespace -> blobs
headers map[uint64]*Header // height -> header (with timestamp)
failSubmit atomic.Bool

tickerMu sync.Mutex
Expand Down Expand Up @@ -50,6 +64,7 @@ func New(opts ...Option) *DummyDA {
d := &DummyDA{
maxBlobSz: DefaultMaxBlobSize,
blobs: make(map[uint64]map[string][][]byte),
headers: make(map[uint64]*Header),
}
for _, opt := range opts {
opt(d)
Expand Down Expand Up @@ -81,13 +96,21 @@ func (d *DummyDA) Submit(_ context.Context, data [][]byte, _ float64, namespace
blobSz += uint64(len(b))
}

now := time.Now()
d.mu.Lock()
height := d.height.Add(1)
if d.blobs[height] == nil {
d.blobs[height] = make(map[string][][]byte)
}
nsKey := string(namespace)
d.blobs[height][nsKey] = append(d.blobs[height][nsKey], data...)
// Store header with timestamp for this height
if d.headers[height] == nil {
d.headers[height] = &Header{
Height: height,
Timestamp: now,
}
}
d.mu.Unlock()

return datypes.ResultSubmit{
Expand All @@ -96,7 +119,7 @@ func (d *DummyDA) Submit(_ context.Context, data [][]byte, _ float64, namespace
Height: height,
BlobSize: blobSz,
SubmittedCount: uint64(len(data)),
Timestamp: time.Now(),
Timestamp: now,
},
}
}
Expand All @@ -109,6 +132,11 @@ func (d *DummyDA) Retrieve(_ context.Context, height uint64, namespace []byte) d
if byHeight != nil {
blobs = byHeight[string(namespace)]
}
// Get timestamp from header if available, otherwise use current time
var timestamp time.Time
if header := d.headers[height]; header != nil {
timestamp = header.Timestamp
}
d.mu.Unlock()

if len(blobs) == 0 {
Expand All @@ -117,7 +145,7 @@ func (d *DummyDA) Retrieve(_ context.Context, height uint64, namespace []byte) d
Code: datypes.StatusNotFound,
Height: height,
Message: datypes.ErrBlobNotFound.Error(),
Timestamp: time.Now(),
Timestamp: timestamp,
},
}
}
Expand All @@ -128,7 +156,7 @@ func (d *DummyDA) Retrieve(_ context.Context, height uint64, namespace []byte) d
Code: datypes.StatusSuccess,
Height: height,
IDs: ids,
Timestamp: time.Now(),
Timestamp: timestamp,
},
Data: blobs,
}
Expand Down Expand Up @@ -202,7 +230,16 @@ func (d *DummyDA) StartHeightTicker(interval time.Duration) func() {
for {
select {
case <-ticker.C:
d.height.Add(1)
now := time.Now()
height := d.height.Add(1)
d.mu.Lock()
if d.headers[height] == nil {
d.headers[height] = &Header{
Height: height,
Timestamp: now,
}
}
d.mu.Unlock()
case <-stopCh:
return
}
Expand All @@ -219,11 +256,12 @@ func (d *DummyDA) StartHeightTicker(interval time.Duration) func() {
}
}

// Reset clears all stored blobs and resets the height.
// Reset clears all stored blobs, headers, and resets the height.
func (d *DummyDA) Reset() {
d.mu.Lock()
d.height.Store(0)
d.blobs = make(map[uint64]map[string][][]byte)
d.headers = make(map[uint64]*Header)
d.failSubmit.Store(false)
d.mu.Unlock()

Expand All @@ -234,3 +272,21 @@ func (d *DummyDA) Reset() {
}
d.tickerMu.Unlock()
}

// GetHeaderByHeight retrieves the header for the given DA height.
// This mirrors the HeaderAPI.GetByHeight method from the real DA client.
// Returns nil if no header exists for the given height.
func (d *DummyDA) GetHeaderByHeight(_ context.Context, height uint64) (*Header, error) {
d.mu.Lock()
header := d.headers[height]
d.mu.Unlock()

if header == nil {
currentHeight := d.height.Load()
if height > currentHeight {
return nil, datypes.ErrHeightFromFuture
}
return nil, datypes.ErrBlobNotFound
}
return header, nil
}
Loading