Skip to content
Open
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
2 changes: 1 addition & 1 deletion .custom-gcl.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: v2.8.0
version: v2.13.1
name: custom-gcl

plugins:
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
# Full history so new issues can be computed against the merge base.
fetch-depth: 0

- uses: actions/setup-go@v5
with:
Expand All @@ -33,7 +36,7 @@ jobs:
- name: Build custom golangci-lint
if: steps.cache-custom-gcl.outputs.cache-hit != 'true'
run: |
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1
golangci-lint custom

# Make custom binary available as golangci-lint in PATH
Expand All @@ -48,6 +51,8 @@ jobs:
uses: golangci/golangci-lint-action@v8
with:
install-mode: "none"
args: "--timeout=60m"
only-new-issues: true
# Compute new issues locally: the action's own patch fetch fails on pull
# requests whose diff exceeds GitHub's 20000-line limit.
args: "--timeout=60m ${{ github.event_name == 'pull_request' && '--new-from-merge-base=origin/master' || '--new-from-rev=HEAD~1' }}"
only-new-issues: false
skip-cache: true
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
# Test binary, build with `go test -c`
*.test

# Custom golangci-lint binary built via `golangci-lint custom` (config: .custom-gcl.yml)
custom-gcl

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
coverage.html
Expand All @@ -25,3 +28,6 @@ Makefile

# Local TODO
TODO.md

# Consensus spec vectors fetched by .github/scripts/fetch-spec-tests.sh
.spec-tests/
34 changes: 21 additions & 13 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ linters:
- err113 # Disable error 113 check
- exhaustive # Disable exhaustive check
- exhaustruct # Disable exhaustruct check
- exhaustruct_v5 # Disable exhaustruct check
- forcetypeassert # Disable force type assert check
- funlen # Disable function length check
- gochecknoglobals # Disable global variable check
- gochecknoinits # Disable global variable check
- gocognit # Disable cognitive complexity check
- goconst # Disable constant check
- gocyclo # Disable gocyclo (redundant with cyclop; version-dispatch switches legitimately exceed default thresholds)
- gomodguard # Superseded by gomodguard_v2, which default: all enables
- intrange # Disable integer range check
- ireturn # Disable return check
- lll # Disable line length check
Expand Down Expand Up @@ -140,18 +142,6 @@ linters:
- all
- -ST1000

# tagalign: Struct tag alignment and ordering
tagalign:
# Don't column-align tags; alignment padding would push the SSZ tag lines
# past the revive line-length-limit (160). Single-space separation is used.
align: false
# Enforce tag key ordering. ssz-index stays first (it defines the field
# position for EIP-7688 progressive containers); remaining keys follow
# alphabetically.
sort: true
order:
- ssz-index

