Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: fix inclusion aggregation bits for Electra #3501

Merged
merged 5 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/eth2wrap/eth2wrap_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/eth2wrap/genwrap/genwrap.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 61 additions & 17 deletions app/eth2wrap/httpwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"io"
"net/http"
"net/url"
"strconv"
"sync"
"testing"
"time"
Expand All @@ -26,6 +27,7 @@
"github.com/obolnetwork/charon/app/z"
"github.com/obolnetwork/charon/eth2util"
"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/eth2util/statecomm"
)

// BlockAttestationsProvider is the interface for providing attestations included in blocks.
Expand All @@ -37,6 +39,13 @@
BlockAttestationsV2(ctx context.Context, stateID string) ([]*spec.VersionedAttestation, error)
}

// BeaconStateCommitteesProvider is the interface for providing committees for given slot.
// It is a standard beacon API endpoint not implemented by eth2client.
// See https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees.
type BeaconStateCommitteesProvider interface {
BeaconStateCommittees(ctx context.Context, slot uint64) ([]*statecomm.StateCommittee, error)
}

// NodePeerCountProvider is the interface for providing node peer count.
// It is a standard beacon API endpoint not implemented by eth2client.
// See https://ethereum.github.io/beacon-APIs/#/Node/getPeerCount.
Expand Down Expand Up @@ -184,7 +193,7 @@
// See https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations.
func (h *httpAdapter) BlockAttestations(ctx context.Context, stateID string) ([]*eth2p0.Attestation, error) {
path := fmt.Sprintf("/eth/v1/beacon/blocks/%s/attestations", stateID)
respBody, statusCode, err := httpGet(ctx, h.address, path, h.timeout)
respBody, statusCode, err := httpGet(ctx, h.address, path, nil, h.timeout)

Check warning on line 196 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L196

Added line #L196 was not covered by tests
if err != nil {
return nil, errors.Wrap(err, "request block attestations")
} else if statusCode == http.StatusNotFound {
Expand All @@ -205,21 +214,24 @@
// See https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestationsV2.
func (h *httpAdapter) BlockAttestationsV2(ctx context.Context, stateID string) ([]*spec.VersionedAttestation, error) {
path := fmt.Sprintf("/eth/v2/beacon/blocks/%s/attestations", stateID)
resp, err := httpGetRaw(ctx, h.address, path, h.timeout)
ctx, cancel := context.WithTimeout(ctx, h.timeout)
defer cancel()

resp, err := httpGetRaw(ctx, h.address, path, nil)
if err != nil {
return nil, errors.Wrap(err, "request block attestations")
}
defer resp.Body.Close()

respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "request block attestations body")
}

if resp.StatusCode == http.StatusNotFound {
return nil, nil // No block for slot, so no attestations.
} else if resp.StatusCode != http.StatusOK {
return nil, errors.New("request block attestations failed", z.Int("status", resp.StatusCode), z.Str("body", string(respBody)))
return nil, errors.New("request block attestations failed", z.Int("status", resp.StatusCode))
}

Check warning on line 230 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L229-L230

Added lines #L229 - L230 were not covered by tests

respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "request block attestations body")

Check warning on line 234 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L234

Added line #L234 was not covered by tests
}

version, err := fetchConsensusVersion(resp)
Expand Down Expand Up @@ -284,9 +296,35 @@
return res, nil
}

// BeaconStateCommittees returns the attestations included in the requested block.
// See https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidators.
func (h *httpAdapter) BeaconStateCommittees(ctx context.Context, slot uint64) ([]*statecomm.StateCommittee, error) {
r := strconv.FormatUint(slot, 10)
path := fmt.Sprintf("/eth/v1/beacon/states/%v/committees", r)
queryParams := map[string]string{
"slot": strconv.FormatUint(slot, 10),
}
respBody, statusCode, err := httpGet(ctx, h.address, path, queryParams, h.timeout)
if err != nil {
return nil, errors.Wrap(err, "request state committees for slot", z.Int("status", statusCode), z.U64("slot", slot))
}

Check warning on line 310 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L301-L310

Added lines #L301 - L310 were not covered by tests

if statusCode != http.StatusOK {
return nil, errors.New("request state committees for slot failed", z.Int("status", statusCode), z.U64("slot", slot))
}

Check warning on line 314 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L312-L314

Added lines #L312 - L314 were not covered by tests

var res statecomm.StateCommitteesResponse
err = json.Unmarshal(respBody, &res)
if err != nil {
return nil, errors.Wrap(err, "unmarshal state committees", z.Int("status", statusCode), z.U64("slot", slot))
}

Check warning on line 320 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L316-L320

Added lines #L316 - L320 were not covered by tests

return res.Data, nil

Check warning on line 322 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L322

Added line #L322 was not covered by tests
}

