package encoder_test import ( "bytes" "encoding/binary" "fmt" "io" "math" "testing" gogo "github.com/gogo/protobuf/proto" "github.com/google/go-cmp/cmp" fastssz "github.com/prysmaticlabs/fastssz" "github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/encoder" "github.com/prysmaticlabs/prysm/v5/config/params" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/testing/protocmp" ) // Define an interface that combines fastssz.Marshaler and proto.Message type MarshalerProtoMessage interface { fastssz.Marshaler proto.Message fastssz.Unmarshaler } type MarshalerProtoCreator interface { Create() MarshalerProtoMessage } type AttestationCreator struct{} type AttestationElectraCreator struct{} type AggregateAttestationAndProofCreator struct{} type AggregateAttestationAndProofElectraCreator struct{} type SignedAggregateAttestationAndProofCreator struct{} type SignedAggregateAttestationAndProofElectraCreator struct{} type AttestationDataCreator struct{} type CheckpointCreator struct{} type BeaconBlockCreator struct{} type SignedBeaconBlockCreator struct{} type BeaconBlockAltairCreator struct{} type SignedBeaconBlockAltairCreator struct{} type BeaconBlockBodyCreator struct{} type BeaconBlockBodyAltairCreator struct{} type ProposerSlashingCreator struct{} type AttesterSlashingCreator struct{} type AttesterSlashingElectraCreator struct{} type DepositCreator struct{} type VoluntaryExitCreator struct{} type SignedVoluntaryExitCreator struct{} type Eth1DataCreator struct{} type BeaconBlockHeaderCreator struct{} type SignedBeaconBlockHeaderCreator struct{} type IndexedAttestationCreator struct{} type IndexedAttestationElectraCreator struct{} type SyncAggregateCreator struct{} type SignedBeaconBlockBellatrixCreator struct{} type BeaconBlockBellatrixCreator struct{} type BeaconBlockBodyBellatrixCreator struct{} type SignedBlindedBeaconBlockBellatrixCreator struct{} type BlindedBeaconBlockBellatrixCreator struct{} type BlindedBeaconBlockBodyBellatrixCreator struct{} type SignedBeaconBlockContentsDenebCreator struct{} type BeaconBlockContentsDenebCreator struct{} type SignedBeaconBlockDenebCreator struct{} type BeaconBlockDenebCreator struct{} type BeaconBlockBodyDenebCreator struct{} type SignedBeaconBlockCapellaCreator struct{} type BeaconBlockCapellaCreator struct{} type BeaconBlockBodyCapellaCreator struct{} type SignedBlindedBeaconBlockCapellaCreator struct{} type BlindedBeaconBlockCapellaCreator struct{} type BlindedBeaconBlockBodyCapellaCreator struct{} type SignedBlindedBeaconBlockDenebCreator struct{} type BlindedBeaconBlockDenebCreator struct{} type BlindedBeaconBlockBodyDenebCreator struct{} type SignedBeaconBlockElectraCreator struct{} type BeaconBlockElectraCreator struct{} type BeaconBlockBodyElectraCreator struct{} type SignedBlindedBeaconBlockElectraCreator struct{} type BlindedBeaconBlockElectraCreator struct{} type BlindedBeaconBlockBodyElectraCreator struct{} type ValidatorRegistrationV1Creator struct{} type SignedValidatorRegistrationV1Creator struct{} type BuilderBidCreator struct{} type BuilderBidCapellaCreator struct{} type BuilderBidDenebCreator struct{} type BlobSidecarCreator struct{} type BlobSidecarsCreator struct{} type Deposit_DataCreator struct{} type BeaconStateCreator struct{} type BeaconStateAltairCreator struct{} type ForkCreator struct{} type PendingAttestationCreator struct{} type HistoricalBatchCreator struct{} type SigningDataCreator struct{} type ForkDataCreator struct{} type DepositMessageCreator struct{} type SyncCommitteeCreator struct{} type SyncAggregatorSelectionDataCreator struct{} type BeaconStateBellatrixCreator struct{} type BeaconStateCapellaCreator struct{} type BeaconStateDenebCreator struct{} type BeaconStateElectraCreator struct{} type PowBlockCreator struct{} type HistoricalSummaryCreator struct{} type BlobIdentifierCreator struct{} type PendingBalanceDepositCreator struct{} type PendingPartialWithdrawalCreator struct{} type PendingConsolidationCreator struct{} type StatusCreator struct{} type BeaconBlocksByRangeRequestCreator struct{} type ENRForkIDCreator struct{} type MetaDataV0Creator struct{} type MetaDataV1Creator struct{} type BlobSidecarsByRangeRequestCreator struct{} type DepositSnapshotCreator struct{} type SyncCommitteeMessageCreator struct{} type SyncCommitteeContributionCreator struct{} type ContributionAndProofCreator struct{} type SignedContributionAndProofCreator struct{} type ValidatorCreator struct{} type BLSToExecutionChangeCreator struct{} type SignedBLSToExecutionChangeCreator struct{} func (AttestationCreator) Create() MarshalerProtoMessage { return ðpb.Attestation{} } func (AttestationElectraCreator) Create() MarshalerProtoMessage { return ðpb.AttestationElectra{} } func (AggregateAttestationAndProofCreator) Create() MarshalerProtoMessage { return ðpb.AggregateAttestationAndProof{} } func (AggregateAttestationAndProofElectraCreator) Create() MarshalerProtoMessage { return ðpb.AggregateAttestationAndProofElectra{} } func (SignedAggregateAttestationAndProofCreator) Create() MarshalerProtoMessage { return ðpb.SignedAggregateAttestationAndProof{} } func (SignedAggregateAttestationAndProofElectraCreator) Create() MarshalerProtoMessage { return ðpb.SignedAggregateAttestationAndProofElectra{} } func (AttestationDataCreator) Create() MarshalerProtoMessage { return ðpb.AttestationData{} } func (CheckpointCreator) Create() MarshalerProtoMessage { return ðpb.Checkpoint{} } func (BeaconBlockCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlock{} } func (SignedBeaconBlockCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlock{} } func (BeaconBlockAltairCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockAltair{} } func (SignedBeaconBlockAltairCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockAltair{} } func (BeaconBlockBodyCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBody{} } func (BeaconBlockBodyAltairCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBodyAltair{} } func (ProposerSlashingCreator) Create() MarshalerProtoMessage { return ðpb.ProposerSlashing{} } func (AttesterSlashingCreator) Create() MarshalerProtoMessage { return ðpb.AttesterSlashing{} } func (AttesterSlashingElectraCreator) Create() MarshalerProtoMessage { return ðpb.AttesterSlashingElectra{} } func (DepositCreator) Create() MarshalerProtoMessage { return ðpb.Deposit{} } func (VoluntaryExitCreator) Create() MarshalerProtoMessage { return ðpb.VoluntaryExit{} } func (SignedVoluntaryExitCreator) Create() MarshalerProtoMessage { return ðpb.SignedVoluntaryExit{} } func (Eth1DataCreator) Create() MarshalerProtoMessage { return ðpb.Eth1Data{} } func (BeaconBlockHeaderCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockHeader{} } func (SignedBeaconBlockHeaderCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockHeader{} } func (IndexedAttestationCreator) Create() MarshalerProtoMessage { return ðpb.IndexedAttestation{} } func (IndexedAttestationElectraCreator) Create() MarshalerProtoMessage { return ðpb.IndexedAttestationElectra{} } func (SyncAggregateCreator) Create() MarshalerProtoMessage { return ðpb.SyncAggregate{} } func (SignedBeaconBlockBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockBellatrix{} } func (BeaconBlockBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBellatrix{} } func (BeaconBlockBodyBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBodyBellatrix{} } func (SignedBlindedBeaconBlockBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.SignedBlindedBeaconBlockBellatrix{} } func (BlindedBeaconBlockBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockBellatrix{} } func (BlindedBeaconBlockBodyBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockBodyBellatrix{} } func (SignedBeaconBlockContentsDenebCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockContentsDeneb{} } func (BeaconBlockContentsDenebCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockContentsDeneb{} } func (SignedBeaconBlockDenebCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockDeneb{} } func (BeaconBlockDenebCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockDeneb{} } func (BeaconBlockBodyDenebCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBodyDeneb{} } func (SignedBeaconBlockCapellaCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockCapella{} } func (BeaconBlockCapellaCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockCapella{} } func (BeaconBlockBodyCapellaCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBodyCapella{} } func (SignedBlindedBeaconBlockCapellaCreator) Create() MarshalerProtoMessage { return ðpb.SignedBlindedBeaconBlockCapella{} } func (BlindedBeaconBlockCapellaCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockCapella{} } func (BlindedBeaconBlockBodyCapellaCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockBodyCapella{} } func (SignedBlindedBeaconBlockDenebCreator) Create() MarshalerProtoMessage { return ðpb.SignedBlindedBeaconBlockDeneb{} } func (BlindedBeaconBlockDenebCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockDeneb{} } func (BlindedBeaconBlockBodyDenebCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockBodyDeneb{} } func (SignedBeaconBlockElectraCreator) Create() MarshalerProtoMessage { return ðpb.SignedBeaconBlockElectra{} } func (BeaconBlockElectraCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockElectra{} } func (BeaconBlockBodyElectraCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlockBodyElectra{} } func (SignedBlindedBeaconBlockElectraCreator) Create() MarshalerProtoMessage { return ðpb.SignedBlindedBeaconBlockElectra{} } func (BlindedBeaconBlockElectraCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockElectra{} } func (BlindedBeaconBlockBodyElectraCreator) Create() MarshalerProtoMessage { return ðpb.BlindedBeaconBlockBodyElectra{} } func (ValidatorRegistrationV1Creator) Create() MarshalerProtoMessage { return ðpb.ValidatorRegistrationV1{} } func (SignedValidatorRegistrationV1Creator) Create() MarshalerProtoMessage { return ðpb.SignedValidatorRegistrationV1{} } func (BuilderBidCreator) Create() MarshalerProtoMessage { return ðpb.BuilderBid{} } func (BuilderBidCapellaCreator) Create() MarshalerProtoMessage { return ðpb.BuilderBidCapella{} } func (BuilderBidDenebCreator) Create() MarshalerProtoMessage { return ðpb.BuilderBidDeneb{} } func (BlobSidecarCreator) Create() MarshalerProtoMessage { return ðpb.BlobSidecar{} } func (BlobSidecarsCreator) Create() MarshalerProtoMessage { return ðpb.BlobSidecars{} } func (Deposit_DataCreator) Create() MarshalerProtoMessage { return ðpb.Deposit_Data{} } func (BeaconStateCreator) Create() MarshalerProtoMessage { return ðpb.BeaconState{} } func (BeaconStateAltairCreator) Create() MarshalerProtoMessage { return ðpb.BeaconStateAltair{} } func (ForkCreator) Create() MarshalerProtoMessage { return ðpb.Fork{} } func (PendingAttestationCreator) Create() MarshalerProtoMessage { return ðpb.PendingAttestation{} } func (HistoricalBatchCreator) Create() MarshalerProtoMessage { return ðpb.HistoricalBatch{} } func (SigningDataCreator) Create() MarshalerProtoMessage { return ðpb.SigningData{} } func (ForkDataCreator) Create() MarshalerProtoMessage { return ðpb.ForkData{} } func (DepositMessageCreator) Create() MarshalerProtoMessage { return ðpb.DepositMessage{} } func (SyncCommitteeCreator) Create() MarshalerProtoMessage { return ðpb.SyncCommittee{} } func (SyncAggregatorSelectionDataCreator) Create() MarshalerProtoMessage { return ðpb.SyncAggregatorSelectionData{} } func (BeaconStateBellatrixCreator) Create() MarshalerProtoMessage { return ðpb.BeaconStateBellatrix{} } func (BeaconStateCapellaCreator) Create() MarshalerProtoMessage { return ðpb.BeaconStateCapella{} } func (BeaconStateDenebCreator) Create() MarshalerProtoMessage { return ðpb.BeaconStateDeneb{} } func (BeaconStateElectraCreator) Create() MarshalerProtoMessage { return ðpb.BeaconStateElectra{} } func (PowBlockCreator) Create() MarshalerProtoMessage { return ðpb.PowBlock{} } func (HistoricalSummaryCreator) Create() MarshalerProtoMessage { return ðpb.HistoricalSummary{} } func (BlobIdentifierCreator) Create() MarshalerProtoMessage { return ðpb.BlobIdentifier{} } func (PendingBalanceDepositCreator) Create() MarshalerProtoMessage { return ðpb.PendingBalanceDeposit{} } func (PendingPartialWithdrawalCreator) Create() MarshalerProtoMessage { return ðpb.PendingPartialWithdrawal{} } func (PendingConsolidationCreator) Create() MarshalerProtoMessage { return ðpb.PendingConsolidation{} } func (StatusCreator) Create() MarshalerProtoMessage { return ðpb.Status{} } func (BeaconBlocksByRangeRequestCreator) Create() MarshalerProtoMessage { return ðpb.BeaconBlocksByRangeRequest{} } func (ENRForkIDCreator) Create() MarshalerProtoMessage { return ðpb.ENRForkID{} } func (MetaDataV0Creator) Create() MarshalerProtoMessage { return ðpb.MetaDataV0{} } func (MetaDataV1Creator) Create() MarshalerProtoMessage { return ðpb.MetaDataV1{} } func (BlobSidecarsByRangeRequestCreator) Create() MarshalerProtoMessage { return ðpb.BlobSidecarsByRangeRequest{} } func (DepositSnapshotCreator) Create() MarshalerProtoMessage { return ðpb.DepositSnapshot{} } func (SyncCommitteeMessageCreator) Create() MarshalerProtoMessage { return ðpb.SyncCommitteeMessage{} } func (SyncCommitteeContributionCreator) Create() MarshalerProtoMessage { return ðpb.SyncCommitteeContribution{} } func (ContributionAndProofCreator) Create() MarshalerProtoMessage { return ðpb.ContributionAndProof{} } func (SignedContributionAndProofCreator) Create() MarshalerProtoMessage { return ðpb.SignedContributionAndProof{} } func (ValidatorCreator) Create() MarshalerProtoMessage { return ðpb.Validator{} } func (BLSToExecutionChangeCreator) Create() MarshalerProtoMessage { return ðpb.BLSToExecutionChange{} } func (SignedBLSToExecutionChangeCreator) Create() MarshalerProtoMessage { return ðpb.SignedBLSToExecutionChange{} } var creators = []MarshalerProtoCreator{ AttestationCreator{}, AttestationElectraCreator{}, AggregateAttestationAndProofCreator{}, AggregateAttestationAndProofElectraCreator{}, SignedAggregateAttestationAndProofCreator{}, SignedAggregateAttestationAndProofElectraCreator{}, AttestationDataCreator{}, CheckpointCreator{}, BeaconBlockCreator{}, SignedBeaconBlockCreator{}, BeaconBlockAltairCreator{}, SignedBeaconBlockAltairCreator{}, BeaconBlockBodyCreator{}, BeaconBlockBodyAltairCreator{}, ProposerSlashingCreator{}, AttesterSlashingCreator{}, AttesterSlashingElectraCreator{}, DepositCreator{}, VoluntaryExitCreator{}, SignedVoluntaryExitCreator{}, Eth1DataCreator{}, BeaconBlockHeaderCreator{}, SignedBeaconBlockHeaderCreator{}, IndexedAttestationCreator{}, IndexedAttestationElectraCreator{}, SyncAggregateCreator{}, SignedBeaconBlockBellatrixCreator{}, BeaconBlockBellatrixCreator{}, BeaconBlockBodyBellatrixCreator{}, SignedBlindedBeaconBlockBellatrixCreator{}, BlindedBeaconBlockBellatrixCreator{}, BlindedBeaconBlockBodyBellatrixCreator{}, SignedBeaconBlockContentsDenebCreator{}, BeaconBlockContentsDenebCreator{}, SignedBeaconBlockDenebCreator{}, BeaconBlockDenebCreator{}, BeaconBlockBodyDenebCreator{}, SignedBeaconBlockCapellaCreator{}, BeaconBlockCapellaCreator{}, BeaconBlockBodyCapellaCreator{}, SignedBlindedBeaconBlockCapellaCreator{}, BlindedBeaconBlockCapellaCreator{}, BlindedBeaconBlockBodyCapellaCreator{}, SignedBlindedBeaconBlockDenebCreator{}, BlindedBeaconBlockDenebCreator{}, BlindedBeaconBlockBodyDenebCreator{}, SignedBeaconBlockElectraCreator{}, BeaconBlockElectraCreator{}, BeaconBlockBodyElectraCreator{}, SignedBlindedBeaconBlockElectraCreator{}, BlindedBeaconBlockElectraCreator{}, BlindedBeaconBlockBodyElectraCreator{}, ValidatorRegistrationV1Creator{}, SignedValidatorRegistrationV1Creator{}, BuilderBidCreator{}, BuilderBidCapellaCreator{}, BuilderBidDenebCreator{}, BlobSidecarCreator{}, BlobSidecarsCreator{}, Deposit_DataCreator{}, BeaconStateCreator{}, BeaconStateAltairCreator{}, ForkCreator{}, PendingAttestationCreator{}, HistoricalBatchCreator{}, SigningDataCreator{}, ForkDataCreator{}, DepositMessageCreator{}, SyncCommitteeCreator{}, SyncAggregatorSelectionDataCreator{}, BeaconStateBellatrixCreator{}, BeaconStateCapellaCreator{}, BeaconStateDenebCreator{}, BeaconStateElectraCreator{}, PowBlockCreator{}, HistoricalSummaryCreator{}, BlobIdentifierCreator{}, PendingBalanceDepositCreator{}, PendingPartialWithdrawalCreator{}, PendingConsolidationCreator{}, StatusCreator{}, BeaconBlocksByRangeRequestCreator{}, ENRForkIDCreator{}, MetaDataV0Creator{}, MetaDataV1Creator{}, BlobSidecarsByRangeRequestCreator{}, DepositSnapshotCreator{}, SyncCommitteeMessageCreator{}, SyncCommitteeContributionCreator{}, ContributionAndProofCreator{}, SignedContributionAndProofCreator{}, ValidatorCreator{}, BLSToExecutionChangeCreator{}, SignedBLSToExecutionChangeCreator{}, } func assertProtoMessagesEqual(t *testing.T, decoded, msg proto.Message) { // Check if two proto messages are equal if proto.Equal(decoded, msg) { return } // If they are not equal, check if their unknown values are equal // Ignore unknown fields when comparing proto messages a := decoded.ProtoReflect().GetUnknown() b := msg.ProtoReflect().GetUnknown() if !bytes.Equal(a, b) { return } // If unknown values are equal, check if any of the fields of the proto message are proto messages themselves containsNestedProtoMessage := false decoded.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { // Check if the field is a message if fd.Kind() == protoreflect.MessageKind { containsNestedProtoMessage = true // Get the corresponding field from the other message otherValue := msg.ProtoReflect().Get(fd) // If the field is not set in the other message, skip it if !otherValue.IsValid() { return true } // Recursively compare the fields assertProtoMessagesEqual(t, v.Message().Interface(), otherValue.Message().Interface()) } return true }) // If there are no proto messages contained inside, then throw an error // The error is thrown iff the decoded message is not equal to the original message // after ignoring unknown fields in (nested) proto message(s). if !containsNestedProtoMessage { t.Log(cmp.Diff(decoded, msg, protocmp.Transform())) t.Fatal("Decoded message is not the same as original") } } // Refactor the unmarshal logic into a private function. func unmarshalProtoMessage(data []byte, creator MarshalerProtoCreator) (MarshalerProtoMessage, error) { msg := creator.Create() if err := proto.Unmarshal(data, msg); err != nil { return nil, err } return msg, nil } func gossipRoundTripHelper(t *testing.T, msg MarshalerProtoMessage, creator MarshalerProtoCreator) { e := &encoder.SszNetworkEncoder{} buf := new(bytes.Buffer) // Example of calling a function that requires MarshalerProtoMessage _, err := e.EncodeGossip(buf, msg) if err != nil { t.Logf("Failed to encode: %v", err) return } decoded := creator.Create() if err := e.DecodeGossip(buf.Bytes(), decoded); err != nil { t.Fatalf("Failed to decode: %v", err) } assertProtoMessagesEqual(t, decoded, msg) } func lengthRoundTripHelper(t *testing.T, msg MarshalerProtoMessage, creator MarshalerProtoCreator) { e := &encoder.SszNetworkEncoder{} buf := new(bytes.Buffer) // Example of calling a function that requires MarshalerProtoMessage _, err := e.EncodeWithMaxLength(buf, msg) if err != nil { t.Logf("Failed to encode: %v", err) return } decoded := creator.Create() if err := e.DecodeWithMaxLength(buf, decoded); err != nil { t.Fatalf("Failed to decode: %v", err) } assertProtoMessagesEqual(t, decoded, msg) } func FuzzRoundTripWithGossip(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte, index int) { if index < 0 || index >= len(creators) { t.Skip() } // Select a random creator from the list. creator := creators[index] msg, err := unmarshalProtoMessage(data, creator) if err != nil { t.Logf("Failed to unmarshal: %v", err) return } gossipRoundTripHelper(t, msg, creator) lengthRoundTripHelper(t, msg, creator) }) } func TestSszNetworkEncoder_RoundTrip_SignedVoluntaryExit(t *testing.T) { e := &encoder.SszNetworkEncoder{} buf := new(bytes.Buffer) data := []byte("\x12`000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n\n0000000000") msg := ðpb.SignedVoluntaryExit{} if err := proto.Unmarshal(data, msg); err != nil { t.Logf("Failed to unmarshal: %v", err) return } _, err := e.EncodeGossip(buf, msg) require.NoError(t, err) decoded := ðpb.SignedVoluntaryExit{} require.NoError(t, e.DecodeGossip(buf.Bytes(), decoded)) assertProtoMessagesEqual(t, decoded, msg) } func TestSszNetworkEncoder_RoundTrip(t *testing.T) { e := &encoder.SszNetworkEncoder{} testRoundTripWithLength(t, e) testRoundTripWithGossip(t, e) } func TestSszNetworkEncoder_FailsSnappyLength(t *testing.T) { e := &encoder.SszNetworkEncoder{} att := ðpb.Fork{} data := make([]byte, 32) binary.PutUvarint(data, encoder.MaxGossipSize+32) err := e.DecodeGossip(data, att) require.ErrorContains(t, "snappy message exceeds max size", err) } func testRoundTripWithLength(t *testing.T, e *encoder.SszNetworkEncoder) { buf := new(bytes.Buffer) msg := ðpb.Fork{ PreviousVersion: []byte("fooo"), CurrentVersion: []byte("barr"), Epoch: 9001, } _, err := e.EncodeWithMaxLength(buf, msg) require.NoError(t, err) decoded := ðpb.Fork{} require.NoError(t, e.DecodeWithMaxLength(buf, decoded)) if !proto.Equal(decoded, msg) { t.Logf("decoded=%+v\n", decoded) t.Error("Decoded message is not the same as original") } } func testRoundTripWithGossip(t *testing.T, e *encoder.SszNetworkEncoder) { buf := new(bytes.Buffer) msg := ðpb.Fork{ PreviousVersion: []byte("fooo"), CurrentVersion: []byte("barr"), Epoch: 9001, } _, err := e.EncodeGossip(buf, msg) require.NoError(t, err) decoded := ðpb.Fork{} require.NoError(t, e.DecodeGossip(buf.Bytes(), decoded)) if !proto.Equal(decoded, msg) { t.Logf("decoded=%+v\n", decoded) t.Error("Decoded message is not the same as original") } } func TestSszNetworkEncoder_EncodeWithMaxLength(t *testing.T) { buf := new(bytes.Buffer) msg := ðpb.Fork{ PreviousVersion: []byte("fooo"), CurrentVersion: []byte("barr"), Epoch: 9001, } e := &encoder.SszNetworkEncoder{} params.SetupTestConfigCleanup(t) c := params.BeaconNetworkConfig() encoder.MaxChunkSize = uint64(5) params.OverrideBeaconNetworkConfig(c) _, err := e.EncodeWithMaxLength(buf, msg) wanted := fmt.Sprintf("which is larger than the provided max limit of %d", encoder.MaxChunkSize) assert.ErrorContains(t, wanted, err) } func TestSszNetworkEncoder_DecodeWithMaxLength(t *testing.T) { buf := new(bytes.Buffer) msg := ðpb.Fork{ PreviousVersion: []byte("fooo"), CurrentVersion: []byte("barr"), Epoch: 4242, } e := &encoder.SszNetworkEncoder{} params.SetupTestConfigCleanup(t) c := params.BeaconNetworkConfig() maxChunkSize := uint64(5) encoder.MaxChunkSize = maxChunkSize params.OverrideBeaconNetworkConfig(c) _, err := e.EncodeGossip(buf, msg) require.NoError(t, err) decoded := ðpb.Fork{} err = e.DecodeWithMaxLength(buf, decoded) wanted := fmt.Sprintf("goes over the provided max limit of %d", maxChunkSize) assert.ErrorContains(t, wanted, err) } func TestSszNetworkEncoder_DecodeWithMultipleFrames(t *testing.T) { buf := new(bytes.Buffer) st, _ := util.DeterministicGenesisState(t, 100) e := &encoder.SszNetworkEncoder{} params.SetupTestConfigCleanup(t) c := params.BeaconNetworkConfig() // 4 * 1 Mib maxChunkSize := uint64(1 << 22) encoder.MaxChunkSize = maxChunkSize params.OverrideBeaconNetworkConfig(c) _, err := e.EncodeWithMaxLength(buf, st.ToProtoUnsafe().(*ethpb.BeaconState)) require.NoError(t, err) // Max snappy block size if buf.Len() <= 76490 { t.Errorf("buffer smaller than expected, wanted > %d but got %d", 76490, buf.Len()) } decoded := new(ethpb.BeaconState) err = e.DecodeWithMaxLength(buf, decoded) assert.NoError(t, err) } func TestSszNetworkEncoder_NegativeMaxLength(t *testing.T) { e := &encoder.SszNetworkEncoder{} length, err := e.MaxLength(0xfffffffffff) assert.Equal(t, 0, length, "Received non zero length on bad message length") assert.ErrorContains(t, "max encoded length is negative", err) } func TestSszNetworkEncoder_MaxInt64(t *testing.T) { e := &encoder.SszNetworkEncoder{} length, err := e.MaxLength(math.MaxInt64 + 1) assert.Equal(t, 0, length, "Received non zero length on bad message length") assert.ErrorContains(t, "invalid length provided", err) } func TestSszNetworkEncoder_DecodeWithBadSnappyStream(t *testing.T) { st := newBadSnappyStream() e := &encoder.SszNetworkEncoder{} decoded := new(ethpb.Fork) err := e.DecodeWithMaxLength(st, decoded) assert.ErrorContains(t, io.EOF.Error(), err) } type badSnappyStream struct { varint []byte header []byte repeat []byte i int // count how many times it was read counter int // count bytes read so far total int } func newBadSnappyStream() *badSnappyStream { const ( magicBody = "sNaPpY" magicChunk = "\xff\x06\x00\x00" + magicBody ) header := make([]byte, len(magicChunk)) // magicChunk == chunkTypeStreamIdentifier byte ++ 3 byte little endian len(magic body) ++ 6 byte magic body // header is a special chunk type, with small fixed length, to add some magic to claim it's really snappy. copy(header, magicChunk) // snappy library constants help us construct the common header chunk easily. payload := make([]byte, 4) // byte 0 is chunk type // Exploit any fancy ignored chunk type // Section 4.4 Padding (chunk type 0xfe). // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). payload[0] = 0xfe // byte 1,2,3 are chunk length (little endian) payload[1] = 0 payload[2] = 0 payload[3] = 0 return &badSnappyStream{ varint: gogo.EncodeVarint(1000), header: header, repeat: payload, i: 0, counter: 0, total: 0, } } func (b *badSnappyStream) Read(p []byte) (n int, err error) { // Stream out varint bytes first to make test happy. if len(b.varint) > 0 { copy(p, b.varint[:1]) b.varint = b.varint[1:] return 1, nil } defer func() { b.counter += 1 b.total += n }() if len(b.repeat) == 0 { panic("no bytes to repeat") } if len(b.header) > 0 { n = copy(p, b.header) b.header = b.header[n:] return } for n < len(p) { n += copy(p[n:], b.repeat[b.i:]) b.i = (b.i + n) % len(b.repeat) } return }