Skip to content

Commit eb0db5d

Browse files
piersyQuentinI
authored andcommitted
op-node: isolate Espresso derivation tests into espresso_-prefixed files
Moves the Espresso batch-auth tests out of the upstream calldata/blob data-source test files into new espresso_calldata_source_test.go and espresso_blob_data_source_test.go, and renames batch_authenticator_test.go to espresso_batch_authenticator_test.go. Pure test relocation: no production code and no test logic, names, comments, or assertions change. The goal is to keep all Espresso-specific tests in espresso_-prefixed files so upstream changes to the shared data-source test files cannot conflict with them on rebase. Moved into espresso_calldata_source_test.go (from calldata_source_test.go): the mockAuthEvents helper, TestDataFromEVMTransactionsEventAuth (including the "multiple authenticated txs each accepted for their own commitment" subtest), and TestDataFromEVMTransactionsForkBoundary. Moved into espresso_blob_data_source_test.go (from blob_data_source_test.go): TestDataAndHashesFromTxsEventAuth and TestDataAndHashesFromTxsForkBoundary. The upstream tests (TestDataFromEVMTransactions, TestDataAndHashesFromTxs, TestFillBlobPointers, TestBlobDataSourceL1FetcherErrors) stay in their original files. The only import adjustment is dropping io, common/hexutil, and op-service/txmgr from the new blob file (used only by the kept TestBlobDataSourceL1FetcherErrors); both original import blocks are unchanged.
1 parent f8a357b commit eb0db5d

5 files changed

Lines changed: 763 additions & 719 deletions

File tree

op-node/rollup/derive/blob_data_source_test.go

Lines changed: 0 additions & 294 deletions
Original file line numberDiff line numberDiff line change
@@ -119,300 +119,6 @@ func TestDataAndHashesFromTxs(t *testing.T) {
119119
require.Equal(t, 0, len(blobHashes))
120120
}
121121

