@@ -5,6 +5,7 @@ package melextraction
55import (
66 "context"
77 "errors"
8+ "fmt"
89 "io"
910 "math/big"
1011 "testing"
@@ -64,11 +65,11 @@ func TestExtractMessages(t *testing.T) {
6465 expectedError : "failed to lookup delayed messages" ,
6566 },
6667 {
67- name : "mismatched number of batch posting reports vs batches" ,
68+ name : "number of batch posting reports higher than batches" ,
6869 melStateParentHash : prevParentBlockHash ,
6970 lookupBatches : emptyLookupBatches , // 0 batches
7071 lookupDelayedMsgs : successfulLookupDelayedMsgs , // 1 batch posting report
71- expectedError : "batch posting reports 1 do not match the number of batches 0" ,
72+ expectedError : "number of batch posting reports 1 higher than the number of batches 0" ,
7273 },
7374 {
7475 name : "batch serialization fails" ,
@@ -94,7 +95,7 @@ func TestExtractMessages(t *testing.T) {
9495 lookupDelayedMsgs : successfulLookupDelayedMsgs ,
9596 serializer : emptySerializer , // Returns nil, hash will be different from parseReport
9697 parseReport : emptyParseReport , // Returns empty hash
97- expectedError : "batch data hash incorrect " ,
98+ expectedError : "not all batch posting reports processed. " ,
9899 },
99100 {
100101 name : "parse sequencer message fails" ,
@@ -131,6 +132,32 @@ func TestExtractMessages(t *testing.T) {
131132 expectedMessages : 2 ,
132133 expectedDelayedMsgs : 1 ,
133134 },
135+ {
136+ // 3 batches, 1 batch posting report whose hash matches only the 2nd batch.
137+ // Exercises the continue path where batch 0's hash doesn't match the report,
138+ // batch 1's hash does match, and batch 2 has no report to check.
139+ name : "OK - multiple batches with subset of reports" ,
140+ melStateParentHash : prevParentBlockHash ,
141+ lookupBatches : threeBatchLookup ,
142+ lookupDelayedMsgs : delayedMsgsWithOneReportForSecondBatch ,
143+ serializer : indexedSerializer (),
144+ parseReport : parseReportForSecondBatch ,
145+ parseSequencerMsg : successfulParseSequencerMsg ,
146+ extractBatchMessages : func (ctx context.Context , melState * mel.State , seqMsg * arbstate.SequencerMessage , delayedMsgDB DelayedMessageDatabase ) ([]* arbostypes.MessageWithMetadata , error ) {
147+ return []* arbostypes.MessageWithMetadata {
148+ {
149+ Message : & arbostypes.L1IncomingMessage {
150+ L2msg : []byte ("msg" ),
151+ Header : & arbostypes.L1IncomingMessageHeader {Kind : arbostypes .L1MessageType_L2Message },
152+ },
153+ },
154+ }, nil
155+ },
156+ expectedMsgCount : 3 , // 1 message per batch * 3 batches
157+ expectedDelayedSeen : 1 ,
158+ expectedMessages : 3 ,
159+ expectedDelayedMsgs : 1 ,
160+ },
134161 }
135162
136163 for _ , tt := range tests {
@@ -374,3 +401,60 @@ func failingExtractBatchMessages(
374401) ([]* arbostypes.MessageWithMetadata , error ) {
375402 return nil , errors .New ("failed to extract batch messages" )
376403}
404+
405+ // threeBatchLookup returns 3 batches and 3 transactions.
406+ func threeBatchLookup (
407+ ctx context.Context ,
408+ parentChainBlock * types.Header ,
409+ txFetcher TransactionFetcher ,
410+ logsFetcher LogsFetcher ,
411+ eventUnpacker EventUnpacker ,
412+ ) ([]* mel.SequencerInboxBatch , []* types.Transaction , error ) {
413+ batches := []* mel.SequencerInboxBatch {{}, {}, {}}
414+ txs := []* types.Transaction {{}, {}, {}}
415+ return batches , txs , nil
416+ }
417+
418+ // delayedMsgsWithOneReportForSecondBatch returns 1 delayed message that is a
419+ // batch posting report whose hash will match the 2nd batch (index 1) produced
420+ // by indexedSerializer.
421+ func delayedMsgsWithOneReportForSecondBatch (
422+ ctx context.Context ,
423+ melState * mel.State ,
424+ parentChainBlock * types.Header ,
425+ txFetcher TransactionFetcher ,
426+ logsFetcher LogsFetcher ,
427+ ) ([]* mel.DelayedInboxMessage , error ) {
428+ hash := common .MaxHash
429+ return []* mel.DelayedInboxMessage {
430+ {
431+ Message : & arbostypes.L1IncomingMessage {
432+ L2msg : []byte ("report-for-batch1" ),
433+ Header : & arbostypes.L1IncomingMessageHeader {
434+ Kind : arbostypes .L1MessageType_BatchPostingReport ,
435+ RequestId : & hash ,
436+ L1BaseFee : common .Big0 ,
437+ },
438+ },
439+ },
440+ }, nil
441+ }
442+
443+ // indexedSerializer returns a serializer that produces different bytes for each
444+ // successive call: "batch0", "batch1", "batch2", …
445+ func indexedSerializer () func (context.Context , * mel.SequencerInboxBatch , * types.Transaction , LogsFetcher ) ([]byte , error ) {
446+ callIndex := 0
447+ return func (ctx context.Context , batch * mel.SequencerInboxBatch , tx * types.Transaction , logsFetcher LogsFetcher ) ([]byte , error ) {
448+ data := []byte (fmt .Sprintf ("batch%d" , callIndex ))
449+ callIndex ++
450+ return data , nil
451+ }
452+ }
453+
454+ // parseReportForSecondBatch returns a hash that matches the serialized bytes
455+ // of the 2nd batch ("batch1") from indexedSerializer.
456+ func parseReportForSecondBatch (
457+ rd io.Reader ,
458+ ) (* big.Int , common.Address , common.Hash , uint64 , * big.Int , uint64 , error ) {
459+ return nil , common.Address {}, crypto .Keccak256Hash ([]byte ("batch1" )), 0 , nil , 0 , nil
460+ }
0 commit comments