@@ -3,6 +3,7 @@ package service
33import (
44 "compress/gzip"
55 "context"
6+ "crypto/sha256"
67 "encoding/json"
78 "fmt"
89 "io"
@@ -21,6 +22,7 @@ import (
2122 "github.com/ethereum-optimism/optimism/op-service/eth"
2223 "github.com/ethereum-optimism/optimism/op-service/testlog"
2324 "github.com/ethereum/go-ethereum/common"
25+ "github.com/ethereum/go-ethereum/crypto/kzg4844"
2426 "github.com/ethereum/go-ethereum/log"
2527 "github.com/stretchr/testify/require"
2628)
@@ -80,7 +82,7 @@ func TestAPIService(t *testing.T) {
8082 BeaconBlockHash : rootOne ,
8183 },
8284 BlobSidecars : storage.BlobSidecars {
83- Data : blobtest .NewBlobSidecars (t , 2 ),
85+ Data : blobtest .NewBlobSidecars (t , 2 , nil ),
8486 },
8587 }
8688
@@ -89,7 +91,7 @@ func TestAPIService(t *testing.T) {
8991 BeaconBlockHash : rootTwo ,
9092 },
9193 BlobSidecars : storage.BlobSidecars {
92- Data : blobtest .NewBlobSidecars (t , 2 ),
94+ Data : blobtest .NewBlobSidecars (t , 2 , nil ),
9395 },
9496 }
9597
@@ -99,7 +101,7 @@ func TestAPIService(t *testing.T) {
99101 BeaconBlockHash : rootThree ,
100102 },
101103 BlobSidecars : storage.BlobSidecars {
102- Data : blobtest .NewBlobSidecars (t , 8 ), // More than 6 blobs
104+ Data : blobtest .NewBlobSidecars (t , 8 , nil ), // More than 6 blobs
103105 },
104106 }
105107
@@ -364,3 +366,130 @@ func TestHealthHandler(t *testing.T) {
364366
365367 require .Equal (t , 200 , response .Code )
366368}
369+
370+ func TestBlobsHandlerJSON (t * testing.T ) {
371+ a , fs , _ , cleanup := setup (t )
372+ defer cleanup ()
373+
374+ // Pre-populate storage with blob sidecars
375+ testBlobs := blobtest .NewBlobSidecars (t , 3 , nil )
376+ data := storage.BlobData {
377+ Header : storage.Header {
378+ BeaconBlockHash : blobtest .Five ,
379+ },
380+ BlobSidecars : storage.BlobSidecars {
381+ Data : testBlobs ,
382+ },
383+ }
384+ err := fs .WriteBlob (context .Background (), data )
385+ require .NoError (t , err )
386+
387+ // Request blobs endpoint with JSON encoding
388+ request := httptest .NewRequest ("GET" , "/eth/v1/beacon/blobs/" + blobtest .Five .String (), nil )
389+ request .Header .Set ("Accept" , "application/json" )
390+ response := httptest .NewRecorder ()
391+
392+ a .router .ServeHTTP (response , request )
393+
394+ require .Equal (t , 200 , response .Code )
395+ require .Equal (t , "application/json" , response .Header ().Get ("Content-Type" ))
396+
397+ var blobs v1.Blobs
398+ err = json .Unmarshal (response .Body .Bytes (), & blobs )
399+ require .NoError (t , err )
400+ require .Equal (t , len (testBlobs ), len (blobs ))
401+
402+ // Verify blob data matches
403+ for i , blob := range blobs {
404+ require .Equal (t , testBlobs [i ].Blob , * blob )
405+ }
406+ }
407+
408+ func TestBlobsHandlerSSZ (t * testing.T ) {
409+ a , fs , _ , cleanup := setup (t )
410+ defer cleanup ()
411+
412+ // Pre-populate storage with blob sidecars
413+ testBlobs := blobtest .NewBlobSidecars (t , 2 , nil )
414+ data := storage.BlobData {
415+ Header : storage.Header {
416+ BeaconBlockHash : blobtest .One ,
417+ },
418+ BlobSidecars : storage.BlobSidecars {
419+ Data : testBlobs ,
420+ },
421+ }
422+ err := fs .WriteBlob (context .Background (), data )
423+ require .NoError (t , err )
424+
425+ // Request blobs endpoint with SSZ encoding
426+ request := httptest .NewRequest ("GET" , "/eth/v1/beacon/blobs/" + blobtest .One .String (), nil )
427+ request .Header .Set ("Accept" , "application/octet-stream" )
428+ response := httptest .NewRecorder ()
429+
430+ a .router .ServeHTTP (response , request )
431+
432+ require .Equal (t , 200 , response .Code )
433+ require .Equal (t , "application/octet-stream" , response .Header ().Get ("Content-Type" ))
434+
435+ // Unmarshal SSZ response
436+ blobs := v1.Blobs {}
437+ err = blobs .UnmarshalSSZ (response .Body .Bytes ())
438+ require .NoError (t , err )
439+ require .Equal (t , len (testBlobs ), len (blobs ))
440+ }
441+
442+ func TestBlobsHandlerWithVersionedHashes (t * testing.T ) {
443+ a , fs , _ , cleanup := setup (t )
444+ defer cleanup ()
445+
446+ // Pre-populate storage with blob sidecars
447+ testBlobs := blobtest .NewBlobSidecars (t , 5 , nil )
448+ data := storage.BlobData {
449+ Header : storage.Header {
450+ BeaconBlockHash : blobtest .Four ,
451+ },
452+ BlobSidecars : storage.BlobSidecars {
453+ Data : testBlobs ,
454+ },
455+ }
456+ err := fs .WriteBlob (context .Background (), data )
457+ require .NoError (t , err )
458+
459+ // Compute versioned hashes from commitments
460+ hasher := sha256 .New ()
461+ commitment0 := kzg4844 .Commitment (testBlobs [0 ].KZGCommitment )
462+ vh0 := kzg4844 .CalcBlobHashV1 (hasher , & commitment0 )
463+
464+ hasher .Reset ()
465+ commitment2 := kzg4844 .Commitment (testBlobs [2 ].KZGCommitment )
466+ vh2 := kzg4844 .CalcBlobHashV1 (hasher , & commitment2 )
467+
468+ // Request blobs endpoint with versioned_hashes filter
469+ request := httptest .NewRequest ("GET" , fmt .Sprintf ("/eth/v1/beacon/blobs/%s?versioned_hashes=%s&versioned_hashes=%s" , blobtest .Four .String (), common .Hash (vh0 ).Hex (), common .Hash (vh2 ).Hex ()), nil )
470+ request .Header .Set ("Accept" , "application/json" )
471+ response := httptest .NewRecorder ()
472+
473+ a .router .ServeHTTP (response , request )
474+
475+ require .Equal (t , 200 , response .Code )
476+
477+ var blobs v1.Blobs
478+ err = json .Unmarshal (response .Body .Bytes (), & blobs )
479+ require .NoError (t , err )
480+ require .Equal (t , 2 , len (blobs ))
481+ }
482+
483+ func TestBlobsHandlerNotFound (t * testing.T ) {
484+ a , _ , _ , cleanup := setup (t )
485+ defer cleanup ()
486+
487+ // Request blobs endpoint for non-existent block
488+ request := httptest .NewRequest ("GET" , "/eth/v1/beacon/blobs/" + blobtest .Seven .String (), nil )
489+ request .Header .Set ("Accept" , "application/json" )
490+ response := httptest .NewRecorder ()
491+
492+ a .router .ServeHTTP (response , request )
493+
494+ require .Equal (t , 404 , response .Code )
495+ }
0 commit comments