EIP-7549: attestation pool (#14121)

* implementation

* test fixes

* Electra tests

* remove aggregator tests

* id comments and tests

* make Id equal to [33]byte
This commit is contained in:
Radosław Kapka
2024-06-25 15:18:07 +02:00
committed by GitHub
parent 5f0d6074d6
commit b8aad84285
20 changed files with 452 additions and 138 deletions

View File

@@ -2,10 +2,14 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["attestation_utils.go"],
srcs = [
"attestation_utils.go",
"id.go",
],
importpath = "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/attestation",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -20,7 +24,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["attestation_utils_test.go"],
srcs = [
"attestation_utils_test.go",
"id_test.go",
],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
@@ -29,6 +36,7 @@ go_test(
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)

View File

@@ -12,6 +12,7 @@ go_library(
"//crypto/bls:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation/aggregation:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",

View File

@@ -8,6 +8,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/attestation/aggregation"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// MaxCoverAttestationAggregation relies on Maximum Coverage greedy algorithm for aggregation.
@@ -171,11 +172,21 @@ func aggregateAttestations(atts []ethpb.Att, keys []int, coverage *bitfield.Bitl
}
}
// Put aggregated attestation at a position of the first selected attestation.
atts[targetIdx] = &ethpb.Attestation{
// Append size byte, which will be unnecessary on switch to Bitlist64.
AggregationBits: coverage.ToBitlist(),
Data: data,
Signature: aggregateSignatures(signs).Marshal(),
if atts[0].Version() == version.Phase0 {
atts[targetIdx] = &ethpb.Attestation{
// Append size byte, which will be unnecessary on switch to Bitlist64.
AggregationBits: coverage.ToBitlist(),
Data: data,
Signature: aggregateSignatures(signs).Marshal(),
}
} else {
atts[targetIdx] = &ethpb.AttestationElectra{
// Append size byte, which will be unnecessary on switch to Bitlist64.
AggregationBits: coverage.ToBitlist(),
CommitteeBits: atts[0].CommitteeBitsVal().Bytes(),
Data: data,
Signature: aggregateSignatures(signs).Marshal(),
}
}
return
}

View File

@@ -0,0 +1,71 @@
package attestation
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// IdSource represents the part of attestation that will be used to generate the Id.
type IdSource uint8
const (
// Full generates the Id from the whole attestation.
Full IdSource = iota
// Data generates the Id from the tuple (slot, committee index, beacon block root, source, target).
Data
)
// Id represents an attestation ID. Its uniqueness depends on the IdSource provided when constructing the Id.
type Id [33]byte
// NewId --
func NewId(att ethpb.Att, source IdSource) (Id, error) {
if err := helpers.ValidateNilAttestation(att); err != nil {
return Id{}, err
}
if att.Version() < 0 || att.Version() > 255 {
return Id{}, errors.New("attestation version must be between 0 and 255")
}
var id Id
id[0] = byte(att.Version())
switch source {
case Full:
h, err := att.HashTreeRoot()
if err != nil {
return Id{}, err
}
copy(id[1:], h[:])
return id, nil
case Data:
data := att.GetData()
if att.Version() >= version.Electra {
committeeIndices := att.CommitteeBitsVal().BitIndices()
if len(committeeIndices) != 1 {
return Id{}, fmt.Errorf("%d committee bits are set instead of 1", len(committeeIndices))
}
dataCopy := ethpb.CopyAttestationData(att.GetData())
dataCopy.CommitteeIndex = primitives.CommitteeIndex(committeeIndices[0])
data = dataCopy
}
h, err := data.HashTreeRoot()
if err != nil {
return Id{}, err
}
copy(id[1:], h[:])
return id, nil
default:
return Id{}, errors.New("invalid source requested")
}
}
// String --
func (id Id) String() string {
return string(id[:])
}

View File

@@ -0,0 +1,63 @@
package attestation_test
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/attestation"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestNewId(t *testing.T) {
t.Run("full source", func(t *testing.T) {
att := util.HydrateAttestation(&ethpb.Attestation{})
_, err := attestation.NewId(att, attestation.Full)
assert.NoError(t, err)
})
t.Run("data source Phase 0", func(t *testing.T) {
att := util.HydrateAttestation(&ethpb.Attestation{})
_, err := attestation.NewId(att, attestation.Data)
assert.NoError(t, err)
})
t.Run("data source Electra", func(t *testing.T) {
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true)
att := util.HydrateAttestationElectra(&ethpb.AttestationElectra{CommitteeBits: cb})
_, err := attestation.NewId(att, attestation.Data)
assert.NoError(t, err)
})
t.Run("ID is different between versions", func(t *testing.T) {
phase0Att := util.HydrateAttestation(&ethpb.Attestation{})
phase0Id, err := attestation.NewId(phase0Att, attestation.Data)
require.NoError(t, err)
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true) // setting committee bit 0 for Electra corresponds to attestation data's committee index 0 for Phase 0
electraAtt := util.HydrateAttestationElectra(&ethpb.AttestationElectra{CommitteeBits: cb})
electraId, err := attestation.NewId(electraAtt, attestation.Data)
require.NoError(t, err)
assert.NotEqual(t, phase0Id, electraId)
})
t.Run("invalid source", func(t *testing.T) {
att := util.HydrateAttestation(&ethpb.Attestation{})
_, err := attestation.NewId(att, 123)
assert.ErrorContains(t, "invalid source requested", err)
})
t.Run("data source Electra - 0 bits set", func(t *testing.T) {
cb := primitives.NewAttestationCommitteeBits()
att := util.HydrateAttestationElectra(&ethpb.AttestationElectra{CommitteeBits: cb})
_, err := attestation.NewId(att, attestation.Data)
assert.ErrorContains(t, "0 committee bits are set", err)
})
t.Run("data source Electra - multiple bits set", func(t *testing.T) {
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true)
cb.SetBitAt(1, true)
att := util.HydrateAttestationElectra(&ethpb.AttestationElectra{CommitteeBits: cb})
_, err := attestation.NewId(att, attestation.Data)
assert.ErrorContains(t, "2 committee bits are set", err)
})
}