# tagliatelle: Struct tag naming conventions
tagliatelle:
case:
Expand All @@ -169,7 +159,7 @@ linters:
# HIGH PRIORITY
enable_no_pkg_logger: false
enable_enum_iota: true
enable_current_year: false
enable_current_year: true
# MEDIUM PRIORITY
enable_capital_comment: false
enable_func_opts: true
Expand All @@ -194,6 +184,24 @@ linters:
- third_party$ # Exclude third party code
- builtin$ # Exclude builtin code
- examples$ # Exclude example code
# Rule-based exclusions
rules:
# gloas ePBS type definitions carry long dynssz size-expression struct
# tags and require ssz-index tags for correct SSZ generation (removing
# them changes the generated SSZ). gofmt aligns these tags past the
# 132-char limit and struct tags cannot be wrapped, so exempt only the
# line-length checks for the gloas type packages.
- path: (spec|api/v1)/gloas/
text: "line is [0-9]+ characters"
linters:
- revive
# The http package is named after the API it wraps. revive reports the
# clash with net/http once per package on an arbitrary file, so a merge
# that adds files to the package would otherwise fail the new-lines check.
- path: ^http/
text: 'package-naming: don''t use "http"'
linters:
- revive
# Formatters: Code formatting tools
formatters:
enable:
Expand Down
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
0.30.0:
- support Gloas consensus types and versioned helpers
- add execution payload envelope retrieval and bid/envelope submission APIs
- add payload timeliness committee duties and payload attestation APIs
- add MultiForkProposalProvider for proposal production across the Gloas fork
- support Gloas attestation hashing, submission and payload-availability votes
- fix attestation-pool decoding and validation across forks
- honor custom SSZ presets when submitting proposals
- add mock SubmitBLSToExecutionChanges
- handle invalid DataVersion values without panicking
- bound HTTP response sizes for POSTs and Gloas fetch endpoints
- add SubmitProposerPreferences with JSON and SSZ support in http, multi and mock clients
- update ePBS block production to POST /eth/v4/validator/blocks/{slot} with a required EPBSProposalOpts.BuilderConfig
- add Gloas BuilderConfig, BuilderEntry, BuilderRequestAuth and SignedBuilderRequestAuth types with JSON, YAML and SSZ support
- add VersionedEPBSProposal.BuilderIndex and SubmitProposalOpts.BuilderURL for direct-builder proposal routing
- validate ePBS bid policy and execution-value metadata, including minimum bids and execution-payment caps
- add head_v2 events and HeadV2Handler, including fork version, payload status and epoch-dependent roots
- add light_client_finality_update and light_client_optimistic_update events through the generic event handler
- add ProposerDutiesV2Provider and ProposerDutiesV2 in http, multi and mock clients
- use /eth/v2/validator/duties/proposer/{epoch} and its dependent-root semantics with head_v2; existing v1 behavior is unchanged
- fix multi event subscriptions to preserve topics and handlers, forward only the active client's events, and continue retrying failed subscriptions
- encode empty Gloas execution-payload transactions as "0x" in JSON and YAML, and accept them on JSON decode
- add Gloas events: execution_payload, execution_payload_gossip, execution_payload_available, execution_payload_bid, payload_attestation_message, proposer_preferences and fast_confirmation
- add http.ValidateEventsOpts, the events options check shared by the http and multi clients
- Event.UnmarshalJSON decodes attester_slashing data as electra.AttesterSlashing rather than phase0.AttesterSlashing, matching the events stream
- multi Events() validates its options as http Events() does, and returns an error if no client is subscribed or awaiting a retry
- Events() no longer checks topics against SupportedEventTopics: removing a topic from the map no longer refuses subscriptions to it, and adding one no longer lets it through
- multi Events() waits one retry interval before retrying a client that failed to subscribe, rather than retrying at once; set the interval, 5s by default, with WithEventsRetryInterval

0.29.0:
- use dynssz library for SSZ handling

Expand Down
65 changes: 65 additions & 0 deletions api/epbsproposalopts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 api

import (
"github.com/ethpandaops/go-eth2-client/spec/gloas"
"github.com/ethpandaops/go-eth2-client/spec/phase0"
)