122-
// TestDataAndHashesFromTxsEventAuth tests event-based batch authentication for both
123-
// calldata and blob transactions in the blob data source path.
124-
//
125-
// Event-based authentication is only active post-Espresso; the fixture
126-
// activates the fork at L1 origin time 0 (genesis) so all test refs satisfy
127-
// ref.Time >= *EspressoTime.
128-
func TestDataAndHashesFromTxsEventAuth(t *testing.T) {
129-
rng := rand.New(rand.NewSource(9999))
130-
privateKey := testutils.InsecureRandomKey(rng)
131-
altKey := testutils.InsecureRandomKey(rng)
132-
batcherAddr := crypto.PubkeyToAddress(*privateKey.Public().(*ecdsa.PublicKey))
133-
altAddr := crypto.PubkeyToAddress(*altKey.Public().(*ecdsa.PublicKey))
134-
batchInboxAddr := testutils.RandomAddress(rng)
135-
authenticatorAddr := testutils.RandomAddress(rng)
136-
logger := testlog.Logger(t, log.LvlInfo)
137-
138-
chainId := new(big.Int).SetUint64(rng.Uint64())
139-
signer := types.NewPragueSigner(chainId)
140-
espressoTime := uint64(0)
141-
config := DataSourceConfig{
142-
l1Signer: signer,
143-
batchInboxAddress: batchInboxAddr,
144-
rollupCfg: &rollup.Config{
145-
EspressoTime: &espressoTime,
146-
BatchAuthenticatorAddress: authenticatorAddr,
147-
},
148-
batchAuthCaches: NewBatchAuthCaches(),
149-
}
150-
151-
ctx := context.Background()
152-
153-
t.Run("authenticated calldata tx accepted", func(t *testing.T) {
154-
l1F := &testutils.MockL1Source{}
155-
txData := &types.LegacyTx{
156-
Nonce: rng.Uint64(),
157-
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
158-
Gas: 2_000_000,
159-
To: &batchInboxAddr,
160-
Value: big.NewInt(10),
161-
Data: testutils.RandomData(rng, 200),
162-
}
163-
calldataTx, _ := types.SignNewTx(privateKey, signer, txData)
164-
165-
ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)}
166-
batchHash := ComputeCalldataBatchHash(calldataTx.Data())
167-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash})
168-
169-
data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger)
170-
require.NoError(t, err)
171-
require.Equal(t, 1, len(data))
172-
require.Equal(t, 0, len(blobHashes))
173-
require.Equal(t, eth.Data(calldataTx.Data()), *data[0].calldata)
174-
l1F.AssertExpectations(t)
175-
})
176-
177-
t.Run("authenticated blob tx accepted", func(t *testing.T) {
178-
l1F := &testutils.MockL1Source{}
179-
blobHash := testutils.RandomHash(rng)
180-
blobTxData := &types.BlobTx{
181-
Nonce: rng.Uint64(),
182-
Gas: 2_000_000,
183-
To: batchInboxAddr,
184-
Data: testutils.RandomData(rng, 100),
185-
BlobHashes: []common.Hash{blobHash},
186-
}
187-
blobTx, _ := types.SignNewTx(privateKey, signer, blobTxData)
188-
189-
ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)}
190-
batchHash := ComputeBlobBatchHash([]common.Hash{blobHash})
191-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash})
192-
193-
data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{blobTx}, &config, batcherAddr, l1F, ref, logger)
194-
require.NoError(t, err)
195-
require.Equal(t, 1, len(data))
196-
require.Equal(t, 1, len(blobHashes))
197-
require.Equal(t, blobHash, blobHashes[0]) // the authenticated blob's hash, not just any
198-
require.Nil(t, data[0].calldata) // blob placeholder
199-
require.Nil(t, data[0].blob) // blob placeholder
200-
l1F.AssertExpectations(t)
201-
})
202-
203-
t.Run("unknown sender rejected without auth event", func(t *testing.T) {
204-
l1F := &testutils.MockL1Source{}
205-
txData := &types.LegacyTx{
206-
Nonce: rng.Uint64(),
207-
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
208-
Gas: 2_000_000,
209-
To: &batchInboxAddr,
210-
Value: big.NewInt(10),
211-
Data: testutils.RandomData(rng, 200),
212-
}
213-
// Signed by an unknown key (not batcherAddr), no auth event — should be rejected
214-
calldataTx, _ := types.SignNewTx(altKey, signer, txData)
215-
216-
ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)}
217-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, nil) // no auth events
218-
219-
data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger)
220-
require.NoError(t, err)
221-
require.Equal(t, 0, len(data), "unknown sender tx without auth event should be rejected")
222-
require.Equal(t, 0, len(blobHashes))
223-
l1F.AssertExpectations(t)
224-
})
225-
226-
t.Run("fallback batcher without auth event rejected", func(t *testing.T) {
227-
l1F := &testutils.MockL1Source{}
228-
txData := &types.LegacyTx{
229-
Nonce: rng.Uint64(),
230-
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
231-
Gas: 2_000_000,
232-
To: &batchInboxAddr,
233-
Value: big.NewInt(10),
234-
Data: testutils.RandomData(rng, 200),
235-
}
236-
// Signed by batcher key (SystemConfig batcherAddr), no auth event — should be rejected
237-
// because all batchers now require event-based authentication
238-
calldataTx, _ := types.SignNewTx(privateKey, signer, txData)
239-
240-
ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)}
241-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, nil) // no auth events
242-
243-
data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger)
244-
require.NoError(t, err)
245-
require.Equal(t, 0, len(data), "fallback batcher without auth event should be rejected")
246-
require.Equal(t, 0, len(blobHashes))
247-
l1F.AssertExpectations(t)
248-
})
249-
250-
t.Run("non-batcher sender accepted when it matches the auth caller", func(t *testing.T) {
251-
l1F := &testutils.MockL1Source{}
252-
txData := &types.LegacyTx{
253-
Nonce: rng.Uint64(),
254-
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
255-
Gas: 2_000_000,
256-
To: &batchInboxAddr,
257-
Value: big.NewInt(10),
258-
Data: testutils.RandomData(rng, 200),
259-
}
260-
// Signed by alt key (not the SystemConfig batcher), and the auth event was
261-
// emitted by that same alt address — should be accepted.
262-
calldataTx, _ := types.SignNewTx(altKey, signer, txData)
263-
264-
ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)}
265-
batchHash := ComputeCalldataBatchHash(calldataTx.Data())
266-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, altAddr, []common.Hash{batchHash})
267-
268-
data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger)
269-
require.NoError(t, err)
270-
require.Equal(t, 1, len(data))
271-
require.Equal(t, 0, len(blobHashes))
272-
require.Equal(t, eth.Data(calldataTx.Data()), *data[0].calldata) // the authenticated tx, not just any
273-
l1F.AssertExpectations(t)
274-
})
275-
276-
t.Run("authenticated tx rejected when sender differs from auth caller", func(t *testing.T) {
277-
l1F := &testutils.MockL1Source{}
278-
txData := &types.LegacyTx{
279-
Nonce: rng.Uint64(),
280-
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
281-
Gas: 2_000_000,
282-
To: &batchInboxAddr,
283-
Value: big.NewInt(10),
284-
Data: testutils.RandomData(rng, 200),
285-
}
286-
// Signed by alt key, but the commitment was authenticated by batcherAddr.
287-
// The submitter must match the auth caller — should be rejected.
288-
calldataTx, _ := types.SignNewTx(altKey, signer, txData)
289-
290-
ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)}
291-
batchHash := ComputeCalldataBatchHash(calldataTx.Data())
292-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash})
293-
294-
data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger)
295-
require.NoError(t, err)
296-
require.Equal(t, 0, len(data), "batch authenticated by a different address than the submitter must be rejected")
297-
require.Equal(t, 0, len(blobHashes))
298-
l1F.AssertExpectations(t)
299-
})
300-
}
301-
302-
// TestDataAndHashesFromTxsForkBoundary exercises the Espresso fork gate flipping in the
303-
// blob data source path (dataAndHashesFromTxs) across a single fixed DataSourceConfig.
304-
//
305-
// This is the path a chain with Ecotone active actually runs: OpenData always selects the
306-
// blob source, and calldata (type-2) batches flow through its non-blob branch. Pre-Espresso
307-
// (L1 origin time < EspressoTime) must use upstream sender-based authorization with no event
308-
// scanning; at and after activation it must switch to event-based authentication. The gate is
309-
// implemented separately here from the calldata source, so this mirrors
310-
// TestDataFromEVMTransactionsForkBoundary to pin both copies.
311-
func TestDataAndHashesFromTxsForkBoundary(t *testing.T) {
312-
rng := rand.New(rand.NewSource(7777))
313-
privateKey := testutils.InsecureRandomKey(rng)
314-
altKey := testutils.InsecureRandomKey(rng)
315-
batcherAddr := crypto.PubkeyToAddress(*privateKey.Public().(*ecdsa.PublicKey))
316-
batchInboxAddr := testutils.RandomAddress(rng)
317-
authenticatorAddr := testutils.RandomAddress(rng)
318-
logger := testlog.Logger(t, log.LvlInfo)
319-
320-
chainId := new(big.Int).SetUint64(rng.Uint64())
321-
signer := types.NewPragueSigner(chainId)
322-
323-
// Fork activates at L1 origin time 1000. A single config is reused across all
324-
// sub-tests; only ref.Time changes to cross the boundary.
325-
espressoTime := uint64(1000)
326-
config := DataSourceConfig{
327-
l1Signer: signer,
328-
batchInboxAddress: batchInboxAddr,
329-
rollupCfg: &rollup.Config{
330-
EspressoTime: &espressoTime,
331-
BatchAuthenticatorAddress: authenticatorAddr,
332-
},
333-
batchAuthCaches: NewBatchAuthCaches(),
334-
}
335-
336-
ctx := context.Background()
337-
338-
// newCalldataBatchTx builds a type-2 calldata batch tx to the inbox (the tx shape an
339-
// Ecotone-active, calldata-batching chain submits through the blob source).
340-
newCalldataBatchTx := func(t *testing.T, author *ecdsa.PrivateKey, data []byte) *types.Transaction {
341-
t.Helper()
342-
tx, err := types.SignNewTx(author, signer, &types.DynamicFeeTx{
343-
ChainID: chainId, Nonce: rng.Uint64(), Gas: 2_000_000,
344-
GasTipCap: big.NewInt(2 * params.GWei), GasFeeCap: big.NewInt(30 * params.GWei),
345-
To: &batchInboxAddr, Data: data,
346-
})
347-
require.NoError(t, err)
348-
return tx
349-
}
350-
351-
t.Run("pre-fork: batcher accepted via sender auth, no event scan", func(t *testing.T) {
352-
// The empty mock asserts pre-fork derivation performs zero L1 receipt scanning:
353-
// any FetchReceipts/L1BlockRefByHash call would be an unexpected call and panic.
354-
l1F := &testutils.MockL1Source{}
355-
txData := testutils.RandomData(rng, 200)
356-
tx := newCalldataBatchTx(t, privateKey, txData)
357-
358-
ref := eth.L1BlockRef{Number: 1, Time: espressoTime - 1, Hash: testutils.RandomHash(rng)}
359-
data, hashes, err := dataAndHashesFromTxs(ctx, types.Transactions{tx}, &config, batcherAddr, l1F, ref, logger)
360-
require.NoError(t, err)
361-
require.Equal(t, 1, len(data), "pre-fork batcher tx should be accepted via sender-based auth")
362-
require.Equal(t, 0, len(hashes))
363-
require.NotNil(t, data[0].calldata)
364-
require.Equal(t, eth.Data(txData), *data[0].calldata)
365-
l1F.AssertExpectations(t)
366-
})
367-
368-
t.Run("pre-fork: non-batcher sender rejected", func(t *testing.T) {
369-
l1F := &testutils.MockL1Source{}
370-
tx := newCalldataBatchTx(t, altKey, testutils.RandomData(rng, 200))
371-
372-
ref := eth.L1BlockRef{Number: 1, Time: espressoTime - 1, Hash: testutils.RandomHash(rng)}
373-
data, hashes, err := dataAndHashesFromTxs(ctx, types.Transactions{tx}, &config, batcherAddr, l1F, ref, logger)
374-
require.NoError(t, err)
375-
require.Equal(t, 0, len(data), "pre-fork tx from a non-batcher sender should be rejected")
376-
require.Equal(t, 0, len(hashes))
377-
l1F.AssertExpectations(t)
378-
})
379-
380-
t.Run("activation block: same batcher tx rejected without auth event", func(t *testing.T) {
381-
// At the exact activation time (ref.Time == EspressoTime) the event-based path is
382-
// active, so a sender-only batcher tx is no longer sufficient.
383-
l1F := &testutils.MockL1Source{}
384-
txData := testutils.RandomData(rng, 200)
385-
tx := newCalldataBatchTx(t, privateKey, txData)
386-
387-
ref := eth.L1BlockRef{Number: 1, Time: espressoTime, Hash: testutils.RandomHash(rng)}
388-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, nil)
389-
390-
data, hashes, err := dataAndHashesFromTxs(ctx, types.Transactions{tx}, &config, batcherAddr, l1F, ref, logger)
391-
require.NoError(t, err)
392-
require.Equal(t, 0, len(data), "post-fork batcher tx without an auth event must be rejected")
393-
require.Equal(t, 0, len(hashes))
394-
l1F.AssertExpectations(t)
395-
})
396-
397-
t.Run("activation block: same batcher tx accepted with auth event", func(t *testing.T) {
398-
l1F := &testutils.MockL1Source{}
399-
txData := testutils.RandomData(rng, 200)
400-
tx := newCalldataBatchTx(t, privateKey, txData)
401-
402-
ref := eth.L1BlockRef{Number: 1, Time: espressoTime, Hash: testutils.RandomHash(rng)}
403-
batchHash := ComputeCalldataBatchHash(tx.Data())
404-
ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash})
405-
406-
data, hashes, err := dataAndHashesFromTxs(ctx, types.Transactions{tx}, &config, batcherAddr, l1F, ref, logger)
407-
require.NoError(t, err)
408-
require.Equal(t, 1, len(data), "post-fork batcher tx with a matching auth event must be accepted")
409-
require.Equal(t, 0, len(hashes))
410-
require.NotNil(t, data[0].calldata)
411-
require.Equal(t, eth.Data(txData), *data[0].calldata)
412-
l1F.AssertExpectations(t)
413-
})
414-
}
415-
416122
func TestFillBlobPointers(t *testing.T) {
417123
blob := eth.Blob{}
418124
rng := rand.New(rand.NewSource(1234))

0 commit comments

Comments
 (0)