2023-05-18 16:13:18 +00:00
package sync
import (
"context"
"fmt"
2024-03-12 17:51:08 +00:00
"os"
"path"
2023-05-18 16:13:18 +00:00
"strings"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
2024-02-15 05:46:47 +00:00
"github.com/prysmaticlabs/prysm/v5/beacon-chain/verification"
2024-03-12 17:51:08 +00:00
"github.com/prysmaticlabs/prysm/v5/config/features"
2024-02-15 05:46:47 +00:00
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/rand"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
2024-03-12 17:51:08 +00:00
"github.com/prysmaticlabs/prysm/v5/io/file"
2024-02-15 05:46:47 +00:00
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
prysmTime "github.com/prysmaticlabs/prysm/v5/time"
"github.com/prysmaticlabs/prysm/v5/time/slots"
2023-05-18 16:13:18 +00:00
"github.com/sirupsen/logrus"
)
func ( s * Service ) validateBlob ( ctx context . Context , pid peer . ID , msg * pubsub . Message ) ( pubsub . ValidationResult , error ) {
2023-09-11 20:46:06 +00:00
receivedTime := prysmTime . Now ( )
2023-05-18 16:13:18 +00:00
if pid == s . cfg . p2p . PeerID ( ) {
return pubsub . ValidationAccept , nil
}
if s . cfg . initialSync . Syncing ( ) {
return pubsub . ValidationIgnore , nil
}
if msg . Topic == nil {
return pubsub . ValidationReject , errInvalidTopic
}
m , err := s . decodePubsubMessage ( msg )
if err != nil {
log . WithError ( err ) . Error ( "Failed to decode message" )
return pubsub . ValidationReject , err
}
2023-11-21 18:44:38 +00:00
bpb , ok := m . ( * eth . BlobSidecar )
2023-05-18 16:13:18 +00:00
if ! ok {
2023-11-21 18:44:38 +00:00
log . WithField ( "message" , m ) . Error ( "Message is not of type *eth.BlobSidecar" )
2023-05-18 16:13:18 +00:00
return pubsub . ValidationReject , errWrongMessage
}
2023-11-21 18:44:38 +00:00
blob , err := blocks . NewROBlob ( bpb )
if err != nil {
return pubsub . ValidationReject , errors . Wrap ( err , "roblob conversion failure" )
}
2024-01-06 23:47:09 +00:00
vf := s . newBlobVerifier ( blob , verification . GossipSidecarRequirements )
2023-05-18 16:13:18 +00:00
2023-12-11 00:37:45 +00:00
if err := vf . BlobIndexInBounds ( ) ; err != nil {
return pubsub . ValidationReject , err
2023-10-25 01:57:54 +00:00
}
2023-10-23 23:08:25 +00:00
// [REJECT] The sidecar is for the correct subnet -- i.e. compute_subnet_for_blob_sidecar(sidecar.index) == subnet_id.
want := fmt . Sprintf ( "blob_sidecar_%d" , computeSubnetForBlobSidecar ( blob . Index ) )
2023-05-18 16:13:18 +00:00
if ! strings . Contains ( * msg . Topic , want ) {
2023-12-15 02:46:12 +00:00
log . WithFields ( blobFields ( blob ) ) . Debug ( "Sidecar index does not match topic" )
2023-05-18 16:13:18 +00:00
return pubsub . ValidationReject , fmt . Errorf ( "wrong topic name: %s" , * msg . Topic )
}
2024-01-06 23:47:09 +00:00
if err := vf . NotFromFutureSlot ( ) ; err != nil {
2023-12-11 00:37:45 +00:00
return pubsub . ValidationIgnore , err
2023-05-18 16:13:18 +00:00
}
2023-12-15 02:46:12 +00:00
startTime , err := slots . ToTime ( uint64 ( s . cfg . chain . GenesisTime ( ) . Unix ( ) ) , blob . Slot ( ) )
if err != nil {
2023-05-18 16:13:18 +00:00
return pubsub . ValidationIgnore , err
}
2023-12-11 00:37:45 +00:00
2023-12-15 02:46:12 +00:00
// [IGNORE] The sidecar is the first sidecar for the tuple (block_header.slot, block_header.proposer_index, sidecar.index) with valid header signature and sidecar inclusion proof
if s . hasSeenBlobIndex ( blob . Slot ( ) , blob . ProposerIndex ( ) , blob . Index ) {
return pubsub . ValidationIgnore , nil
}
if err := vf . SlotAboveFinalized ( ) ; err != nil {
return pubsub . ValidationIgnore , err
2023-05-18 16:13:18 +00:00
}
2023-12-11 00:37:45 +00:00
if err := vf . SidecarParentSeen ( s . hasBadBlock ) ; err != nil {
2023-10-17 14:42:15 +00:00
go func ( ) {
2023-12-11 00:37:45 +00:00
if err := s . sendBatchRootRequest ( context . Background ( ) , [ ] [ 32 ] byte { blob . ParentRoot ( ) } , rand . NewGenerator ( ) ) ; err != nil {
2023-10-17 14:42:15 +00:00
log . WithError ( err ) . WithFields ( blobFields ( blob ) ) . Debug ( "Failed to send batch root request" )
}
} ( )
2023-11-03 14:07:43 +00:00
missingParentBlobSidecarCount . Inc ( )
2023-12-11 00:37:45 +00:00
return pubsub . ValidationIgnore , err
2023-05-18 16:13:18 +00:00
}
2023-12-15 02:46:12 +00:00
if err := vf . ValidProposerSignature ( ctx ) ; err != nil {
return pubsub . ValidationReject , err
}
2023-12-11 00:37:45 +00:00
if err := vf . SidecarParentValid ( s . hasBadBlock ) ; err != nil {
return pubsub . ValidationReject , err
2023-10-17 14:42:15 +00:00
}
2023-12-11 00:37:45 +00:00
if err := vf . SidecarParentSlotLower ( ) ; err != nil {
return pubsub . ValidationReject , err
2023-10-17 14:42:15 +00:00
}
2023-12-11 00:37:45 +00:00
if err := vf . SidecarDescendsFromFinalized ( ) ; err != nil {
return pubsub . ValidationReject , err
2023-11-21 18:44:38 +00:00
}
2023-10-17 14:42:15 +00:00
2023-12-11 00:37:45 +00:00
if err := vf . SidecarInclusionProven ( ) ; err != nil {
2023-05-18 16:13:18 +00:00
return pubsub . ValidationReject , err
}
2023-12-11 00:37:45 +00:00
if err := vf . SidecarKzgProofVerified ( ) ; err != nil {
2024-03-12 17:51:08 +00:00
saveInvalidBlobToTemp ( blob )
2023-12-11 00:37:45 +00:00
return pubsub . ValidationReject , err
2023-05-18 16:13:18 +00:00
}
2023-12-11 00:37:45 +00:00
if err := vf . SidecarProposerExpected ( ctx ) ; err != nil {
2023-05-18 16:13:18 +00:00
return pubsub . ValidationReject , err
}
2023-12-11 00:37:45 +00:00
fields := blobFields ( blob )
sinceSlotStartTime := receivedTime . Sub ( startTime )
2024-03-13 16:10:18 +00:00
validationTime := s . cfg . clock . Now ( ) . Sub ( receivedTime )
2023-12-11 00:37:45 +00:00
fields [ "sinceSlotStartTime" ] = sinceSlotStartTime
2024-03-13 16:10:18 +00:00
fields [ "validationTime" ] = validationTime
2023-12-11 00:37:45 +00:00
log . WithFields ( fields ) . Debug ( "Received blob sidecar gossip" )
2024-03-13 16:10:18 +00:00
blobSidecarVerificationGossipSummary . Observe ( float64 ( validationTime . Milliseconds ( ) ) )
2023-12-11 00:37:45 +00:00
blobSidecarArrivalGossipSummary . Observe ( float64 ( sinceSlotStartTime . Milliseconds ( ) ) )
vBlobData , err := vf . VerifiedROBlob ( )
2023-05-18 16:13:18 +00:00
if err != nil {
2023-12-11 00:37:45 +00:00
return pubsub . ValidationReject , err
2023-05-18 16:13:18 +00:00
}
2023-12-11 00:37:45 +00:00
msg . ValidatorData = vBlobData
2023-05-18 16:13:18 +00:00
2023-12-11 00:37:45 +00:00
return pubsub . ValidationAccept , nil
2023-05-18 16:13:18 +00:00
}
2023-12-11 00:37:45 +00:00
// Returns true if the blob with the same slot, proposer index, and blob index has been seen before.
func ( s * Service ) hasSeenBlobIndex ( slot primitives . Slot , proposerIndex primitives . ValidatorIndex , index uint64 ) bool {
2023-05-18 16:13:18 +00:00
s . seenBlobLock . RLock ( )
defer s . seenBlobLock . RUnlock ( )
2023-12-11 00:37:45 +00:00
b := append ( bytesutil . Bytes32 ( uint64 ( slot ) ) , bytesutil . Bytes32 ( uint64 ( proposerIndex ) ) ... )
b = append ( b , bytesutil . Bytes32 ( index ) ... )
2023-05-18 16:13:18 +00:00
_ , seen := s . seenBlobCache . Get ( string ( b ) )
return seen
}
2023-12-11 00:37:45 +00:00
// Sets the blob with the same slot, proposer index, and blob index as seen.
func ( s * Service ) setSeenBlobIndex ( slot primitives . Slot , proposerIndex primitives . ValidatorIndex , index uint64 ) {
2023-05-18 16:13:18 +00:00
s . seenBlobLock . Lock ( )
defer s . seenBlobLock . Unlock ( )
2023-12-11 00:37:45 +00:00
b := append ( bytesutil . Bytes32 ( uint64 ( slot ) ) , bytesutil . Bytes32 ( uint64 ( proposerIndex ) ) ... )
b = append ( b , bytesutil . Bytes32 ( index ) ... )
2023-05-18 16:13:18 +00:00
s . seenBlobCache . Add ( string ( b ) , true )
}
2023-11-21 18:44:38 +00:00
func blobFields ( b blocks . ROBlob ) logrus . Fields {
2023-05-18 16:13:18 +00:00
return logrus . Fields {
2023-11-21 18:44:38 +00:00
"slot" : b . Slot ( ) ,
"proposerIndex" : b . ProposerIndex ( ) ,
"blockRoot" : fmt . Sprintf ( "%#x" , b . BlockRoot ( ) ) ,
2023-10-25 16:27:19 +00:00
"kzgCommitment" : fmt . Sprintf ( "%#x" , b . KzgCommitment ) ,
2023-05-18 16:13:18 +00:00
"index" : b . Index ,
}
}
2023-10-23 23:08:25 +00:00
func computeSubnetForBlobSidecar ( index uint64 ) uint64 {
return index % params . BeaconConfig ( ) . BlobsidecarSubnetCount
}
2024-03-12 17:51:08 +00:00
// saveInvalidBlobToTemp as a block ssz. Writes to temp directory.
func saveInvalidBlobToTemp ( b blocks . ROBlob ) {
if ! features . Get ( ) . SaveInvalidBlob {
return
}
2024-03-13 06:36:43 +00:00
filename := fmt . Sprintf ( "blob_sidecar_%#x_%d_%d.ssz" , b . BlockRoot ( ) , b . Slot ( ) , b . Index )
2024-03-12 17:51:08 +00:00
fp := path . Join ( os . TempDir ( ) , filename )
log . Warnf ( "Writing invalid blob sidecar to disk at %s" , fp )
enc , err := b . MarshalSSZ ( )
if err != nil {
log . WithError ( err ) . Error ( "Failed to ssz encode blob sidecar" )
return
}
if err := file . WriteFile ( fp , enc ) ; err != nil {
log . WithError ( err ) . Error ( "Failed to write to disk" )
}
}