From 7877caed3b7eaf475342025dee08b3b4a39493e4 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2026 12:54:24 +0200 Subject: [PATCH 1/4] add Gloas aggregate conversion Dispatch Gloas aggregate-and-proof values through the existing versioned containers and reject missing selected arms.\n\nFiles: attestation aggregator conversion and focused coverage.\n\nLive Dirk-backed aggregate verification remains pending an available Gloas environment. --- .../attestationaggregator/standard/service.go | 28 +++++ .../standard/service_internal_test.go | 114 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 services/attestationaggregator/standard/service_internal_test.go diff --git a/services/attestationaggregator/standard/service.go b/services/attestationaggregator/standard/service.go index beeef0c7..909362ba 100644 --- a/services/attestationaggregator/standard/service.go +++ b/services/attestationaggregator/standard/service.go @@ -24,6 +24,7 @@ import ( "github.com/attestantio/go-eth2-client/api" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/electra" + "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/attestantio/vouch/services/accountmanager" "github.com/attestantio/vouch/services/attestationaggregator" @@ -308,6 +309,20 @@ func createVersionedAggregateAndProof(duty *attestationaggregator.Duty, aggregat Fulu: aggregateAndProof, } return versionedAggregateAndProof, nil + case spec.DataVersionGloas: + if aggregateAttestation.Gloas == nil { + return nil, errors.New("no gloas attestation") + } + aggregateAndProof := &gloas.AggregateAndProof{ + AggregatorIndex: duty.ValidatorIndex, + Aggregate: aggregateAttestation.Gloas, + SelectionProof: duty.SlotSignature, + } + versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ + Version: aggregateAttestation.Version, + Gloas: aggregateAndProof, + } + return versionedAggregateAndProof, nil default: return &spec.VersionedAggregateAndProof{}, errors.New("unknown version") } @@ -406,6 +421,19 @@ func createVersionedSignedAggregateAndProof(aggregateAndProof *spec.VersionedAgg Fulu: signedAggregateAndProof, } return signedVersionedAggregateAndProof, nil + case spec.DataVersionGloas: + if aggregateAndProof.Gloas == nil { + return nil, errors.New("no gloas aggregate and proof") + } + signedAggregateAndProof := &gloas.SignedAggregateAndProof{ + Message: aggregateAndProof.Gloas, + Signature: sig, + } + signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ + Version: aggregateAndProof.Version, + Gloas: signedAggregateAndProof, + } + return signedVersionedAggregateAndProof, nil default: return &spec.VersionedSignedAggregateAndProof{}, errors.New("unknown version") } diff --git a/services/attestationaggregator/standard/service_internal_test.go b/services/attestationaggregator/standard/service_internal_test.go new file mode 100644 index 00000000..0da1dac1 --- /dev/null +++ b/services/attestationaggregator/standard/service_internal_test.go @@ -0,0 +1,114 @@ +// Copyright © 2026 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 standard + +import ( + "testing" + + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/gloas" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/attestantio/vouch/services/attestationaggregator" + "github.com/stretchr/testify/require" +) + +func TestCreateVersionedAggregateAndProofGloas(t *testing.T) { + duty := &attestationaggregator.Duty{ + ValidatorIndex: phase0.ValidatorIndex(42), + SlotSignature: phase0.BLSSignature{1}, + } + attestation := &gloas.Attestation{} + + tests := []struct { + name string + versionedAttestation *spec.VersionedAttestation + err string + }{ + { + name: "Valid", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionGloas, + Gloas: attestation, + }, + }, + { + name: "MissingArm", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionGloas, + }, + err: "no gloas attestation", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := createVersionedAggregateAndProof(duty, test.versionedAttestation) + if test.err != "" { + require.Nil(t, result) + require.EqualError(t, err, test.err) + return + } + + require.NoError(t, err) + require.Equal(t, spec.DataVersionGloas, result.Version) + require.NotNil(t, result.Gloas) + require.Same(t, attestation, result.Gloas.Aggregate) + require.Equal(t, duty.ValidatorIndex, result.Gloas.AggregatorIndex) + require.Equal(t, duty.SlotSignature, result.Gloas.SelectionProof) + }) + } +} + +func TestCreateVersionedSignedAggregateAndProofGloas(t *testing.T) { + aggregateAndProof := &gloas.AggregateAndProof{} + sig := phase0.BLSSignature{2} + + tests := []struct { + name string + versionedAggregateAndProof *spec.VersionedAggregateAndProof + err string + }{ + { + name: "Valid", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionGloas, + Gloas: aggregateAndProof, + }, + }, + { + name: "MissingArm", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionGloas, + }, + err: "no gloas aggregate and proof", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := createVersionedSignedAggregateAndProof(test.versionedAggregateAndProof, sig) + if test.err != "" { + require.Nil(t, result) + require.EqualError(t, err, test.err) + return + } + + require.NoError(t, err) + require.Equal(t, spec.DataVersionGloas, result.Version) + require.NotNil(t, result.Gloas) + require.Same(t, aggregateAndProof, result.Gloas.Message) + require.Equal(t, sig, result.Gloas.Signature) + }) + } +} From 929ff871aecfd44485f1c4c1a99a59ad5c533059 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2026 16:17:28 +0200 Subject: [PATCH 2/4] simplify aggregate version conversion --- .../attestationaggregator/standard/service.go | 329 ++++++++---------- .../standard/service_internal_test.go | 184 +++++++++- 2 files changed, 304 insertions(+), 209 deletions(-) diff --git a/services/attestationaggregator/standard/service.go b/services/attestationaggregator/standard/service.go index 909362ba..c9202d64 100644 --- a/services/attestationaggregator/standard/service.go +++ b/services/attestationaggregator/standard/service.go @@ -212,117 +212,31 @@ func (s *Service) Aggregate(ctx context.Context, duty *attestationaggregator.Dut func createVersionedAggregateAndProof(duty *attestationaggregator.Duty, aggregateAttestation *spec.VersionedAttestation) (*spec.VersionedAggregateAndProof, error) { switch aggregateAttestation.Version { case spec.DataVersionPhase0: - if aggregateAttestation.Phase0 == nil { - return nil, errors.New("no phase0 attestation") - } - aggregateAndProof := &phase0.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Phase0, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Phase0: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Phase0, "phase0") case spec.DataVersionAltair: - if aggregateAttestation.Altair == nil { - return nil, errors.New("no altair attestation") - } - aggregateAndProof := &phase0.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Altair, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Altair: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Altair, "altair") case spec.DataVersionBellatrix: - if aggregateAttestation.Bellatrix == nil { - return nil, errors.New("no bellatrix attestation") - } - aggregateAndProof := &phase0.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Bellatrix, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Bellatrix: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Bellatrix, "bellatrix") case spec.DataVersionCapella: - if aggregateAttestation.Capella == nil { - return nil, errors.New("no capella attestation") - } - aggregateAndProof := &phase0.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Capella, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Capella: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Capella, "capella") case spec.DataVersionDeneb: - if aggregateAttestation.Deneb == nil { - return nil, errors.New("no deneb attestation") - } - aggregateAndProof := &phase0.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Deneb, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Deneb: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Deneb, "deneb") case spec.DataVersionElectra: - if aggregateAttestation.Electra == nil { - return nil, errors.New("no electra attestation") - } - aggregateAndProof := &electra.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Electra, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Electra: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedElectraAggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Electra, "electra") case spec.DataVersionFulu: - if aggregateAttestation.Fulu == nil { - return nil, errors.New("no fulu attestation") - } - aggregateAndProof := &electra.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Fulu, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ - Version: aggregateAttestation.Version, - Fulu: aggregateAndProof, - } - return versionedAggregateAndProof, nil + return createVersionedElectraAggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Fulu, "fulu") case spec.DataVersionGloas: if aggregateAttestation.Gloas == nil { return nil, errors.New("no gloas attestation") } - aggregateAndProof := &gloas.AggregateAndProof{ - AggregatorIndex: duty.ValidatorIndex, - Aggregate: aggregateAttestation.Gloas, - SelectionProof: duty.SlotSignature, - } - versionedAggregateAndProof := &spec.VersionedAggregateAndProof{ + return &spec.VersionedAggregateAndProof{ Version: aggregateAttestation.Version, - Gloas: aggregateAndProof, - } - return versionedAggregateAndProof, nil + Gloas: &gloas.AggregateAndProof{ + AggregatorIndex: duty.ValidatorIndex, + Aggregate: aggregateAttestation.Gloas, + SelectionProof: duty.SlotSignature, + }, + }, nil default: return &spec.VersionedAggregateAndProof{}, errors.New("unknown version") } @@ -331,114 +245,145 @@ func createVersionedAggregateAndProof(duty *attestationaggregator.Duty, aggregat func createVersionedSignedAggregateAndProof(aggregateAndProof *spec.VersionedAggregateAndProof, sig phase0.BLSSignature) (*spec.VersionedSignedAggregateAndProof, error) { switch aggregateAndProof.Version { case spec.DataVersionPhase0: - if aggregateAndProof.Phase0 == nil { - return nil, errors.New("no phase0 aggregate and proof") - } - signedAggregateAndProof := &phase0.SignedAggregateAndProof{ - Message: aggregateAndProof.Phase0, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Phase0: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Phase0, sig, "phase0") case spec.DataVersionAltair: - if aggregateAndProof.Altair == nil { - return nil, errors.New("no altair aggregate and proof") - } - signedAggregateAndProof := &phase0.SignedAggregateAndProof{ - Message: aggregateAndProof.Altair, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Altair: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Altair, sig, "altair") case spec.DataVersionBellatrix: - if aggregateAndProof.Bellatrix == nil { - return nil, errors.New("no bellatrix aggregate and proof") - } - signedAggregateAndProof := &phase0.SignedAggregateAndProof{ - Message: aggregateAndProof.Bellatrix, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Bellatrix: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Bellatrix, sig, "bellatrix") case spec.DataVersionCapella: - if aggregateAndProof.Capella == nil { - return nil, errors.New("no capella aggregate and proof") - } - signedAggregateAndProof := &phase0.SignedAggregateAndProof{ - Message: aggregateAndProof.Capella, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Capella: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Capella, sig, "capella") case spec.DataVersionDeneb: - if aggregateAndProof.Deneb == nil { - return nil, errors.New("no deneb aggregate and proof") - } - signedAggregateAndProof := &phase0.SignedAggregateAndProof{ - Message: aggregateAndProof.Deneb, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Deneb: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Deneb, sig, "deneb") case spec.DataVersionElectra: - if aggregateAndProof.Electra == nil { - return nil, errors.New("no electra aggregate and proof") - } - signedAggregateAndProof := &electra.SignedAggregateAndProof{ - Message: aggregateAndProof.Electra, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Electra: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedElectraSignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Electra, sig, "electra") case spec.DataVersionFulu: - if aggregateAndProof.Fulu == nil { - return nil, errors.New("no fulu aggregate and proof") - } - signedAggregateAndProof := &electra.SignedAggregateAndProof{ - Message: aggregateAndProof.Fulu, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ - Version: aggregateAndProof.Version, - Fulu: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + return createVersionedElectraSignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Fulu, sig, "fulu") case spec.DataVersionGloas: if aggregateAndProof.Gloas == nil { return nil, errors.New("no gloas aggregate and proof") } - signedAggregateAndProof := &gloas.SignedAggregateAndProof{ - Message: aggregateAndProof.Gloas, - Signature: sig, - } - signedVersionedAggregateAndProof := &spec.VersionedSignedAggregateAndProof{ + return &spec.VersionedSignedAggregateAndProof{ Version: aggregateAndProof.Version, - Gloas: signedAggregateAndProof, - } - return signedVersionedAggregateAndProof, nil + Gloas: &gloas.SignedAggregateAndProof{ + Message: aggregateAndProof.Gloas, + Signature: sig, + }, + }, nil default: return &spec.VersionedSignedAggregateAndProof{}, errors.New("unknown version") } } +func createVersionedPhase0AggregateAndProof(duty *attestationaggregator.Duty, + version spec.DataVersion, + aggregateAttestation *phase0.Attestation, + name string, +) (*spec.VersionedAggregateAndProof, error) { + if aggregateAttestation == nil { + return nil, errors.New("no " + name + " attestation") + } + aggregateAndProof := &phase0.AggregateAndProof{ + AggregatorIndex: duty.ValidatorIndex, + Aggregate: aggregateAttestation, + SelectionProof: duty.SlotSignature, + } + result := &spec.VersionedAggregateAndProof{Version: version} + switch version { + case spec.DataVersionPhase0: + result.Phase0 = aggregateAndProof + case spec.DataVersionAltair: + result.Altair = aggregateAndProof + case spec.DataVersionBellatrix: + result.Bellatrix = aggregateAndProof + case spec.DataVersionCapella: + result.Capella = aggregateAndProof + case spec.DataVersionDeneb: + result.Deneb = aggregateAndProof + default: + return nil, errors.New("unknown version") + } + return result, nil +} + +func createVersionedElectraAggregateAndProof(duty *attestationaggregator.Duty, + version spec.DataVersion, + aggregateAttestation *electra.Attestation, + name string, +) (*spec.VersionedAggregateAndProof, error) { + if aggregateAttestation == nil { + return nil, errors.New("no " + name + " attestation") + } + aggregateAndProof := &electra.AggregateAndProof{ + AggregatorIndex: duty.ValidatorIndex, + Aggregate: aggregateAttestation, + SelectionProof: duty.SlotSignature, + } + result := &spec.VersionedAggregateAndProof{Version: version} + switch version { + case spec.DataVersionElectra: + result.Electra = aggregateAndProof + case spec.DataVersionFulu: + result.Fulu = aggregateAndProof + default: + return nil, errors.New("unknown version") + } + return result, nil +} + +func createVersionedPhase0SignedAggregateAndProof(version spec.DataVersion, + aggregateAndProof *phase0.AggregateAndProof, + sig phase0.BLSSignature, + name string, +) (*spec.VersionedSignedAggregateAndProof, error) { + if aggregateAndProof == nil { + return nil, errors.New("no " + name + " aggregate and proof") + } + signedAggregateAndProof := &phase0.SignedAggregateAndProof{ + Message: aggregateAndProof, + Signature: sig, + } + result := &spec.VersionedSignedAggregateAndProof{Version: version} + switch version { + case spec.DataVersionPhase0: + result.Phase0 = signedAggregateAndProof + case spec.DataVersionAltair: + result.Altair = signedAggregateAndProof + case spec.DataVersionBellatrix: + result.Bellatrix = signedAggregateAndProof + case spec.DataVersionCapella: + result.Capella = signedAggregateAndProof + case spec.DataVersionDeneb: + result.Deneb = signedAggregateAndProof + default: + return nil, errors.New("unknown version") + } + return result, nil +} + +func createVersionedElectraSignedAggregateAndProof(version spec.DataVersion, + aggregateAndProof *electra.AggregateAndProof, + sig phase0.BLSSignature, + name string, +) (*spec.VersionedSignedAggregateAndProof, error) { + if aggregateAndProof == nil { + return nil, errors.New("no " + name + " aggregate and proof") + } + signedAggregateAndProof := &electra.SignedAggregateAndProof{ + Message: aggregateAndProof, + Signature: sig, + } + result := &spec.VersionedSignedAggregateAndProof{Version: version} + switch version { + case spec.DataVersionElectra: + result.Electra = signedAggregateAndProof + case spec.DataVersionFulu: + result.Fulu = signedAggregateAndProof + default: + return nil, errors.New("unknown version") + } + return result, nil +} + // AggregatorsAndSignatures reports signatures and whether validators are attestation aggregators for a given slot. func (s *Service) AggregatorsAndSignatures(ctx context.Context, accounts []e2wtypes.Account, diff --git a/services/attestationaggregator/standard/service_internal_test.go b/services/attestationaggregator/standard/service_internal_test.go index 0da1dac1..a786b58e 100644 --- a/services/attestationaggregator/standard/service_internal_test.go +++ b/services/attestationaggregator/standard/service_internal_test.go @@ -17,18 +17,18 @@ import ( "testing" "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/electra" "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/attestantio/vouch/services/attestationaggregator" "github.com/stretchr/testify/require" ) -func TestCreateVersionedAggregateAndProofGloas(t *testing.T) { +func TestCreateVersionedAggregateAndProof(t *testing.T) { duty := &attestationaggregator.Duty{ ValidatorIndex: phase0.ValidatorIndex(42), SlotSignature: phase0.BLSSignature{1}, } - attestation := &gloas.Attestation{} tests := []struct { name string @@ -36,10 +36,59 @@ func TestCreateVersionedAggregateAndProofGloas(t *testing.T) { err string }{ { - name: "Valid", + name: "Phase0", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionPhase0, + Phase0: &phase0.Attestation{}, + }, + }, + { + name: "Altair", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionAltair, + Altair: &phase0.Attestation{}, + }, + }, + { + name: "Bellatrix", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionBellatrix, + Bellatrix: &phase0.Attestation{}, + }, + }, + { + name: "Capella", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionCapella, + Capella: &phase0.Attestation{}, + }, + }, + { + name: "Deneb", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionDeneb, + Deneb: &phase0.Attestation{}, + }, + }, + { + name: "Electra", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionElectra, + Electra: &electra.Attestation{}, + }, + }, + { + name: "Fulu", + versionedAttestation: &spec.VersionedAttestation{ + Version: spec.DataVersionFulu, + Fulu: &electra.Attestation{}, + }, + }, + { + name: "Gloas", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionGloas, - Gloas: attestation, + Gloas: &gloas.Attestation{}, }, }, { @@ -61,17 +110,18 @@ func TestCreateVersionedAggregateAndProofGloas(t *testing.T) { } require.NoError(t, err) - require.Equal(t, spec.DataVersionGloas, result.Version) - require.NotNil(t, result.Gloas) - require.Same(t, attestation, result.Gloas.Aggregate) - require.Equal(t, duty.ValidatorIndex, result.Gloas.AggregatorIndex) - require.Equal(t, duty.SlotSignature, result.Gloas.SelectionProof) + require.Equal(t, test.versionedAttestation.Version, result.Version) + aggregatorIndex, err := result.AggregatorIndex() + require.NoError(t, err) + require.Equal(t, duty.ValidatorIndex, aggregatorIndex) + selectionProof, err := result.SelectionProof() + require.NoError(t, err) + require.Equal(t, duty.SlotSignature, selectionProof) }) } } -func TestCreateVersionedSignedAggregateAndProofGloas(t *testing.T) { - aggregateAndProof := &gloas.AggregateAndProof{} +func TestCreateVersionedSignedAggregateAndProof(t *testing.T) { sig := phase0.BLSSignature{2} tests := []struct { @@ -80,10 +130,59 @@ func TestCreateVersionedSignedAggregateAndProofGloas(t *testing.T) { err string }{ { - name: "Valid", + name: "Phase0", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionPhase0, + Phase0: &phase0.AggregateAndProof{}, + }, + }, + { + name: "Altair", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionAltair, + Altair: &phase0.AggregateAndProof{}, + }, + }, + { + name: "Bellatrix", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionBellatrix, + Bellatrix: &phase0.AggregateAndProof{}, + }, + }, + { + name: "Capella", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionCapella, + Capella: &phase0.AggregateAndProof{}, + }, + }, + { + name: "Deneb", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionDeneb, + Deneb: &phase0.AggregateAndProof{}, + }, + }, + { + name: "Electra", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionElectra, + Electra: &electra.AggregateAndProof{}, + }, + }, + { + name: "Fulu", + versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ + Version: spec.DataVersionFulu, + Fulu: &electra.AggregateAndProof{}, + }, + }, + { + name: "Gloas", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionGloas, - Gloas: aggregateAndProof, + Gloas: &gloas.AggregateAndProof{}, }, }, { @@ -105,10 +204,61 @@ func TestCreateVersionedSignedAggregateAndProofGloas(t *testing.T) { } require.NoError(t, err) - require.Equal(t, spec.DataVersionGloas, result.Version) - require.NotNil(t, result.Gloas) - require.Same(t, aggregateAndProof, result.Gloas.Message) - require.Equal(t, sig, result.Gloas.Signature) + require.Equal(t, test.versionedAggregateAndProof.Version, result.Version) + signature, err := result.Signature() + require.NoError(t, err) + require.Equal(t, sig, signature) + }) + } +} + +func TestCreateVersionedAggregateAndProofRejectsMissingArms(t *testing.T) { + duty := &attestationaggregator.Duty{} + tests := []struct { + name string + version spec.DataVersion + err string + }{ + {name: "Phase0", version: spec.DataVersionPhase0, err: "no phase0 attestation"}, + {name: "Altair", version: spec.DataVersionAltair, err: "no altair attestation"}, + {name: "Bellatrix", version: spec.DataVersionBellatrix, err: "no bellatrix attestation"}, + {name: "Capella", version: spec.DataVersionCapella, err: "no capella attestation"}, + {name: "Deneb", version: spec.DataVersionDeneb, err: "no deneb attestation"}, + {name: "Electra", version: spec.DataVersionElectra, err: "no electra attestation"}, + {name: "Fulu", version: spec.DataVersionFulu, err: "no fulu attestation"}, + {name: "Gloas", version: spec.DataVersionGloas, err: "no gloas attestation"}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := createVersionedAggregateAndProof(duty, &spec.VersionedAttestation{Version: test.version}) + require.Nil(t, result) + require.EqualError(t, err, test.err) + }) + } +} + +func TestCreateVersionedSignedAggregateAndProofRejectsMissingArms(t *testing.T) { + tests := []struct { + name string + version spec.DataVersion + err string + }{ + {name: "Phase0", version: spec.DataVersionPhase0, err: "no phase0 aggregate and proof"}, + {name: "Altair", version: spec.DataVersionAltair, err: "no altair aggregate and proof"}, + {name: "Bellatrix", version: spec.DataVersionBellatrix, err: "no bellatrix aggregate and proof"}, + {name: "Capella", version: spec.DataVersionCapella, err: "no capella aggregate and proof"}, + {name: "Deneb", version: spec.DataVersionDeneb, err: "no deneb aggregate and proof"}, + {name: "Electra", version: spec.DataVersionElectra, err: "no electra aggregate and proof"}, + {name: "Fulu", version: spec.DataVersionFulu, err: "no fulu aggregate and proof"}, + {name: "Gloas", version: spec.DataVersionGloas, err: "no gloas aggregate and proof"}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := createVersionedSignedAggregateAndProof(&spec.VersionedAggregateAndProof{Version: test.version}, phase0.BLSSignature{}) + require.Nil(t, result) + require.EqualError(t, err, test.err) }) } } From 9796922014435cf0ef3dc598f12b9ee45a10e6c5 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2026 17:08:45 +0200 Subject: [PATCH 3/4] prove aggregate conversion wiring in tests The version conversion tests asserted only the scalar accessors, which are satisfied by a container whose Aggregate or Message is nil. Such a container panics in submitAggregateAttestations, which reads Slot() through Message.Aggregate.Data with no nil guard. Assert the aggregate through the arm each version names, and assert Slot() on the signed path. Also drop the redundant name parameter from the conversion helpers in favour of version.String(), and return nil rather than an empty container alongside the error from the outer default arms. --- .../attestationaggregator/standard/service.go | 44 +++---- .../standard/service_internal_test.go | 123 +++++++++++------- 2 files changed, 99 insertions(+), 68 deletions(-) diff --git a/services/attestationaggregator/standard/service.go b/services/attestationaggregator/standard/service.go index c9202d64..623c0e25 100644 --- a/services/attestationaggregator/standard/service.go +++ b/services/attestationaggregator/standard/service.go @@ -212,19 +212,19 @@ func (s *Service) Aggregate(ctx context.Context, duty *attestationaggregator.Dut func createVersionedAggregateAndProof(duty *attestationaggregator.Duty, aggregateAttestation *spec.VersionedAttestation) (*spec.VersionedAggregateAndProof, error) { switch aggregateAttestation.Version { case spec.DataVersionPhase0: - return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Phase0, "phase0") + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Phase0) case spec.DataVersionAltair: - return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Altair, "altair") + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Altair) case spec.DataVersionBellatrix: - return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Bellatrix, "bellatrix") + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Bellatrix) case spec.DataVersionCapella: - return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Capella, "capella") + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Capella) case spec.DataVersionDeneb: - return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Deneb, "deneb") + return createVersionedPhase0AggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Deneb) case spec.DataVersionElectra: - return createVersionedElectraAggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Electra, "electra") + return createVersionedElectraAggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Electra) case spec.DataVersionFulu: - return createVersionedElectraAggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Fulu, "fulu") + return createVersionedElectraAggregateAndProof(duty, aggregateAttestation.Version, aggregateAttestation.Fulu) case spec.DataVersionGloas: if aggregateAttestation.Gloas == nil { return nil, errors.New("no gloas attestation") @@ -238,26 +238,26 @@ func createVersionedAggregateAndProof(duty *attestationaggregator.Duty, aggregat }, }, nil default: - return &spec.VersionedAggregateAndProof{}, errors.New("unknown version") + return nil, errors.New("unknown version") } } func createVersionedSignedAggregateAndProof(aggregateAndProof *spec.VersionedAggregateAndProof, sig phase0.BLSSignature) (*spec.VersionedSignedAggregateAndProof, error) { switch aggregateAndProof.Version { case spec.DataVersionPhase0: - return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Phase0, sig, "phase0") + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Phase0, sig) case spec.DataVersionAltair: - return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Altair, sig, "altair") + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Altair, sig) case spec.DataVersionBellatrix: - return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Bellatrix, sig, "bellatrix") + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Bellatrix, sig) case spec.DataVersionCapella: - return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Capella, sig, "capella") + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Capella, sig) case spec.DataVersionDeneb: - return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Deneb, sig, "deneb") + return createVersionedPhase0SignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Deneb, sig) case spec.DataVersionElectra: - return createVersionedElectraSignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Electra, sig, "electra") + return createVersionedElectraSignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Electra, sig) case spec.DataVersionFulu: - return createVersionedElectraSignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Fulu, sig, "fulu") + return createVersionedElectraSignedAggregateAndProof(aggregateAndProof.Version, aggregateAndProof.Fulu, sig) case spec.DataVersionGloas: if aggregateAndProof.Gloas == nil { return nil, errors.New("no gloas aggregate and proof") @@ -270,17 +270,16 @@ func createVersionedSignedAggregateAndProof(aggregateAndProof *spec.VersionedAgg }, }, nil default: - return &spec.VersionedSignedAggregateAndProof{}, errors.New("unknown version") + return nil, errors.New("unknown version") } } func createVersionedPhase0AggregateAndProof(duty *attestationaggregator.Duty, version spec.DataVersion, aggregateAttestation *phase0.Attestation, - name string, ) (*spec.VersionedAggregateAndProof, error) { if aggregateAttestation == nil { - return nil, errors.New("no " + name + " attestation") + return nil, errors.New("no " + version.String() + " attestation") } aggregateAndProof := &phase0.AggregateAndProof{ AggregatorIndex: duty.ValidatorIndex, @@ -308,10 +307,9 @@ func createVersionedPhase0AggregateAndProof(duty *attestationaggregator.Duty, func createVersionedElectraAggregateAndProof(duty *attestationaggregator.Duty, version spec.DataVersion, aggregateAttestation *electra.Attestation, - name string, ) (*spec.VersionedAggregateAndProof, error) { if aggregateAttestation == nil { - return nil, errors.New("no " + name + " attestation") + return nil, errors.New("no " + version.String() + " attestation") } aggregateAndProof := &electra.AggregateAndProof{ AggregatorIndex: duty.ValidatorIndex, @@ -333,10 +331,9 @@ func createVersionedElectraAggregateAndProof(duty *attestationaggregator.Duty, func createVersionedPhase0SignedAggregateAndProof(version spec.DataVersion, aggregateAndProof *phase0.AggregateAndProof, sig phase0.BLSSignature, - name string, ) (*spec.VersionedSignedAggregateAndProof, error) { if aggregateAndProof == nil { - return nil, errors.New("no " + name + " aggregate and proof") + return nil, errors.New("no " + version.String() + " aggregate and proof") } signedAggregateAndProof := &phase0.SignedAggregateAndProof{ Message: aggregateAndProof, @@ -363,10 +360,9 @@ func createVersionedPhase0SignedAggregateAndProof(version spec.DataVersion, func createVersionedElectraSignedAggregateAndProof(version spec.DataVersion, aggregateAndProof *electra.AggregateAndProof, sig phase0.BLSSignature, - name string, ) (*spec.VersionedSignedAggregateAndProof, error) { if aggregateAndProof == nil { - return nil, errors.New("no " + name + " aggregate and proof") + return nil, errors.New("no " + version.String() + " aggregate and proof") } signedAggregateAndProof := &electra.SignedAggregateAndProof{ Message: aggregateAndProof, diff --git a/services/attestationaggregator/standard/service_internal_test.go b/services/attestationaggregator/standard/service_internal_test.go index a786b58e..d851ce8e 100644 --- a/services/attestationaggregator/standard/service_internal_test.go +++ b/services/attestationaggregator/standard/service_internal_test.go @@ -24,91 +24,122 @@ import ( "github.com/stretchr/testify/require" ) +// testAggregateSlot is the slot carried by the attestations the tests supply. Asserting on it +// proves the conversion carried the supplied attestation through rather than leaving it nil. +const testAggregateSlot = phase0.Slot(99) + +// aggregateOf returns the aggregate attestation held by the arm the version names, so a +// conversion that populated the wrong arm - or left the aggregate nil - is visible to the caller. +func aggregateOf(t *testing.T, aggregateAndProof *spec.VersionedAggregateAndProof) any { + t.Helper() + + switch aggregateAndProof.Version { + case spec.DataVersionPhase0: + return aggregateAndProof.Phase0.Aggregate + case spec.DataVersionAltair: + return aggregateAndProof.Altair.Aggregate + case spec.DataVersionBellatrix: + return aggregateAndProof.Bellatrix.Aggregate + case spec.DataVersionCapella: + return aggregateAndProof.Capella.Aggregate + case spec.DataVersionDeneb: + return aggregateAndProof.Deneb.Aggregate + case spec.DataVersionElectra: + return aggregateAndProof.Electra.Aggregate + case spec.DataVersionFulu: + return aggregateAndProof.Fulu.Aggregate + case spec.DataVersionGloas: + return aggregateAndProof.Gloas.Aggregate + default: + require.FailNow(t, "no aggregate for version", aggregateAndProof.Version.String()) + + return nil + } +} + func TestCreateVersionedAggregateAndProof(t *testing.T) { duty := &attestationaggregator.Duty{ ValidatorIndex: phase0.ValidatorIndex(42), SlotSignature: phase0.BLSSignature{1}, } + phase0Attestation := &phase0.Attestation{Data: &phase0.AttestationData{Slot: testAggregateSlot}} + electraAttestation := &electra.Attestation{Data: &phase0.AttestationData{Slot: testAggregateSlot}} + gloasAttestation := &gloas.Attestation{Data: &phase0.AttestationData{Slot: testAggregateSlot}} tests := []struct { name string versionedAttestation *spec.VersionedAttestation - err string + expectedAggregate any }{ { name: "Phase0", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionPhase0, - Phase0: &phase0.Attestation{}, + Phase0: phase0Attestation, }, + expectedAggregate: phase0Attestation, }, { name: "Altair", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionAltair, - Altair: &phase0.Attestation{}, + Altair: phase0Attestation, }, + expectedAggregate: phase0Attestation, }, { name: "Bellatrix", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionBellatrix, - Bellatrix: &phase0.Attestation{}, + Bellatrix: phase0Attestation, }, + expectedAggregate: phase0Attestation, }, { name: "Capella", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionCapella, - Capella: &phase0.Attestation{}, + Capella: phase0Attestation, }, + expectedAggregate: phase0Attestation, }, { name: "Deneb", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionDeneb, - Deneb: &phase0.Attestation{}, + Deneb: phase0Attestation, }, + expectedAggregate: phase0Attestation, }, { name: "Electra", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionElectra, - Electra: &electra.Attestation{}, + Electra: electraAttestation, }, + expectedAggregate: electraAttestation, }, { name: "Fulu", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionFulu, - Fulu: &electra.Attestation{}, + Fulu: electraAttestation, }, + expectedAggregate: electraAttestation, }, { name: "Gloas", versionedAttestation: &spec.VersionedAttestation{ Version: spec.DataVersionGloas, - Gloas: &gloas.Attestation{}, - }, - }, - { - name: "MissingArm", - versionedAttestation: &spec.VersionedAttestation{ - Version: spec.DataVersionGloas, + Gloas: gloasAttestation, }, - err: "no gloas attestation", + expectedAggregate: gloasAttestation, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := createVersionedAggregateAndProof(duty, test.versionedAttestation) - if test.err != "" { - require.Nil(t, result) - require.EqualError(t, err, test.err) - return - } - require.NoError(t, err) require.Equal(t, test.versionedAttestation.Version, result.Version) aggregatorIndex, err := result.AggregatorIndex() @@ -117,97 +148,99 @@ func TestCreateVersionedAggregateAndProof(t *testing.T) { selectionProof, err := result.SelectionProof() require.NoError(t, err) require.Equal(t, duty.SlotSignature, selectionProof) + require.Same(t, test.expectedAggregate, aggregateOf(t, result)) }) } } func TestCreateVersionedSignedAggregateAndProof(t *testing.T) { sig := phase0.BLSSignature{2} + phase0AggregateAndProof := &phase0.AggregateAndProof{ + Aggregate: &phase0.Attestation{Data: &phase0.AttestationData{Slot: testAggregateSlot}}, + } + electraAggregateAndProof := &electra.AggregateAndProof{ + Aggregate: &electra.Attestation{Data: &phase0.AttestationData{Slot: testAggregateSlot}}, + } + gloasAggregateAndProof := &gloas.AggregateAndProof{ + Aggregate: &gloas.Attestation{Data: &phase0.AttestationData{Slot: testAggregateSlot}}, + } tests := []struct { name string versionedAggregateAndProof *spec.VersionedAggregateAndProof - err string }{ { name: "Phase0", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionPhase0, - Phase0: &phase0.AggregateAndProof{}, + Phase0: phase0AggregateAndProof, }, }, { name: "Altair", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionAltair, - Altair: &phase0.AggregateAndProof{}, + Altair: phase0AggregateAndProof, }, }, { name: "Bellatrix", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionBellatrix, - Bellatrix: &phase0.AggregateAndProof{}, + Bellatrix: phase0AggregateAndProof, }, }, { name: "Capella", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionCapella, - Capella: &phase0.AggregateAndProof{}, + Capella: phase0AggregateAndProof, }, }, { name: "Deneb", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionDeneb, - Deneb: &phase0.AggregateAndProof{}, + Deneb: phase0AggregateAndProof, }, }, { name: "Electra", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionElectra, - Electra: &electra.AggregateAndProof{}, + Electra: electraAggregateAndProof, }, }, { name: "Fulu", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionFulu, - Fulu: &electra.AggregateAndProof{}, + Fulu: electraAggregateAndProof, }, }, { name: "Gloas", versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ Version: spec.DataVersionGloas, - Gloas: &gloas.AggregateAndProof{}, + Gloas: gloasAggregateAndProof, }, }, - { - name: "MissingArm", - versionedAggregateAndProof: &spec.VersionedAggregateAndProof{ - Version: spec.DataVersionGloas, - }, - err: "no gloas aggregate and proof", - }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := createVersionedSignedAggregateAndProof(test.versionedAggregateAndProof, sig) - if test.err != "" { - require.Nil(t, result) - require.EqualError(t, err, test.err) - return - } - require.NoError(t, err) require.Equal(t, test.versionedAggregateAndProof.Version, result.Version) signature, err := result.Signature() require.NoError(t, err) require.Equal(t, sig, signature) + // Slot() reads through Message.Aggregate.Data without a nil guard, and the multinode + // submitter calls it on every submission, so this proves the message was carried + // through rather than left nil. + slot, err := result.Slot() + require.NoError(t, err) + require.Equal(t, testAggregateSlot, slot) }) } } @@ -227,6 +260,7 @@ func TestCreateVersionedAggregateAndProofRejectsMissingArms(t *testing.T) { {name: "Electra", version: spec.DataVersionElectra, err: "no electra attestation"}, {name: "Fulu", version: spec.DataVersionFulu, err: "no fulu attestation"}, {name: "Gloas", version: spec.DataVersionGloas, err: "no gloas attestation"}, + {name: "Unknown", version: spec.DataVersionUnknown, err: "unknown version"}, } for _, test := range tests { @@ -252,6 +286,7 @@ func TestCreateVersionedSignedAggregateAndProofRejectsMissingArms(t *testing.T) {name: "Electra", version: spec.DataVersionElectra, err: "no electra aggregate and proof"}, {name: "Fulu", version: spec.DataVersionFulu, err: "no fulu aggregate and proof"}, {name: "Gloas", version: spec.DataVersionGloas, err: "no gloas aggregate and proof"}, + {name: "Unknown", version: spec.DataVersionUnknown, err: "unknown version"}, } for _, test := range tests { From a65e38e0c1f328a8c90295b071972d29da80d76e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 21 Aug 2026 11:17:33 +0200 Subject: [PATCH 4/4] Record the gloas attestation aggregation arms in the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6ecb2b6..7052b822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ gloas: - do not update execution chain head state from gloas blocks, as an execution payload bid carries no execution block number - report "builder" as a beacon block proposal source method - preserve the beacon node's gloas payload vote in both the attestation signing root and the submitted attestation, rejecting an invalid vote before signing + - convert gloas aggregate attestations and signed aggregate-and-proof containers for aggregation and submission, rejecting a missing gloas arm before signing - update go-eth2-client to a gloas pseudo-version - satisfy the attgo struct field order and comment capitalisation rules across services and strategies