diff --git a/api/versionedblockrequest.go b/api/versionedblockrequest.go index 40bbaa56..7d46fa53 100644 --- a/api/versionedblockrequest.go +++ b/api/versionedblockrequest.go @@ -333,7 +333,7 @@ func (v *VersionedBlockRequest) StateRoot() (phase0.Root, error) { } // AttesterSlashings returns the attester slashings of the beacon block. -func (v *VersionedBlockRequest) AttesterSlashings() ([]*phase0.AttesterSlashing, error) { +func (v *VersionedBlockRequest) AttesterSlashings() ([]spec.VersionedAttesterSlashing, error) { switch v.Version { case spec.DataVersionBellatrix: if v.Bellatrix == nil || @@ -342,7 +342,15 @@ func (v *VersionedBlockRequest) AttesterSlashings() ([]*phase0.AttesterSlashing, return nil, ErrDataMissing } - return v.Bellatrix.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]spec.VersionedAttesterSlashing, len(v.Bellatrix.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Bellatrix.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = spec.VersionedAttesterSlashing{ + Version: spec.DataVersionBellatrix, + Bellatrix: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case spec.DataVersionCapella: if v.Capella == nil || v.Capella.Message == nil || @@ -350,7 +358,15 @@ func (v *VersionedBlockRequest) AttesterSlashings() ([]*phase0.AttesterSlashing, return nil, ErrDataMissing } - return v.Capella.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]spec.VersionedAttesterSlashing, len(v.Capella.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Capella.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = spec.VersionedAttesterSlashing{ + Version: spec.DataVersionCapella, + Capella: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case spec.DataVersionDeneb: if v.Deneb == nil || v.Deneb.Message == nil || @@ -358,7 +374,15 @@ func (v *VersionedBlockRequest) AttesterSlashings() ([]*phase0.AttesterSlashing, return nil, ErrDataMissing } - return v.Deneb.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]spec.VersionedAttesterSlashing, len(v.Deneb.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Deneb.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = spec.VersionedAttesterSlashing{ + Version: spec.DataVersionDeneb, + Deneb: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case spec.DataVersionElectra: if v.Electra == nil || v.Electra.Message == nil || @@ -366,7 +390,15 @@ func (v *VersionedBlockRequest) AttesterSlashings() ([]*phase0.AttesterSlashing, return nil, ErrDataMissing } - return v.Electra.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]spec.VersionedAttesterSlashing, len(v.Electra.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Electra.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = spec.VersionedAttesterSlashing{ + Version: spec.DataVersionElectra, + Electra: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil default: return nil, ErrUnsupportedVersion } diff --git a/spec/versionedattesterslashing.go b/spec/versionedattesterslashing.go new file mode 100644 index 00000000..1aab159f --- /dev/null +++ b/spec/versionedattesterslashing.go @@ -0,0 +1,229 @@ +// Copyright © 2024 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "errors" + + "github.com/attestantio/go-eth2-client/spec/electra" + "github.com/attestantio/go-eth2-client/spec/phase0" +) + +// VersionedAttesterSlashing contains a versioned attestation. +type VersionedAttesterSlashing struct { + Version DataVersion + Phase0 *phase0.AttesterSlashing + Altair *phase0.AttesterSlashing + Bellatrix *phase0.AttesterSlashing + Capella *phase0.AttesterSlashing + Deneb *phase0.AttesterSlashing + Electra *electra.AttesterSlashing +} + +// IsEmpty returns true if there is no block. +func (v *VersionedAttesterSlashing) IsEmpty() bool { + return v.Phase0 == nil && v.Altair == nil && v.Bellatrix == nil && v.Capella == nil && v.Deneb == nil && v.Electra == nil +} + +// Attestation1 returns the first indexed attestation. +func (v *VersionedAttesterSlashing) Attestation1() (*VersionedIndexedAttestation, error) { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return nil, errors.New("no Phase0 indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionPhase0, + Phase0: v.Phase0.Attestation1, + } + + return &versionedIndexedAttestation, nil + case DataVersionAltair: + if v.Altair == nil { + return nil, errors.New("no Altair indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionPhase0, + Altair: v.Altair.Attestation1, + } + + return &versionedIndexedAttestation, nil + case DataVersionBellatrix: + if v.Bellatrix == nil { + return nil, errors.New("no Bellatrix indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionPhase0, + Bellatrix: v.Bellatrix.Attestation1, + } + + return &versionedIndexedAttestation, nil + case DataVersionCapella: + if v.Capella == nil { + return nil, errors.New("no Capella indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionCapella, + Capella: v.Capella.Attestation1, + } + + return &versionedIndexedAttestation, nil + case DataVersionDeneb: + if v.Deneb == nil { + return nil, errors.New("no Deneb indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionDeneb, + Deneb: v.Deneb.Attestation1, + } + + return &versionedIndexedAttestation, nil + case DataVersionElectra: + if v.Electra == nil { + return nil, errors.New("no Electra indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionElectra, + Electra: v.Electra.Attestation1, + } + + return &versionedIndexedAttestation, nil + default: + return nil, errors.New("unknown version") + } +} + +// Attestation2 returns the second indexed attestation. +func (v *VersionedAttesterSlashing) Attestation2() (*VersionedIndexedAttestation, error) { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return nil, errors.New("no Phase0 indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionPhase0, + Phase0: v.Phase0.Attestation2, + } + + return &versionedIndexedAttestation, nil + case DataVersionAltair: + if v.Altair == nil { + return nil, errors.New("no Altair indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionPhase0, + Altair: v.Altair.Attestation2, + } + + return &versionedIndexedAttestation, nil + case DataVersionBellatrix: + if v.Bellatrix == nil { + return nil, errors.New("no Bellatrix indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionPhase0, + Bellatrix: v.Bellatrix.Attestation2, + } + + return &versionedIndexedAttestation, nil + case DataVersionCapella: + if v.Capella == nil { + return nil, errors.New("no Capella indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionCapella, + Capella: v.Capella.Attestation2, + } + + return &versionedIndexedAttestation, nil + case DataVersionDeneb: + if v.Deneb == nil { + return nil, errors.New("no Deneb indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionDeneb, + Deneb: v.Deneb.Attestation2, + } + + return &versionedIndexedAttestation, nil + case DataVersionElectra: + if v.Electra == nil { + return nil, errors.New("no Electra indexed attestation") + } + + versionedIndexedAttestation := VersionedIndexedAttestation{ + Version: DataVersionElectra, + Electra: v.Electra.Attestation2, + } + + return &versionedIndexedAttestation, nil + default: + return nil, errors.New("unknown version") + } +} + +// String returns a string version of the structure. +func (v *VersionedAttesterSlashing) String() string { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return "" + } + + return v.Phase0.String() + case DataVersionAltair: + if v.Altair == nil { + return "" + } + + return v.Altair.String() + case DataVersionBellatrix: + if v.Bellatrix == nil { + return "" + } + + return v.Bellatrix.String() + case DataVersionCapella: + if v.Capella == nil { + return "" + } + + return v.Capella.String() + case DataVersionDeneb: + if v.Deneb == nil { + return "" + } + + return v.Deneb.String() + case DataVersionElectra: + if v.Electra == nil { + return "" + } + + return v.Electra.String() + default: + return "unknown version" + } +} diff --git a/spec/versionedbeaconblock.go b/spec/versionedbeaconblock.go index 341b992d..f878ec9b 100644 --- a/spec/versionedbeaconblock.go +++ b/spec/versionedbeaconblock.go @@ -540,44 +540,92 @@ func (v *VersionedBeaconBlock) Attestations() ([]VersionedAttestation, error) { } // AttesterSlashings returns the attester slashings of the beacon block. -func (v *VersionedBeaconBlock) AttesterSlashings() ([]*phase0.AttesterSlashing, error) { +func (v *VersionedBeaconBlock) AttesterSlashings() ([]VersionedAttesterSlashing, error) { switch v.Version { case DataVersionPhase0: if v.Phase0 == nil || v.Phase0.Body == nil { return nil, errors.New("no phase0 block") } - return v.Phase0.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Phase0.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Phase0.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionPhase0, + Phase0: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionAltair: if v.Altair == nil || v.Altair.Body == nil { return nil, errors.New("no altair block") } - return v.Altair.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Altair.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Altair.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionAltair, + Altair: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionBellatrix: if v.Bellatrix == nil || v.Bellatrix.Body == nil { return nil, errors.New("no bellatrix block") } - return v.Bellatrix.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Bellatrix.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Bellatrix.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionBellatrix, + Bellatrix: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionCapella: if v.Capella == nil || v.Capella.Body == nil { return nil, errors.New("no capella block") } - return v.Capella.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Capella.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Capella.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionCapella, + Capella: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionDeneb: if v.Deneb == nil || v.Deneb.Body == nil { return nil, errors.New("no deneb block") } - return v.Deneb.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Deneb.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Deneb.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionDeneb, + Deneb: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionElectra: if v.Electra == nil || v.Electra.Body == nil { return nil, errors.New("no electra block") } - return v.Electra.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Electra.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Electra.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionElectra, + Electra: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil default: return nil, errors.New("unknown version") } diff --git a/spec/versionedindexedattestation.go b/spec/versionedindexedattestation.go new file mode 100644 index 00000000..e3baaa85 --- /dev/null +++ b/spec/versionedindexedattestation.go @@ -0,0 +1,213 @@ +// Copyright © 2024 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "errors" + + "github.com/attestantio/go-eth2-client/spec/electra" + "github.com/attestantio/go-eth2-client/spec/phase0" +) + +// VersionedIndexedAttestation contains a versioned indexed attestation. +type VersionedIndexedAttestation struct { + Version DataVersion + Phase0 *phase0.IndexedAttestation + Altair *phase0.IndexedAttestation + Bellatrix *phase0.IndexedAttestation + Capella *phase0.IndexedAttestation + Deneb *phase0.IndexedAttestation + Electra *electra.IndexedAttestation +} + +// IsEmpty returns true if there is no block. +func (v *VersionedIndexedAttestation) IsEmpty() bool { + return v.Phase0 == nil && v.Altair == nil && v.Bellatrix == nil && v.Capella == nil && v.Deneb == nil && v.Electra == nil +} + +// AttestingIndices returns the attesting indices of the indexed attestation. +func (v *VersionedIndexedAttestation) AttestingIndices() ([]uint64, error) { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return nil, errors.New("no Phase0 indexed attestation") + } + + return v.Phase0.AttestingIndices, nil + case DataVersionAltair: + if v.Altair == nil { + return nil, errors.New("no Altair indexed attestation") + } + + return v.Altair.AttestingIndices, nil + case DataVersionBellatrix: + if v.Bellatrix == nil { + return nil, errors.New("no Bellatrix indexed attestation") + } + + return v.Bellatrix.AttestingIndices, nil + case DataVersionCapella: + if v.Capella == nil { + return nil, errors.New("no Capella indexed attestation") + } + + return v.Capella.AttestingIndices, nil + case DataVersionDeneb: + if v.Deneb == nil { + return nil, errors.New("no Deneb indexed attestation") + } + + return v.Deneb.AttestingIndices, nil + case DataVersionElectra: + if v.Electra == nil { + return nil, errors.New("no Electra indexed attestation") + } + + return v.Electra.AttestingIndices, nil + default: + return nil, errors.New("unknown version") + } +} + +// Data returns the data of the indexed attestation. +func (v *VersionedIndexedAttestation) Data() (*phase0.AttestationData, error) { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return nil, errors.New("no Phase0 indexed attestation") + } + + return v.Phase0.Data, nil + case DataVersionAltair: + if v.Altair == nil { + return nil, errors.New("no Altair indexed attestation") + } + + return v.Altair.Data, nil + case DataVersionBellatrix: + if v.Bellatrix == nil { + return nil, errors.New("no Bellatrix indexed attestation") + } + + return v.Bellatrix.Data, nil + case DataVersionCapella: + if v.Capella == nil { + return nil, errors.New("no Capella indexed attestation") + } + + return v.Capella.Data, nil + case DataVersionDeneb: + if v.Deneb == nil { + return nil, errors.New("no Deneb indexed attestation") + } + + return v.Deneb.Data, nil + case DataVersionElectra: + if v.Electra == nil { + return nil, errors.New("no Electra indexed attestation") + } + + return v.Electra.Data, nil + default: + return nil, errors.New("unknown version") + } +} + +// Signature returns the signature of the indexed attestation. +func (v *VersionedIndexedAttestation) Signature() (phase0.BLSSignature, error) { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return phase0.BLSSignature{}, errors.New("no Phase0 indexed attestation") + } + + return v.Phase0.Signature, nil + case DataVersionAltair: + if v.Altair == nil { + return phase0.BLSSignature{}, errors.New("no Altair indexed attestation") + } + + return v.Altair.Signature, nil + case DataVersionBellatrix: + if v.Bellatrix == nil { + return phase0.BLSSignature{}, errors.New("no Bellatrix indexed attestation") + } + + return v.Bellatrix.Signature, nil + case DataVersionCapella: + if v.Capella == nil { + return phase0.BLSSignature{}, errors.New("no Capella indexed attestation") + } + + return v.Capella.Signature, nil + case DataVersionDeneb: + if v.Deneb == nil { + return phase0.BLSSignature{}, errors.New("no Deneb indexed attestation") + } + + return v.Deneb.Signature, nil + case DataVersionElectra: + if v.Electra == nil { + return phase0.BLSSignature{}, errors.New("no Electra indexed attestation") + } + + return v.Electra.Signature, nil + default: + return phase0.BLSSignature{}, errors.New("unknown version") + } +} + +// String returns a string version of the structure. +func (v *VersionedIndexedAttestation) String() string { + switch v.Version { + case DataVersionPhase0: + if v.Phase0 == nil { + return "" + } + + return v.Phase0.String() + case DataVersionAltair: + if v.Altair == nil { + return "" + } + + return v.Altair.String() + case DataVersionBellatrix: + if v.Bellatrix == nil { + return "" + } + + return v.Bellatrix.String() + case DataVersionCapella: + if v.Capella == nil { + return "" + } + + return v.Capella.String() + case DataVersionDeneb: + if v.Deneb == nil { + return "" + } + + return v.Deneb.String() + case DataVersionElectra: + if v.Electra == nil { + return "" + } + + return v.Electra.String() + default: + return "unknown version" + } +} diff --git a/spec/versionedsignedbeaconblock.go b/spec/versionedsignedbeaconblock.go index b5ff055e..3e4afb97 100644 --- a/spec/versionedsignedbeaconblock.go +++ b/spec/versionedsignedbeaconblock.go @@ -715,44 +715,92 @@ func (v *VersionedSignedBeaconBlock) VoluntaryExits() ([]*phase0.SignedVoluntary } // AttesterSlashings returns the attester slashings of the beacon block. -func (v *VersionedSignedBeaconBlock) AttesterSlashings() ([]*phase0.AttesterSlashing, error) { +func (v *VersionedSignedBeaconBlock) AttesterSlashings() ([]VersionedAttesterSlashing, error) { switch v.Version { case DataVersionPhase0: if v.Phase0 == nil || v.Phase0.Message == nil || v.Phase0.Message.Body == nil { return nil, errors.New("no phase0 block") } - return v.Phase0.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Phase0.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Phase0.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionPhase0, + Phase0: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionAltair: if v.Altair == nil || v.Altair.Message == nil || v.Altair.Message.Body == nil { return nil, errors.New("no altair block") } - return v.Altair.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Altair.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Altair.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionAltair, + Altair: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionBellatrix: if v.Bellatrix == nil || v.Bellatrix.Message == nil || v.Bellatrix.Message.Body == nil { return nil, errors.New("no bellatrix block") } - return v.Bellatrix.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Bellatrix.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Bellatrix.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionBellatrix, + Bellatrix: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionCapella: if v.Capella == nil || v.Capella.Message == nil || v.Capella.Message.Body == nil { return nil, errors.New("no capella block") } - return v.Capella.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Capella.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Capella.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionCapella, + Capella: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionDeneb: if v.Deneb == nil || v.Deneb.Message == nil || v.Deneb.Message.Body == nil { return nil, errors.New("no deneb block") } - return v.Deneb.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Deneb.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Deneb.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionDeneb, + Deneb: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil case DataVersionElectra: if v.Electra == nil || v.Electra.Message == nil || v.Electra.Message.Body == nil { return nil, errors.New("no electra block") } - return v.Electra.Message.Body.AttesterSlashings, nil + versionedAttesterSlashings := make([]VersionedAttesterSlashing, len(v.Electra.Message.Body.AttesterSlashings)) + for i, attesterSlashing := range v.Electra.Message.Body.AttesterSlashings { + versionedAttesterSlashings[i] = VersionedAttesterSlashing{ + Version: DataVersionElectra, + Electra: attesterSlashing, + } + } + + return versionedAttesterSlashings, nil default: return nil, errors.New("unknown version") }