// ProposerConfig implements eth2exp.ProposerConfigProvider.
func (h *httpAdapter) ProposerConfig(ctx context.Context) (*eth2exp.ProposerConfigResponse, error) {
respBody, statusCode, err := httpGet(ctx, h.address, "/proposer_config", h.timeout)
respBody, statusCode, err := httpGet(ctx, h.address, "/proposer_config", nil, h.timeout)

Check warning on line 327 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L327

Added line #L327 was not covered by tests
if err != nil {
return nil, errors.Wrap(err, "submit sync committee selections")
} else if statusCode != http.StatusOK {
Expand All @@ -305,7 +343,7 @@
// See https://ethereum.github.io/beacon-APIs/#/Node/getPeerCount.
func (h *httpAdapter) NodePeerCount(ctx context.Context) (int, error) {
const path = "/eth/v1/node/peer_count"
respBody, statusCode, err := httpGet(ctx, h.address, path, h.timeout)
respBody, statusCode, err := httpGet(ctx, h.address, path, nil, h.timeout)

Check warning on line 346 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L346

Added line #L346 was not covered by tests
if err != nil {
return 0, errors.Wrap(err, "request beacon node peer count")
} else if statusCode != http.StatusOK {
Expand Down Expand Up @@ -400,10 +438,7 @@
}

// httpGetRaw performs a GET request and returns the raw http response or an error.
func httpGetRaw(ctx context.Context, base string, endpoint string, timeout time.Duration) (*http.Response, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

func httpGetRaw(ctx context.Context, base string, endpoint string, queryParams map[string]string) (*http.Response, error) {
addr, err := url.JoinPath(base, endpoint)
if err != nil {
return nil, errors.Wrap(err, "invalid address")
Expand All @@ -419,6 +454,12 @@
return nil, errors.Wrap(err, "new GET request with ctx")
}

q := req.URL.Query()
for key, val := range queryParams {
q.Add(key, val)
}

Check warning on line 460 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L459-L460

Added lines #L459 - L460 were not covered by tests
req.URL.RawQuery = q.Encode()

res, err := new(http.Client).Do(req)
if err != nil {
return nil, errors.Wrap(err, "failed to call GET endpoint")
Expand All @@ -428,16 +469,19 @@
}

// httpGet performs a GET request and returns the body and status code or an error.
func httpGet(ctx context.Context, base string, endpoint string, timeout time.Duration) ([]byte, int, error) {
res, err := httpGetRaw(ctx, base, endpoint, timeout)
func httpGet(ctx context.Context, base string, endpoint string, queryParams map[string]string, timeout time.Duration) ([]byte, int, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

res, err := httpGetRaw(ctx, base, endpoint, queryParams)

Check warning on line 476 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L472-L476

Added lines #L472 - L476 were not covered by tests
if err != nil {
return nil, 0, errors.Wrap(err, "failed to read GET response")
}
defer res.Body.Close()

data, err := io.ReadAll(res.Body)
if err != nil {
return nil, 0, errors.Wrap(err, "failed to read GET response body")
return nil, res.StatusCode, errors.Wrap(err, "failed to read GET response body")

Check warning on line 484 in app/eth2wrap/httpwrap.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/httpwrap.go#L484

Added line #L484 was not covered by tests
}

return data, res.StatusCode, nil
Expand Down
10 changes: 10 additions & 0 deletions app/eth2wrap/lazy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"

"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/eth2util/statecomm"
)

//go:generate mockery --name=Client --output=mocks --outpkg=mocks --case=underscore
Expand Down Expand Up @@ -212,6 +213,15 @@
return cl.BlockAttestationsV2(ctx, stateID)
}

func (l *lazy) BeaconStateCommittees(ctx context.Context, slot uint64) ([]*statecomm.StateCommittee, error) {
cl, err := l.getOrCreateClient(ctx)
if err != nil {
return nil, err
}

Check warning on line 220 in app/eth2wrap/lazy.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/lazy.go#L216-L220

Added lines #L216 - L220 were not covered by tests

return cl.BeaconStateCommittees(ctx, slot)

Check warning on line 222 in app/eth2wrap/lazy.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/lazy.go#L222

Added line #L222 was not covered by tests
}

func (l *lazy) NodePeerCount(ctx context.Context) (int, error) {
cl, err := l.getOrCreateClient(ctx)
if err != nil {
Expand Down
32 changes: 32 additions & 0 deletions app/eth2wrap/mocks/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions app/eth2wrap/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"

"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/eth2util/statecomm"
)

// NewMultiForT creates a new mutil client for testing.
Expand Down Expand Up @@ -213,6 +214,24 @@
return res, err
}

func (m multi) BeaconStateCommittees(ctx context.Context, slot uint64) ([]*statecomm.StateCommittee, error) {
const label = "beacon_state_committees"
defer latency(ctx, label, false)()

res, err := provide(ctx, m.clients, m.fallbacks,
func(ctx context.Context, args provideArgs) ([]*statecomm.StateCommittee, error) {
return args.client.BeaconStateCommittees(ctx, slot)
},

Check warning on line 224 in app/eth2wrap/multi.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/multi.go#L217-L224

Added lines #L217 - L224 were not covered by tests
nil, m.selector,
)
if err != nil {
incError(label)
err = wrapError(ctx, err, label)
}

Check warning on line 230 in app/eth2wrap/multi.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/multi.go#L227-L230

Added lines #L227 - L230 were not covered by tests

return res, err

Check warning on line 232 in app/eth2wrap/multi.go

View check run for this annotation

Codecov / codecov/patch

app/eth2wrap/multi.go#L232

Added line #L232 was not covered by tests
}

func (m multi) NodePeerCount(ctx context.Context) (int, error) {
const label = "node_peer_count"
defer latency(ctx, label, false)()
Expand Down
14 changes: 7 additions & 7 deletions core/signeddata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func TestSignedDataSetSignature(t *testing.T) {
Version: eth2spec.DataVersionPhase0,
Phase0: &eth2p0.Attestation{
AggregationBits: testutil.RandomBitList(1),
Data: testutil.RandomAttestationData(),
Data: testutil.RandomAttestationDataPhase0(),
Signature: testutil.RandomEth2Signature(),
},
},
Expand All @@ -285,7 +285,7 @@ func TestSignedDataSetSignature(t *testing.T) {
Version: eth2spec.DataVersionAltair,
Altair: &eth2p0.Attestation{
AggregationBits: testutil.RandomBitList(1),
Data: testutil.RandomAttestationData(),
Data: testutil.RandomAttestationDataPhase0(),
Signature: testutil.RandomEth2Signature(),
},
},
Expand All @@ -298,7 +298,7 @@ func TestSignedDataSetSignature(t *testing.T) {
Version: eth2spec.DataVersionBellatrix,
Bellatrix: &eth2p0.Attestation{
AggregationBits: testutil.RandomBitList(1),
Data: testutil.RandomAttestationData(),
Data: testutil.RandomAttestationDataPhase0(),
Signature: testutil.RandomEth2Signature(),
},
},
Expand All @@ -311,7 +311,7 @@ func TestSignedDataSetSignature(t *testing.T) {
Version: eth2spec.DataVersionCapella,
Capella: &eth2p0.Attestation{
AggregationBits: testutil.RandomBitList(1),
Data: testutil.RandomAttestationData(),
Data: testutil.RandomAttestationDataPhase0(),
Signature: testutil.RandomEth2Signature(),
},
},
Expand All @@ -324,7 +324,7 @@ func TestSignedDataSetSignature(t *testing.T) {
Version: eth2spec.DataVersionDeneb,
Deneb: &eth2p0.Attestation{
AggregationBits: testutil.RandomBitList(1),
Data: testutil.RandomAttestationData(),
Data: testutil.RandomAttestationDataPhase0(),
Signature: testutil.RandomEth2Signature(),
},
},
Expand All @@ -337,7 +337,7 @@ func TestSignedDataSetSignature(t *testing.T) {
Version: eth2spec.DataVersionElectra,
Electra: &electra.Attestation{
AggregationBits: testutil.RandomBitList(1),
Data: testutil.RandomAttestationData(),
Data: testutil.RandomAttestationDataPhase0(),
Signature: testutil.RandomEth2Signature(),
CommitteeBits: testutil.RandomBitVec64(),
},
Expand Down Expand Up @@ -747,7 +747,7 @@ func TestVersionedSignedProposal(t *testing.T) {
}

func TestVersionedSignedAggregateAndProofUtilFunctions(t *testing.T) {
data := testutil.RandomAttestationData()
data := testutil.RandomAttestationDataPhase0()
aggregationBits := testutil.RandomBitList(64)
type testCase struct {
name string
Expand Down
Loading
Loading