// EPBSProposalOpts are the options for obtaining ePBS block proposals.
//
// Post-Gloas there is no blinded proposal: proposers commit to an execution
// payload bid rather than to a payload, so the axis that replaced blinded versus
// unblinded is IncludePayload — whether the execution payload envelope travels
// with the block or stays cached on the producing node.
type EPBSProposalOpts struct {
Common CommonOpts

// Slot is the slot for which the proposal should be fetched.
Slot phase0.Slot
// RandaoReveal is the RANDAO reveal for the proposal.
RandaoReveal phase0.BLSSignature
// Graffiti is the graffiti to be included in the beacon block body.
Graffiti [32]byte
// SkipRandaoVerification is true if we do not want the server to verify our RANDAO reveal.
// If this is set then the RANDAO reveal should be passed as the point at infinity (0xc0…00)
SkipRandaoVerification bool
// BuilderConfig supplies the required P2P and direct-builder bid policy.
BuilderConfig *gloas.BuilderConfig
// IncludePayload selects whether a self-built proposal carries its execution
// payload envelope and blobs.
//
// True asks a self-building node for the stateless form: everything needed to
// publish travels in the response, so any beacon node can publish the block.
// This is what multiple-beacon-node, distributed-validator and failover setups
// require. It is a request rather than a guarantee: a block built on an
// external builder's bid comes back with
// VersionedEPBSProposal.ExecutionPayloadIncluded false whatever was asked for,
// because the node does not hold the builder's payload, so those setups must
// read that field rather than assume this one describes what arrived.
//
// False is stateful: the producing node caches the envelope and blobs, so the
// block must be published via that same node, and the envelope retrieved from
// it with ExecutionPayloadEnvelope() — but only when the node self-built the
// block. An external builder's bid comes back excluded regardless of what was
// asked for, and the response alone does not say which case occurred; that is
// operational context the caller must already know.
//
// The spec marks this parameter required with no default, and the two choices
// carry materially different operational constraints, so it is a pointer:
// there is no safe value to assume on the caller's behalf, and leaving it
// unset is rejected rather than silently resolved to the constraining mode.
IncludePayload *bool
}
4 changes: 2 additions & 2 deletions api/eventsopts.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2025 Attestant Limited.
// Copyright © 2025 - 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
Expand Down Expand Up @@ -147,7 +147,7 @@ type ExecutionPayloadAvailableEventHandlerFunc func(context.Context, *apiv1.Exec
type ExecutionPayloadBidEventHandlerFunc func(context.Context, *gloas.SignedExecutionPayloadBid)

// ExecutionPayloadGossipEventHandlerFunc is the handler for execution_payload_gossip events.
type ExecutionPayloadGossipEventHandlerFunc func(context.Context, *apiv1.ExecutionPayloadEvent)
type ExecutionPayloadGossipEventHandlerFunc func(context.Context, *apiv1.ExecutionPayloadGossipEvent)

// FastConfirmationEventHandlerFunc is the handler for fast_confirmation events.
type FastConfirmationEventHandlerFunc func(context.Context, *apiv1.FastConfirmationEvent)
Expand Down
40 changes: 40 additions & 0 deletions api/executionpayloadenvelopeopts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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 api

import (
"github.com/ethpandaops/go-eth2-client/spec/phase0"
)

// ExecutionPayloadEnvelopeOpts are the options for obtaining the execution
// payload envelope a node cached while producing a block.
//
// This is the other half of asking for a proposal with the payload excluded: the
// node kept the envelope, and this is how the caller retrieves it to sign and
// publish.
type ExecutionPayloadEnvelopeOpts struct {
Common CommonOpts

// Slot is the slot for which the envelope was built.
Slot phase0.Slot
// BeaconBlockRoot is the root of the beacon block the envelope commits to.
//
// Passing it makes the lookup re-org resistant: if a re-org happens between
// producing the block and retrieving the envelope, the caller gets nothing
// rather than an envelope committed to a block that is no longer on the
// chain. The node is expected to check the cached envelope against it, and
// the returned envelope's own root is checked against it again here, so a
// node that does not is caught rather than trusted.
BeaconBlockRoot phase0.Root
}
24 changes: 24 additions & 0 deletions api/payloadattestationdataopts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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 api

import "github.com/ethpandaops/go-eth2-client/spec/phase0"

// PayloadAttestationDataOpts are the options for obtaining payload attestation data.
type PayloadAttestationDataOpts struct {
Common CommonOpts

// Slot is the slot for which the data is obtained.
Slot phase0.Slot
}
26 changes: 26 additions & 0 deletions api/payloadattestationpoolopts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 api

import "github.com/ethpandaops/go-eth2-client/spec/phase0"

// PayloadAttestationPoolOpts are the options for obtaining the payload attestation pool.
type PayloadAttestationPoolOpts struct {
Common CommonOpts

// Slot is the slot for which the data is obtained. If not present then
// data for all slots will be obtained. Note that some beacon nodes
// require a slot and will reject a request without one.
Slot *phase0.Slot
}
26 changes: 26 additions & 0 deletions api/ptcdutiesopts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 api

import "github.com/ethpandaops/go-eth2-client/spec/phase0"

// PTCDutiesOpts are the options for obtaining payload timeliness committee duties.
type PTCDutiesOpts struct {
Common CommonOpts

// Epoch is the epoch for which the data is obtained.
Epoch phase0.Epoch
// Indices is a list of validators for which to obtain the duties.
Indices []phase0.ValidatorIndex
}
2 changes: 1 addition & 1 deletion api/signedexecutionpayloadenvelopeopts.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2025 Attestant Limited.
// 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
Expand Down
7 changes: 2 additions & 5 deletions api/submitexecutionpayloadbidopts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,10 @@ type SubmitExecutionPayloadBidOpts struct {
SignedExecutionPayloadBid *spec.VersionedSignedExecutionPayloadBid
}

// SubmitAgnosticExecutionPayloadBidOpts are the options for submitting
// execution payload bids supplied as a fork-agnostic
// *all.SignedExecutionPayloadBid.
// SubmitAgnosticExecutionPayloadBidOpts are the options for submitting a fork-agnostic execution payload bid.
type SubmitAgnosticExecutionPayloadBidOpts struct {
Common CommonOpts

// SignedExecutionPayloadBid is the signed bid to publish. Its Version
// selects the wire schema and the consensus version header.
// SignedExecutionPayloadBid is the fork-agnostic signed execution payload bid to submit.
SignedExecutionPayloadBid *all.SignedExecutionPayloadBid
}
Loading
Loading