@@ -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-
416122func TestFillBlobPointers (t * testing.T ) {
417123 blob := eth.Blob {}
418124 rng := rand .New (rand .NewSource (1234 ))
0 commit comments