@@ -13,6 +13,8 @@ import (
13
13
gethRPC "github.com/ethereum/go-ethereum/rpc"
14
14
"github.com/holiman/uint256"
15
15
"github.com/pkg/errors"
16
+ "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/kzg"
17
+ "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas"
16
18
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/types"
17
19
"github.com/prysmaticlabs/prysm/v5/beacon-chain/verification"
18
20
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
@@ -44,11 +46,15 @@ var (
44
46
GetPayloadMethodV3 ,
45
47
GetPayloadBodiesByHashV1 ,
46
48
GetPayloadBodiesByRangeV1 ,
49
+ GetBlobsV1 ,
47
50
}
48
51
electraEngineEndpoints = []string {
49
52
NewPayloadMethodV4 ,
50
53
GetPayloadMethodV4 ,
51
54
}
55
+ fuluEngineEndpoints = []string {
56
+ GetBlobsV2 ,
57
+ }
52
58
)
53
59
54
60
const (
@@ -85,6 +91,8 @@ const (
85
91
ExchangeCapabilities = "engine_exchangeCapabilities"
86
92
// GetBlobsV1 request string for JSON-RPC.
87
93
GetBlobsV1 = "engine_getBlobsV1"
94
+ // GetBlobsV2 request string for JSON-RPC.
95
+ GetBlobsV2 = "engine_getBlobsV2"
88
96
// Defines the seconds before timing out engine endpoints with non-block execution semantics.
89
97
defaultEngineTimeout = time .Second
90
98
)
@@ -108,6 +116,7 @@ type Reconstructor interface {
108
116
ctx context.Context , blindedBlocks []interfaces.ReadOnlySignedBeaconBlock ,
109
117
) ([]interfaces.SignedBeaconBlock , error )
110
118
ReconstructBlobSidecars (ctx context.Context , block interfaces.ReadOnlySignedBeaconBlock , blockRoot [32 ]byte , hi func (uint64 ) bool ) ([]blocks.VerifiedROBlob , error )
119
+ ReconstructDataColumnSidecars (ctx context.Context , block interfaces.ReadOnlySignedBeaconBlock , blockRoot [32 ]byte , hi func (uint64 ) bool ) ([]blocks.VerifiedRODataColumn , error )
111
120
}
112
121
113
122
// EngineCaller defines a client that can interact with an Ethereum
@@ -302,6 +311,9 @@ func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
302
311
if params .ElectraEnabled () {
303
312
supportedEngineEndpoints = append (supportedEngineEndpoints , electraEngineEndpoints ... )
304
313
}
314
+ if params .FuluEnabled () {
315
+ supportedEngineEndpoints = append (supportedEngineEndpoints , fuluEngineEndpoints ... )
316
+ }
305
317
var result []string
306
318
err := s .rpcClient .CallContext (ctx , & result , ExchangeCapabilities , supportedEngineEndpoints )
307
319
if err != nil {
@@ -492,19 +504,38 @@ func (s *Service) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
492
504
}
493
505
494
506
// GetBlobs returns the blob and proof from the execution engine for the given versioned hashes.
495
- func (s * Service ) GetBlobs (ctx context.Context , versionedHashes []common.Hash ) ([] * pb. BlobAndProof , error ) {
507
+ func (s * Service ) GetBlobs (ctx context.Context , slot primitives. Slot , versionedHashes []common.Hash ) (any , error ) {
496
508
ctx , span := trace .StartSpan (ctx , "powchain.engine-api-client.GetBlobs" )
497
509
defer span .End ()
498
- // If the execution engine does not support `GetBlobsV1`, return early to prevent encountering an error later.
499
- if ! s .capabilityCache .has (GetBlobsV1 ) {
510
+
511
+ method , proto := s .getPayloadMethodAndMessage (slot )
512
+ // If the execution engine does not support the method, return early to prevent encountering an error later.
513
+ if ! s .capabilityCache .has (method ) {
500
514
return nil , nil
501
515
}
502
516
503
- result := make ([]* pb.BlobAndProof , len (versionedHashes ))
504
- err := s .rpcClient .CallContext (ctx , & result , GetBlobsV1 , versionedHashes )
517
+ var result any
518
+ switch proto .(type ) {
519
+ case * pb.BlobAndProofV2 :
520
+ result = make ([]* pb.BlobAndProofV2 , len (versionedHashes ))
521
+ case * pb.BlobAndProof :
522
+ result = make ([]* pb.BlobAndProof , len (versionedHashes ))
523
+ default :
524
+ return nil , errors .New ("unsupported blob proof type" )
525
+ }
526
+
527
+ err := s .rpcClient .CallContext (ctx , & result , method , versionedHashes )
505
528
return result , handleRPCError (err )
506
529
}
507
530
531
+ func (s * Service ) getPayloadMethodAndMessage (slot primitives.Slot ) (string , proto.Message ) {
532
+ pe := slots .ToEpoch (slot )
533
+ if pe >= params .BeaconConfig ().FuluForkEpoch {
534
+ return GetBlobsV2 , & pb.BlobAndProofV2 {}
535
+ }
536
+ return GetBlobsV1 , & pb.BlobAndProof {}
537
+ }
538
+
508
539
// ReconstructFullBlock takes in a blinded beacon block and reconstructs
509
540
// a beacon block with a full execution payload via the engine API.
510
541
func (s * Service ) ReconstructFullBlock (
@@ -561,10 +592,14 @@ func (s *Service) ReconstructBlobSidecars(ctx context.Context, block interfaces.
561
592
}
562
593
563
594
// Fetch blobs from EL
564
- blobs , err := s .GetBlobs (ctx , kzgHashes )
595
+ result , err := s .GetBlobs (ctx , block . Block (). Slot () , kzgHashes )
565
596
if err != nil {
566
597
return nil , errors .Wrap (err , "could not get blobs" )
567
598
}
599
+ blobs , ok := result .([]* pb.BlobAndProof )
600
+ if ! ok {
601
+ return nil , errors .New ("invalid blob proof type" )
602
+ }
568
603
if len (blobs ) == 0 {
569
604
return nil , nil
570
605
}
@@ -615,6 +650,88 @@ func (s *Service) ReconstructBlobSidecars(ctx context.Context, block interfaces.
615
650
return verifiedBlobs , nil
616
651
}
617
652
653
+ // ReconstructDataColumnSidecars reconstructs the verified data column sidecars for a given beacon block.
654
+ // It retrieves the KZG commitments from the block body, fetches the associated blobs and cell proofs,
655
+ // and constructs the corresponding verified read-only data column sidecars.
656
+ func (s * Service ) ReconstructDataColumnSidecars (ctx context.Context , block interfaces.ReadOnlySignedBeaconBlock , blockRoot [32 ]byte ) ([]blocks.VerifiedRODataColumn , error ) {
657
+ blockBody := block .Block ().Body ()
658
+ kzgCommitments , err := blockBody .BlobKzgCommitments ()
659
+ if err != nil {
660
+ return nil , errors .Wrap (err , "could not get blob KZG commitments" )
661
+ }
662
+
663
+ // Collect KZG hashes for all blobs
664
+ var kzgHashes []common.Hash
665
+ var kzgIndexes []int
666
+ for i , commitment := range kzgCommitments {
667
+ kzgHashes = append (kzgHashes , primitives .ConvertKzgCommitmentToVersionedHash (commitment ))
668
+ kzgIndexes = append (kzgIndexes , i )
669
+ }
670
+
671
+ // Fetch all blobs from EL
672
+ result , err := s .GetBlobs (ctx , block .Block ().Slot (), kzgHashes )
673
+ if err != nil {
674
+ return nil , errors .Wrap (err , "could not get blobs" )
675
+ }
676
+
677
+ blobAndProofs , ok := result .([]* pb.BlobAndProofV2 )
678
+ if ! ok {
679
+ return nil , errors .New ("invalid blob and proof type" )
680
+ }
681
+ for _ , blobAndCellProofs := range blobAndProofs {
682
+ if blobAndCellProofs == nil {
683
+ return nil , errors .Wrap (err , "unable to reconstruct data column sidecars, not enough blob data from EL" )
684
+ }
685
+ }
686
+
687
+ var cellsAndProofs []kzg.CellsAndProofs
688
+ for _ , blobAndCellProofs := range blobAndProofs {
689
+ blob := kzg .Blob (blobAndCellProofs .Blob )
690
+ cells , err := kzg .ComputeCells (& blob )
691
+ if err != nil {
692
+ return nil , errors .Wrap (err , "could not compute cells" )
693
+ }
694
+
695
+ proofs := make ([]kzg.Proof , len (blobAndCellProofs .CellProofs ))
696
+ for i , proof := range blobAndCellProofs .CellProofs {
697
+ proofs [i ] = kzg .Proof (proof )
698
+ }
699
+ cellsAndProofs = append (cellsAndProofs , kzg.CellsAndProofs {
700
+ Cells : cells ,
701
+ Proofs : proofs ,
702
+ })
703
+ }
704
+
705
+ header , err := block .Header ()
706
+ if err != nil {
707
+ return nil , errors .Wrap (err , "could not get header" )
708
+ }
709
+
710
+ kzgCommitmentsInclusionProof , err := blocks .MerkleProofKZGCommitments (blockBody )
711
+ if err != nil {
712
+ return nil , errors .Wrap (err , "could not get Merkle proof for KZG commitments" )
713
+ }
714
+
715
+ dataColumnSidecars , err := peerdas .DataColumnSidecarsForReconstruct (kzgCommitments , header , kzgCommitmentsInclusionProof , cellsAndProofs )
716
+ if err != nil {
717
+ return nil , errors .Wrap (err , "could not reconstruct data column sidecars" )
718
+ }
719
+
720
+ verifiedRODataColumns := make ([]blocks.VerifiedRODataColumn , len (dataColumnSidecars ))
721
+ for i , dataColumnSidecar := range dataColumnSidecars {
722
+ roDataColumn , err := blocks .NewRODataColumnWithRoot (dataColumnSidecar , blockRoot )
723
+ if err != nil {
724
+ return nil , errors .Wrap (err , "new read-only data column with root" )
725
+ }
726
+
727
+ verifiedRODataColumns [i ] = blocks .NewVerifiedRODataColumn (roDataColumn )
728
+ }
729
+
730
+ log .WithField ("root" , fmt .Sprintf ("%x" , blockRoot )).Debug ("Data columns reconstructed successfully" )
731
+
732
+ return verifiedRODataColumns , nil
733
+ }
734
+
618
735
func fullPayloadFromPayloadBody (
619
736
header interfaces.ExecutionData , body * pb.ExecutionPayloadBody , bVersion int ,
620
737
) (interfaces.ExecutionData , error ) {
0 commit comments