diff --git a/cl/cltypes/attestations.go b/cl/cltypes/attestations.go index 24e11980082..e86e0d5bb31 100644 --- a/cl/cltypes/attestations.go +++ b/cl/cltypes/attestations.go @@ -497,7 +497,7 @@ func (a *AttestationData) DecodeSSZ(buf []byte) error { // EncodingSizeSSZ returns the ssz encoded size in bytes for the AttestationData object func (a *AttestationData) EncodingSizeSSZ() int { - return 2*common.BlockNumberLength + length.Hash + a.Source.EncodingSizeSSZ() + a.Target.EncodingSizeSSZ() + return 2*common.BlockNumberLength + length.Hash + 40 + 40 } // HashSSZ ssz hashes the AttestationData object @@ -518,3 +518,95 @@ func (a *AttestationData) HashSSZ() ([32]byte, error) { targetRoot, }, 8) } + +// Pending attestation. (only in Phase0 state) +type PendingAttestation struct { + AggregationBits []byte + Data *AttestationData + InclusionDelay uint64 + ProposerIndex uint64 +} + +// MarshalSSZTo ssz marshals the Attestation object to a target array +func (a *PendingAttestation) EncodeSSZ(buf []byte) (dst []byte, err error) { + dst = buf + dst = append(dst, ssz_utils.OffsetSSZ(148)...) + if dst, err = a.Data.EncodeSSZ(dst); err != nil { + return + } + fmt.Println(dst) + dst = append(dst, ssz_utils.Uint64SSZ(a.InclusionDelay)...) + dst = append(dst, ssz_utils.Uint64SSZ(a.ProposerIndex)...) + + if len(a.AggregationBits) > 2048 { + return nil, fmt.Errorf("too many aggregation bits in attestation") + } + dst = append(dst, a.AggregationBits...) + + return +} + +// DecodeSSZ ssz unmarshals the Attestation object +func (a *PendingAttestation) DecodeSSZ(buf []byte) error { + var err error + if len(buf) < a.EncodingSizeSSZ() { + return ssz_utils.ErrLowBufferSize + } + + tail := buf + + // Field (1) 'Data' + if a.Data == nil { + a.Data = new(AttestationData) + } + if err = a.Data.DecodeSSZ(buf[4:132]); err != nil { + return err + } + + a.InclusionDelay = ssz_utils.UnmarshalUint64SSZ(buf[132:]) + a.ProposerIndex = ssz_utils.UnmarshalUint64SSZ(buf[140:]) + // Field (0) 'AggregationBits' + { + buf = tail[148:] + if err = ssz.ValidateBitlist(buf, 2048); err != nil { + return err + } + if cap(a.AggregationBits) == 0 { + a.AggregationBits = make([]byte, 0, len(buf)) + } + a.AggregationBits = append(a.AggregationBits, buf...) + } + return err +} + +func (a *PendingAttestation) DecodeSSZWithVersion(buf []byte, _ int) error { + return a.DecodeSSZ(buf) +} + +// EncodingSizeSSZ returns the ssz encoded size in bytes for the Attestation object +func (a *PendingAttestation) EncodingSizeSSZ() int { + return 148 + len(a.AggregationBits) +} + +// HashSSZ ssz hashes the Attestation object +func (a *PendingAttestation) HashSSZ() ([32]byte, error) { + leaves := make([][32]byte, 4) + var err error + if a.Data == nil { + return [32]byte{}, fmt.Errorf("missing attestation data") + } + leaves[0], err = merkle_tree.BitlistRootWithLimit(a.AggregationBits, 2048) + if err != nil { + return [32]byte{}, err + } + + leaves[1], err = a.Data.HashSSZ() + if err != nil { + return [32]byte{}, err + } + + leaves[2] = merkle_tree.Uint64Root(a.InclusionDelay) + leaves[3] = merkle_tree.Uint64Root(a.ProposerIndex) + + return merkle_tree.ArraysRoot(leaves, 4) +} diff --git a/cl/cltypes/attestations_test.go b/cl/cltypes/attestations_test.go index ef9e512d82d..a021e37d7c8 100644 --- a/cl/cltypes/attestations_test.go +++ b/cl/cltypes/attestations_test.go @@ -8,7 +8,10 @@ import ( "github.com/stretchr/testify/require" "github.com/ledgerwatch/erigon/cl/cltypes" + "github.com/ledgerwatch/erigon/cl/utils" "github.com/ledgerwatch/erigon/common" + + _ "embed" ) var testAttData = &cltypes.AttestationData{ @@ -62,3 +65,22 @@ func TestAttestationMarshalUnmarmashal(t *testing.T) { require.NoError(t, testData2.DecodeSSZ(marshalled)) require.Equal(t, testData2, attestations[0]) } + +//go:embed tests/pending_attestation.ssz_snappy +var pendingAttestationTest []byte + +func TestPendingAttestation(t *testing.T) { + att := &cltypes.PendingAttestation{} + encodedExpected, err := utils.DecompressSnappy(pendingAttestationTest) + require.NoError(t, err) + + require.NoError(t, att.DecodeSSZ(encodedExpected)) + + root, err := att.HashSSZ() + require.NoError(t, err) + require.Equal(t, libcommon.HexToHash("6d73ce691559544e2d4ec0071abd7e7a4dbe3a3ede4d7008e0f6db75deb40bde"), libcommon.Hash(root)) + + encodedHave, err := att.EncodeSSZ(nil) + require.NoError(t, err) + require.Equal(t, encodedExpected, encodedHave) +} diff --git a/cl/cltypes/clone.go b/cl/cltypes/clone.go index f8f4911afad..b11cc2f0fe4 100644 --- a/cl/cltypes/clone.go +++ b/cl/cltypes/clone.go @@ -6,6 +6,10 @@ func (*SignedBeaconBlock) Clone() clonable.Clonable { return &SignedBeaconBlock{} } +func (*PendingAttestation) Clone() clonable.Clonable { + return &PendingAttestation{} +} + func (*Eth1Block) Clone() clonable.Clonable { return &Eth1Block{} } diff --git a/cl/cltypes/tests/pending_attestation.ssz_snappy b/cl/cltypes/tests/pending_attestation.ssz_snappy new file mode 100644 index 00000000000..a55d6dd1d03 Binary files /dev/null and b/cl/cltypes/tests/pending_attestation.ssz_snappy differ