Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
228 commits
Select commit Hold shift + click to select a range
f94accf
exporter: drop dead vendor sszgen directive (file is hand-maintained)
iurii-ssv Jun 23, 2026
31fdf1d
gloas: add node-side ePBS wire types and Gloas fork placeholder
iurii-ssv Jun 23, 2026
228fe53
beacon: wire GLOAS_FORK_EPOCH from BN spec and add (*Beacon).IsGloas
iurii-ssv Jun 23, 2026
dbec104
gloas: add PTC payload-attestation types and goclient endpoints
iurii-ssv Jun 23, 2026
f9512fc
gloas: align ePBS role names with SIP #94 and map beacon->runner roles
iurii-ssv Jun 23, 2026
7f97579
beacon: expose PTCCalls on the BeaconNode interface
iurii-ssv Jun 23, 2026
23cce94
gloas, goclient: dedupe PTC JSON hex-decoding and client-fallback loops
iurii-ssv Jun 23, 2026
147f001
ekm: support Gloas (ePBS) PTC payload-attestation signing
iurii-ssv Jun 24, 2026
1641b10
gloas: add PTC attester runner on ssv-spec ePBS constants
iurii-ssv Jun 24, 2026
cc8c3f5
gloas: register PTCAttesterRunner and add runner unit tests
iurii-ssv Jun 24, 2026
9e80836
gloas: add PTC attestation scheduler handler
iurii-ssv Jun 24, 2026
305ec88
message/validation: accept Gloas (ePBS) PTC attestation messages
iurii-ssv Jun 24, 2026
39b1e33
gloas: move DataVersionGloas to networkconfig and tidy fork helpers
iurii-ssv Jun 24, 2026
cd39e23
networkconfig: add GlamsterdamDevnet (ePBS devnet) stub
iurii-ssv Jun 24, 2026
3217f1f
docker: exclude tla/ from the build context
iurii-ssv Jun 24, 2026
844e6ab
ptc: address code-review feedback
iurii-ssv Jun 24, 2026
3840728
ptc: reconcile runner with boole-fork's renamed duty-completion API
iurii-ssv Jun 24, 2026
01b5f85
gloas: add ProposerPreferences wire types, endpoint abstraction, signing
iurii-ssv Jun 24, 2026
57111ae
gloas: add ProposerPreferences runner (SIP #94 §5)
iurii-ssv Jun 24, 2026
e6bd527
gloas: emit & route ProposerPreferences duties (SIP #94 §5)
iurii-ssv Jun 24, 2026
7d2eabd
gloas: ProposerPreferences message validation (SIP #94 §5)
iurii-ssv Jun 24, 2026
0309e83
gloas: deprecate ValidatorRegistration at the fork (SIP #94 §5)
iurii-ssv Jun 24, 2026
191bb2e
gloas: pre-fork ProposerPreferences emission window (SIP #94 §5)
iurii-ssv Jun 24, 2026
f84069f
gloas: ProposerPreferences spam hardening (SIP #94 §5)
iurii-ssv Jun 24, 2026
0487ea6
gloas: ProposerPreferences runner unit tests (SIP #94 §5)
iurii-ssv Jun 24, 2026
77e2813
gloas: ProposerPreferences reorg re-emission (SIP #94 §5)
iurii-ssv Jun 25, 2026
5acd64f
gloas: ProposerPreferences multi-slot runner + review fixes (SIP #94 §5)
iurii-ssv Jun 25, 2026
789fcdf
gloas: dedup ProposerPreferences dependent_root fetches (SIP #94 §5)
iurii-ssv Jun 25, 2026
0731881
gloas: add NewGloasVoteChecker for §2 attestations (SIP #94 §2)
iurii-ssv Jun 25, 2026
79188d4
gloas: wire GloasBeaconVote into the committee runner (SIP #94 §2)
iurii-ssv Jun 25, 2026
12d42c2
gloas: recognize §2 GloasBeaconVote in observer + duty tracer (SIP #9…
iurii-ssv Jun 25, 2026
9198035
gloas: test §2 fork-aware decode in observer + duty tracer (SIP #94 §2)
iurii-ssv Jun 25, 2026
26bd246
gloas: retime duty deadlines to quarters at the fork (SIP #94 §1)
iurii-ssv Jun 25, 2026
0720a98
gloas: add node-side Gloas block + bid SSZ types (SIP #94 §4)
iurii-ssv Jun 25, 2026
dbed6b3
gloas: add goclient produce/publish for Gloas blocks (SIP #94 §4)
iurii-ssv Jun 25, 2026
b527531
gloas: run the proposer duty on node-side Gloas blocks (SIP #94 §4)
iurii-ssv Jun 25, 2026
ab3975f
gloas: tidy stale comments and a parse-error message from review
iurii-ssv Jun 25, 2026
d3d4f19
runner: guard GetStateRoot against a nil State
iurii-ssv Jun 25, 2026
97d6156
goclient: drop stale 2/3-of-slot wording from the aggregate/contribut…
iurii-ssv Jun 25, 2026
c2a0bb6
gloas: content-match before decoding on the proposer submit path
iurii-ssv Jun 25, 2026
f85be26
networkconfig: add IsGloasAtSlot and use it across slot-keyed callers
iurii-ssv Jun 25, 2026
5c5dc47
gloas: type ExecutionPayloadBid.PrevRandao as Hash32
iurii-ssv Jun 25, 2026
b8c6030
gloas: add §6 envelope QBFT types (SIP #94 §6)
iurii-ssv Jun 26, 2026
7f0e01c
gloas: add §6 envelope value-check + decided-block-root store (SIP #9…
iurii-ssv Jun 26, 2026
bdd4e5a
gloas: wire the proposer side of §6 — record the §4 root + trigger th…
iurii-ssv Jun 26, 2026
3e7126e
gloas: address §6 envelope/proposer review findings
iurii-ssv Jun 26, 2026
0726a00
gloas: add the §6 EnvelopeBuilder runner and register it
iurii-ssv Jun 26, 2026
9be9be8
runner: dedup the {BaseRunner, ValCheck} JSON (de)serialization
iurii-ssv Jun 26, 2026
47fe79f
message/validation: accept the §6 envelope-builder role (role 9)
iurii-ssv Jun 26, 2026
fc249be
gloas: implement the §6 heavy payload — types, goclient, runner, trigger
iurii-ssv Jun 26, 2026
129e466
gloas: drop the unused full/signed envelope Encode/Decode wrappers
iurii-ssv Jun 26, 2026
57ead64
gloas: test the §6 envelope runner's post-consensus publish path
iurii-ssv Jun 26, 2026
ac8503a
gloas: verify the §6 ExecutionPayload layout against the canonical spec
iurii-ssv Jun 26, 2026
e56b7b0
gloas: every operator submits the §4 block (restore liveness redundancy)
iurii-ssv Jun 27, 2026
c58aa9e
gloas: fix two stale §6 doc comments
iurii-ssv Jun 27, 2026
494f254
ekm: add the §6 DomainBeaconBuilder signing arm
iurii-ssv Jun 27, 2026
eec7e88
gloas: keep the §2 aggregation index on Gloas slots
iurii-ssv Jun 27, 2026
b898bf6
gloas: refresh PTC duties on reorg / indices change
iurii-ssv Jun 27, 2026
249ea8f
gloas: fix two stale §5/§6 doc comments
iurii-ssv Jun 27, 2026
a59eb48
gloas: harden PTC message validation (lateness + duty-count cap)
iurii-ssv Jun 27, 2026
6158c39
gloas: add proposal build-source telemetry + envelope log slot
iurii-ssv Jun 27, 2026
7cf5602
gloas: gate PTC messages on a real duty assignment
iurii-ssv Jun 27, 2026
3211c03
gloas: retime QBFT round timer to IntervalDuration; add ProposerDelay…
iurii-ssv Jun 27, 2026
3d942ea
gloas: scale attestation-data fetch budgets to the Gloas slot window
iurii-ssv Jun 28, 2026
66edafd
gloas: fix stale ePBS submit comments, add submit-error test, tidy EKM
iurii-ssv Jun 28, 2026
5e25c78
gloas: commit ePBS implementation plan as temporary in-branch scratch
iurii-ssv Jun 28, 2026
3faefb5
gloas: tighten ePBS code comments across the PR
iurii-ssv Jun 28, 2026
94ba697
gloas: plan §8 fork-transition monitoring + logs-first observability
iurii-ssv Jun 28, 2026
24f5a3b
gloas: add greppable logs for fork activation, build-source, duty out…
iurii-ssv Jun 28, 2026
2c38966
gloas: mark §8 log-coverage audit G1-G4 done (logs shipped)
iurii-ssv Jun 28, 2026
2dab52b
gloas: log §2 vote index + §5 proposer-preferences inputs (greppable)
iurii-ssv Jun 28, 2026
a92837d
gloas: fix golangci-lint findings on the ePBS PR
iurii-ssv Jun 28, 2026
759afcf
gloas: address ePBS PR review — fan-out publish, envelope duty check,…
iurii-ssv Jun 29, 2026
48715aa
gloas: review cleanups — detach attestation singleflight ctx
iurii-ssv Jun 29, 2026
2b8b29e
gloas: address ePBS PR review — doc-comment clarifications + plan notes
iurii-ssv Jun 30, 2026
cba5b74
gloas: retarget ePBS devnet plan to glamsterdam-devnet-6; document en…
iurii-ssv Jun 30, 2026
d3a51d8
gloas: wire §4/§5/§6 to the merged beacon-APIs (#580)
iurii-ssv Jun 30, 2026
094f1ac
gloas: normalize hand-rolled beacon-node address (fixes scheme-less c…
iurii-ssv Jun 30, 2026
3ecc0f3
gloas: plan — local_testnet ePBS testing merged to aetheria main (§7 …
iurii-ssv Jun 30, 2026
e49b54d
gloas: retry duty fetch when no validators are eligible yet
iurii-ssv Jul 1, 2026
c39984e
fix linter
iurii-ssv Jul 1, 2026
7d847b3
gloas: bring ePBS submit paths onto the mature runners' idiom
iurii-ssv Jul 1, 2026
98572d7
gloas: tighten ePBS doc-comments
iurii-ssv Jul 1, 2026
647c1e8
gloas: §5 publish-finality KNOWN ISSUE + consolidated plan TODO index
iurii-ssv Jul 1, 2026
1c61d94
gloas: add proposer slot-dispatch + late-fetch diagnostics (ssvlabs/s…
iurii-ssv Jul 1, 2026
161d04d
gloas: plan — track remote-signing (Web3Signer) breakage RS-1/2/3 acc…
iurii-ssv Jul 1, 2026
2198a15
gloas: fix remote-signer fork_info on Gloas slots (RS-1)
iurii-ssv Jul 1, 2026
32885b9
gloas: plan — RS-1 done; correct RS-2 (module boundary, not "add a ca…
iurii-ssv Jul 1, 2026
f01223f
fix(gloas): pad graffiti to 32 bytes in the §4 block request
iurii-ssv Jul 1, 2026
75dda33
fix(gloas): sign the §4 Gloas block on the local key manager
iurii-ssv Jul 1, 2026
c26330a
gloas: plan — devnet run 2026-07-01 (§4 produces on devnet-5); correc…
iurii-ssv Jul 1, 2026
dfab724
gloas: add block slashing protection to the local §4 Gloas block signer
iurii-ssv Jul 1, 2026
ec951ba
gloas: fetch §2 attestation data via a hand-rolled GET on Gloas slots
iurii-ssv Jul 1, 2026
42c3547
fix(gloas): type §4/§6 execution requests as the EIP-8282 five-list v…
iurii-ssv Jul 1, 2026
01652f5
add tla/ to gitignore
iurii-ssv Jul 2, 2026
d0bf201
gloas: remove the ePBS implementation plan (fully migrated)
iurii-ssv Jul 2, 2026
a32cd24
fix(gloas): key §4 block slashing protection to the signed block's slot
iurii-ssv Jul 2, 2026
448496e
gloas: export GloasDataVersion and guard its mirror of DataVersionGloas
iurii-ssv Jul 2, 2026
a8ebe90
gloas: convert proposer-duty TEMP diagnostics to permanent Debug logs
iurii-ssv Jul 2, 2026
73d531f
gloas: decode proposer dependent_root directly into phase0.Root
iurii-ssv Jul 2, 2026
a9a98eb
gloas: extract requestProposerDutiesDependentRoot + cover it with tests
iurii-ssv Jul 2, 2026
a9d24bb
gloas: treat "already known" §4 block submit as success (#2922)
iurii-ssv Jul 2, 2026
f575c98
gloas: publish the full §6 envelope (unblinded) to match Lodestar (#2…
iurii-ssv Jul 2, 2026
4212af8
gloas: fix slot-fraction wording left stale by the interval retiming
iurii-ssv Jul 3, 2026
d4938d8
gloas: fix "signalling" misspellings flagged by the misspell linter
iurii-ssv Jul 3, 2026
b726d7d
gloas: drop embedded-field selector in gloasBlockStub (staticcheck QF…
iurii-ssv Jul 3, 2026
52aa8c7
gloas: treat "already known" §6 envelope publish as success (#2923)
iurii-ssv Jul 3, 2026
0004106
gloas: enable §5 proposer-preferences re-emission on dependent_root c…
iurii-ssv Jul 8, 2026
9846e08
gloas: §5 proposer-preferences dedup — IGNORE distinct root past cap
iurii-ssv Jul 8, 2026
d4c0684
gloas: align PTC duty-limit and proposer value-check with the SIP
iurii-ssv Jul 8, 2026
96ac785
gloas: flag §5 submit 404 as a missing beacon-API route
iurii-ssv Jul 15, 2026
d76b077
gloas: fix §5 pre-consensus starvation across skewed emission ticks
iurii-ssv Jul 15, 2026
c8200e2
gloas: tolerate §5/§6 duty-view staleness across validator-set changes
iurii-ssv Jul 15, 2026
dba3331
gloas: mark proposer duty views stale on indices-change receipt, not …
iurii-ssv Jul 16, 2026
5567860
gloas: §5 skip re-broadcast of an unchanged in-flight preference on r…
iurii-ssv Jul 16, 2026
e84e39f
gloas: log own-validator message drops at the router fall-through
iurii-ssv Jul 17, 2026
ac22fec
gloas: settle the V5/V9/V11 node-side verification items
iurii-ssv Jul 26, 2026
6cf47dd
ibft/storage: resolve local-directory ssv-spec replaces in GetSpecDir
iurii-ssv Jul 28, 2026
e0c8606
gloas: re-pin ssv-spec for the request-auth constants; absorb the Env…
iurii-ssv Jul 28, 2026
c9baa48
gloas: request-auth wire types and the BuilderEntry config vocabulary…
iurii-ssv Jul 28, 2026
05b4082
gloas: DomainRequestAuth signing arms (#2962)
iurii-ssv Jul 28, 2026
0559ec5
gloas: #2962 phase 1 — threshold request-auth rounds on the §5 dispat…
iurii-ssv Jul 28, 2026
59b884c
gloas: #2962 review follow-ups — clock-anchored auth cache, shared-to…
iurii-ssv Jul 29, 2026
bf71214
gloas: #2962 follow-ups — hard-fail auth partials without local build…
iurii-ssv Jul 29, 2026
0bc17a8
gloas: #2962 review round 3 — replay stash into concluded re-emission…
iurii-ssv Jul 29, 2026
1ab737d
gloas: retire the GlamsterdamDevnet networkconfig stub
iurii-ssv Jul 29, 2026
e900e79
gloas: group the Gloas beacon-node interfaces with the other *Calls b…
iurii-ssv Aug 5, 2026
ba3a8a6
gloas: reconcile the request-auth overlay to the merged Gloas builder…
iurii-ssv Aug 25, 2026
d87d72a
gloas: adopt the renamed spectypes.DomainBuilderRequestAuth
iurii-ssv Aug 25, 2026
f8c1c2c
gloas: adopt the produceBlockV4 POST for the direct-builder overlay (…
iurii-ssv Aug 25, 2026
edffc6b
gloas: phase-2 self-review — dedupe the produce SSZ decode; comment p…
iurii-ssv Aug 25, 2026
4ee26a7
gloas: submit ahead-of-time builder preferences (phase 3)
iurii-ssv Aug 25, 2026
3c9a4e4
gloas: phase-3 self-review — simplify the proposer-pubkey conversion
iurii-ssv Aug 25, 2026
835fbfe
gloas: deferred goclient refinements (produce version guard, HTTP-hel…
iurii-ssv Aug 26, 2026
61e17bd
gloas: code-review follow-ups — POST on a knobs-only config; comment/…
iurii-ssv Aug 26, 2026
a2b62d9
gloas: carry builder_boost_factor on the produce GET fallback
iurii-ssv Aug 26, 2026
1b33f73
message/validation: admit the Gloas runner roles in validRoleUnion (#…
iurii-ssv Aug 26, 2026
a43a5b2
message: add the Gloas runner roles to the exporter role-string mappers
iurii-ssv Aug 26, 2026
f790a47
exporter/dutytracer: explicitly skip the Gloas duty types
iurii-ssv Aug 26, 2026
1d2bea0
gloas: point the remote-signer TODO(gloas) markers at the tracking issue
iurii-ssv Aug 26, 2026
86c3e62
gloas: address direct-builder review follow-ups
iurii-ssv Aug 26, 2026
68eebed
gloas: resolve the builder config once, not per read (parse-once)
iurii-ssv Aug 26, 2026
8cbd104
gloas: polish the parse-once follow-ups (docs, receiver, lint)
iurii-ssv Aug 26, 2026
6c3a61c
gloas: fix PTC payload_attestation_data URL and 204 abstain handling
iurii-ssv Aug 27, 2026
f52364c
gloas: extract the Eth-Consensus-Version header name into a constant
iurii-ssv Aug 27, 2026
7cbca49
gloas: POST-first produceBlockV4 with a neutral local-build config
iurii-ssv Aug 27, 2026
b14b4ab
gloas: PTC abstain test + POST-first/204 doc-comment fixes
iurii-ssv Aug 27, 2026
1678da4
gloas: mark BlindedExecutionPayloadEnvelope internal-only; drop dead …
iurii-ssv Aug 27, 2026
e9aeb77
gloas: set the required Eth-Blob-Data-Included header on §6 envelope …
iurii-ssv Aug 27, 2026
f9e20be
gloas: repin ssv-spec to epbs-gloas-types head; typed MsgID constructors
iurii-ssv Aug 28, 2026
d62e9d3
networkconfig: keep Boole unscheduled by default on local-testnet
iurii-ssv Aug 31, 2026
fd70308
gloas: name the Eth-Blob-Data-Included header; document the neutral-b…
iurii-ssv Aug 31, 2026
4211789
spectest: support ssv-spec's split vector layout (ssvlabs/spec-tests)
iurii-ssv Aug 31, 2026
0a6106f
gloas: run the repinned spec's Gloas valcheck vectors; skip the unmap…
iurii-ssv Aug 31, 2026
b6b7d88
spectest: point the Gloas vector-mapping skips at the tracking issue
iurii-ssv Aug 31, 2026
ac8b555
deps: bump Ethereum-related dependencies (root + ssvsigner)
iurii-ssv Sep 1, 2026
af6feaf
gloas: source beacon-chain types from go-eth2-client; SSZ via dynamic…
iurii-ssv Sep 1, 2026
65350fa
gloas: envelope round timers, voter join, reorg gating (review follow…
iurii-ssv Sep 2, 2026
1d7b840
gloas: bump ssv-spec to the reviewed dynssz branch
iurii-ssv Sep 2, 2026
77d2357
observability: align semconv with otel/sdk to fix boot schema conflict
iurii-ssv Sep 2, 2026
65f6c85
gloas: Gloas aggregate containers, ForkAtEpoch Gloas, devnet-8 golden…
iurii-ssv Sep 2, 2026
d2d9dd7
gloas: rework the §6 envelope duty to dissemination and threshold sig…
iurii-ssv Sep 7, 2026
1849b77
gloas: reveal the self-build envelope from the produce response (SIP …
iurii-ssv Sep 7, 2026
580e4b2
message validation: keep per-epoch duty counts across the lookahead (…
iurii-ssv Sep 7, 2026
33c4690
duties: pre-fetch PTC duties across the Gloas fork and reconcile look…
iurii-ssv Sep 7, 2026
3d03760
message validation: accept messages up to a second before their slot
iurii-ssv Sep 7, 2026
72f7c83
review follow-ups: PayloadRoot note, duty-count ring, dead voter branch
iurii-ssv Sep 8, 2026
51f01f9
message validation: build duty IDs through the shared builder
iurii-ssv Sep 8, 2026
f4cfc56
goclient: join a slot's payload-attestation fetches into one request
iurii-ssv Sep 8, 2026
c692193
gloas envelope: review follow-ups on the runner and its dissemination…
iurii-ssv Sep 9, 2026
e232aac
gloas envelope: hold the reveal data only once it binds
iurii-ssv Sep 9, 2026
f9c9b3f
gloas proposer: decide payload_root with the block and sign the envel…
iurii-ssv Sep 10, 2026
08db854
gloas runners: adopt the coded sentinels and count envelope publishes…
iurii-ssv Sep 10, 2026
cf22c69
tests: pass the interval duration to the round estimator
iurii-ssv Sep 11, 2026
635f697
message validation: ignore a repeated preference or request-auth root…
iurii-ssv Sep 11, 2026
4ec6d26
gloas value check: reject index 1 for a block the operator knows to b…
iurii-ssv Sep 12, 2026
5ddf7a9
review follow-ups: pyspec aggregate vector, §7 same-index reject, PTC…
iurii-ssv Sep 14, 2026
151c991
gloas proposer: release the reveal data as soon as it cannot be publi…
iurii-ssv Sep 15, 2026
1200915
review follow-ups: cache the envelope match, decode the decided value…
iurii-ssv Sep 15, 2026
653a23f
networkconfig: compare fork schedules by what is scheduled
iurii-ssv Sep 17, 2026
5906770
rebase follow-ups: adapt stage's queue tests to the spec pin; keep th…
iurii-ssv Sep 17, 2026
ed84933
queue consumer: one pop policy, and an explicit multi-slot opt-out fr…
iurii-ssv Sep 17, 2026
ff6c4cd
queue consumer: never hold a multi-slot runner's messages while it lo…
iurii-ssv Sep 21, 2026
86595be
beacon config: tolerate a client that lags on a future fork
iurii-ssv Sep 21, 2026
48f47a1
message validation: bound a preference's earliness by the lookahead e…
iurii-ssv Sep 21, 2026
dfaf4ca
runner: give the PTC attestation's outcome the next slot too
iurii-ssv Sep 21, 2026
1682c0c
preferences dispatcher: a re-emission takes the slot over once assign…
iurii-ssv Sep 21, 2026
c4a4424
message validation: allocate per-operator state only for a verified m…
iurii-ssv Sep 22, 2026
c9dc698
message validation: say why a cleared PTC epoch is tolerated
iurii-ssv Sep 22, 2026
44d0066
queue consumer: a multi-slot runner takes anything, in every state
iurii-ssv Sep 22, 2026
e4f5b5a
beacon config: say which side lags on a fork, and keep checking the c…
iurii-ssv Sep 22, 2026
ca75283
proposer preferences: build under the remembered root when the fetch …
iurii-ssv Sep 22, 2026
e7c7a1b
docs: point the builders page at the design record and the coverage t…
iurii-ssv Sep 22, 2026
0b3ab01
deps: finish the go-bitfield module-path migration the go-eth2-client…
iurii-ssv Sep 22, 2026
6fe7a95
deps: gofmt the import blocks the module-path switch reordered
iurii-ssv Sep 22, 2026
3cd7642
sip-94 conformance: batched request auths, hostname auth default, rev…
iurii-ssv Sep 23, 2026
7a38b68
comments: cite SIP-94 and beacon-APIs#630 in place of the builder ove…
iurii-ssv Sep 23, 2026
366ff8e
attestation data: count the refetch skip in the deadline test
iurii-ssv Sep 23, 2026
979063e
spec tests: look the module cache up once per process, from the first…
iurii-ssv Sep 23, 2026
37b138d
proposer: drop the previous duty's state even when doppelganger prote…
iurii-ssv Sep 23, 2026
662e84e
cleanups: drop a duplicate role read in partial-signature validation;…
iurii-ssv Sep 23, 2026
41df79c
message validation: read a signer's state without allocating it in th…
iurii-ssv Sep 23, 2026
ddd8ce0
proposer: a bad share doesn't fail the duty, and a reconstruct left a…
iurii-ssv Sep 23, 2026
a809166
post-consensus validation: drop a decode of the decided value that th…
iurii-ssv Sep 23, 2026
a9f30ce
beacon config: every lagging client needs upgrading, and stop two epo…
iurii-ssv Sep 23, 2026
d7d060a
comments: drop the stale §6 envelope mentions, and say what bounds th…
iurii-ssv Sep 23, 2026
3c3bdcf
runners: a bad share in a pre-consensus quorum doesn't fail the duty
iurii-ssv Sep 23, 2026
937d5ef
sync committee contribution: a bad share in one subnet's quorum no lo…
iurii-ssv Sep 23, 2026
86ffdde
proposer duties: a reorg marks the refetched epochs stale until the r…
iurii-ssv Sep 23, 2026
ca97c43
committee runners: a reconstruct with no bad share to drop fails the …
iurii-ssv Sep 24, 2026
dc025b4
aggregator committee: reconstruct the pre-consensus selection proofs …
iurii-ssv Sep 24, 2026
1f0a7da
ssv-spec: re-pin both go.mods to the latest #643 head, and port its p…
iurii-ssv Sep 24, 2026
5f7ea55
gloas: validate the decided vote with ssv-spec's GloasBeaconVote, and…
iurii-ssv Sep 24, 2026
2ebe147
review follow-ups: conclude a duty on errors after its decision; PTC …
iurii-ssv Sep 24, 2026
56916c7
review follow-ups: say an expired deadline concludes the committee du…
iurii-ssv Sep 24, 2026
8289e6d
ssv-spec: re-pin both go.mods to the latest #643 head, and drop the p…
iurii-ssv Sep 25, 2026
55c0dca
proposer preferences: hold the pre-fork emission until slot SLOTS_PER…
iurii-ssv Sep 25, 2026
5d42d8f
review follow-ups: name the pre-fork emission slot in the preferences…
iurii-ssv Sep 25, 2026
071b1f1
review follow-ups: use FirstSlotAtEpoch in the duties tests; keep the…
iurii-ssv Sep 25, 2026
638c1ff
gloas: alias go-eth2-client's beacon-API contents types instead of ke…
iurii-ssv Sep 25, 2026
713d014
proposer: release the reveal data on every failure that rules the rev…
iurii-ssv Sep 25, 2026
0497c02
validator: create the PTC-attester and preferences runners only when …
iurii-ssv Sep 26, 2026
8d4f83f
proposer: keep the reveal data on a late consensus message
iurii-ssv Sep 26, 2026
de3bb1e
qbft/roundtimer: shorten the proposer round budget to 1.5s (SIP-102) …
momosh-ssv Sep 28, 2026
0337cb4
runners: leave a duty open on an error after its decision, and report…
iurii-ssv Sep 29, 2026
d9befc6
proposer preferences: stash only the lookahead, and evict past slots …
iurii-ssv Sep 29, 2026
9809e71
PTC: schedule the slot's duties before the look-ahead fetch
iurii-ssv Sep 29, 2026
267ddaa
beacon config: stop the node when no client shares its fork schedule
iurii-ssv Sep 29, 2026
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
241 changes: 131 additions & 110 deletions beacon/goclient/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import (
"github.com/attestantio/go-eth2-client/api"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/electra"
eth2gloas "github.com/attestantio/go-eth2-client/spec/gloas"
"github.com/attestantio/go-eth2-client/spec/phase0"
ssz "github.com/ferranbt/fastssz"
"go.uber.org/zap"

"github.com/ssvlabs/ssv/networkconfig"
"github.com/ssvlabs/ssv/observability/log/fields"
)

// IsAggregator returns true if the validator is selected as an aggregator for the given
Expand Down Expand Up @@ -49,11 +52,11 @@ func (gc *GoClient) SubmitAggregateSelectionProof(
index phase0.ValidatorIndex,
slotSig []byte,
) (ssz.Marshaler, spec.DataVersion, error) {
// As specified in spec, an aggregator should wait until two thirds of the way through slot
// to broadcast the best aggregate to the global aggregate channel.
// As specified in spec, an aggregator waits until the aggregation deadline (see
// waitIntoSlot) to broadcast the best aggregate to the global aggregate channel.
// https://github.com/ethereum/consensus-specs/blob/v0.9.3/specs/validator/0_beacon-chain-validator.md#broadcast-aggregate
if err := gc.waitTwoThirdsIntoSlot(ctx, slot); err != nil {
return nil, 0, fmt.Errorf("wait for 2/3 of slot: %w", err)
if err := gc.waitIntoSlot(ctx, slot, 2); err != nil {
return nil, 0, fmt.Errorf("wait for aggregation deadline: %w", err)
}

va, _, err := gc.fetchVersionedAggregate(ctx, slot, committeeIndex)
Expand Down Expand Up @@ -82,32 +85,83 @@ func (gc *GoClient) SubmitSignedAggregateSelectionProof(
return nil
}

// computeAttestationDataRoot re-derives the attestation data root for the given slot/committee
// from this node's own view, used as a fallback when the cluster-attested root is unknown.
// waitIntoSlot waits until the given number of intervals into the slot has transpired
// (intervals * IntervalDuration after the start of the slot): intervals=1 is the attestation and
// sync-message deadline, intervals=2 the aggregation and contribution deadline. IntervalDuration
// is 1/3 of the slot before Gloas, 1/4 from Gloas on (SIP #94 §1).
func (gc *GoClient) waitIntoSlot(ctx context.Context, slot phase0.Slot, intervals int) error {
config := gc.getBeaconConfig()
finalTime := config.SlotStartTime(slot).Add(time.Duration(intervals) * config.IntervalDuration(slot))
wait := time.Until(finalTime)
if wait <= 0 {
return nil
}

select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(wait):
return nil
}
}

// computeAttestationDataRoot re-derives the attestation-data root for (slot, committeeIndex) from this
// node's own view. On Gloas it also returns the root under the opposite payload-status index — the one
// field of the re-derived data that can disagree with what the cluster decided, which the caller retries
// under (see fetchVersionedAggregate). Both roots come from the same fetch, so they are guaranteed to
// differ in nothing else; a nil altRoot means the slot is pre-Gloas and no such alternative exists.
func (gc *GoClient) computeAttestationDataRoot(
ctx context.Context,
slot phase0.Slot,
committeeIndex phase0.CommitteeIndex,
) (root [32]byte, err error) {
attData, _, err := gc.GetAttestationData(ctx, slot)
) (root [32]byte, altRoot *[32]byte, err error) {
cached, _, err := gc.GetAttestationData(ctx, slot)
if err != nil {
return root, fmt.Errorf("fetch attestation data: %w", err)
return root, nil, fmt.Errorf("fetch attestation data: %w", err)
}

// GetAttestationData hands back the pointer it caches for the slot, shared with every other caller
// (notably the committee runner). Work on a copy so the Index rewrites below can't write through.
attData := *cached

// Explicitly set Index field as beacon nodes may return inconsistent values.
// EIP-7549: For Electra and later, index must always be 0, pre-Electra uses committee index.
config := gc.getBeaconConfig()
dataVersion, _ := config.ForkAtEpoch(config.EstimatedEpochAtSlot(slot))
attData.Index = 0
if dataVersion < spec.DataVersionElectra {
attData.Index = committeeIndex
// EIP-7549: Electra+ uses Index=0; pre-Electra uses committee index. Gloas (EIP-7732) instead keeps
// the BN-supplied payload-status index (0=EMPTY/1=FULL) — it is part of the signed AttestationData
// (SIP #94 §2 aggregation path), so the aggregate must be fetched under it.
// Decide the fork from the requested duty slot, not attData.Slot — the latter is what the
// beacon node returned (the same source the comment above warns "may return inconsistent values"),
// whereas the aggregate is for our duty's slot, which is authoritative.
cfg := gc.getBeaconConfig()
isGloas := cfg.IsGloasAtSlot(slot)
// On Gloas the BN-supplied Index is the payload-status value and is kept exactly as returned.
if !isGloas {
version, _ := cfg.ForkAtEpoch(cfg.EstimatedEpochAtSlot(slot))
attData.Index = 0
if version < spec.DataVersionElectra {
attData.Index = committeeIndex
}
}

root, err = attData.HashTreeRoot()
if err != nil {
return root, fmt.Errorf("fetch attestation data root: %w", err)
return root, nil, fmt.Errorf("fetch attestation data root: %w", err)
}
if !isGloas {
return root, nil, nil
}

// The §2 payload-status index is a single bit (0=EMPTY / 1=FULL), so flipping it enumerates the
// only value the cluster could have decided other than the one our own beacon node reported.
if attData.Index == 0 {
attData.Index = 1
} else {
attData.Index = 0
}
flipped, err := attData.HashTreeRoot()
if err != nil {
return root, nil, fmt.Errorf("hash flipped attestation data root: %w", err)
}
return root, nil
return root, &flipped, nil
}

// fetchVersionedAggregate fetches the aggregate attestation for the given slot/committee,
Expand All @@ -118,39 +172,69 @@ func (gc *GoClient) computeAttestationDataRoot(
// cluster-decided value): the beacon node then holds at least our own attestation matching it.
// Re-deriving the data locally can yield a root nobody attested with, which the node answers
// with a 404 (no matching aggregate).
//
// A cache hit only guarantees that *some* beacon node accepted the attestation. The
// AggregateAttestation request below is routed to the single highest-scored client and does
// not fail over on 4xx, so it can still 404 if that node hasn't ingested the attestation yet
// (gossip backfill before the aggregation deadline makes this rare).
func (gc *GoClient) fetchVersionedAggregate(
ctx context.Context,
slot phase0.Slot,
committeeIndex phase0.CommitteeIndex,
) (*spec.VersionedAttestation, spec.DataVersion, error) {
root, found := gc.attestedDataRoot(slot, committeeIndex)
var altRoot *[32]byte
if !found {
// No record of our own attestation (it failed or hasn't landed yet) — fall back
// to re-deriving the root from this node's view of the slot.
var err error
root, err = gc.computeAttestationDataRoot(ctx, slot, committeeIndex)
root, altRoot, err = gc.computeAttestationDataRoot(ctx, slot, committeeIndex)
if err != nil {
return nil, DataVersionNil, err
}
}

aggDataReqStart := time.Now()
aggDataResp, err := gc.multiClient.AggregateAttestation(ctx, &api.AggregateAttestationOpts{
Slot: slot,
AttestationDataRoot: root,
CommitteeIndex: committeeIndex,
})
recordRequest(ctx, gc.log, "AggregateAttestation", gc.multiClient, http.MethodGet, true, time.Since(aggDataReqStart), err)
resp, err := gc.fetchAggregate(ctx, slot, committeeIndex, root)
if err != nil && altRoot != nil && isNotFound(err) {
// Only the re-derived root can disagree with the cluster: it carries *our* beacon node's
// Gloas payload-status index (SIP #94 §2), while the committee signed the QBFT-decided one.
// A 404 means no aggregate exists under our index, so try the only other value the bit can
// hold rather than silently missing the aggregate. Cheap: one extra GET on a path that has
// already failed, and unreachable from the common cache-hit path, whose root is by
// construction the decided one.
gc.log.Debug("retrying gloas aggregate fetch under the opposite payload-status index",
fields.Slot(slot),
zap.Uint64("committee_index", uint64(committeeIndex)))
resp, err = gc.fetchAggregate(ctx, slot, committeeIndex, *altRoot)
}
if err != nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("fetch aggregate attestation: %w", err), "AggregateAttestation")
}
if err := checkPtrResponse(aggDataResp, "aggregate attestation"); err != nil {
if err := checkPtrResponse(resp, "aggregate attestation"); err != nil {
return nil, DataVersionNil, errMultiClient(err, "AggregateAttestation")
}

return aggDataResp.Data, aggDataResp.Data.Version, nil
return resp.Data, resp.Data.Version, nil
}

// fetchAggregate performs the aggregate GET for one candidate attestation-data root.
func (gc *GoClient) fetchAggregate(
ctx context.Context,
slot phase0.Slot,
committeeIndex phase0.CommitteeIndex,
root phase0.Root,
) (*api.Response[*spec.VersionedAttestation], error) {
start := time.Now()
resp, err := gc.multiClient.AggregateAttestation(ctx, &api.AggregateAttestationOpts{
Slot: slot,
AttestationDataRoot: root,
CommitteeIndex: committeeIndex,
})
recordRequest(ctx, gc.log, "AggregateAttestation", gc.multiClient, http.MethodGet, true, time.Since(start), err)
return resp, err
}

// versionedAggregateToSSZ unwraps the fetched aggregate into its fork's attestation container.
func versionedAggregateToSSZ(va *spec.VersionedAttestation) (ssz.Marshaler, spec.DataVersion, error) {
switch va.Version {
case spec.DataVersionPhase0:
Expand Down Expand Up @@ -188,99 +272,36 @@ func versionedAggregateToSSZ(va *spec.VersionedAttestation) (ssz.Marshaler, spec
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return va.Fulu, va.Version, nil
case spec.DataVersionGloas:
if va.Gloas == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return va.Gloas, va.Version, nil
default:
return nil, DataVersionNil, errMultiClient(fmt.Errorf("unknown data version: %d", va.Version), "AggregateAttestation")
}
}

// versionedToAggregateAndProof wraps the fetched aggregate into its fork's AggregateAndProof. Gloas gets
// go-eth2-client's own container: it serializes like Electra's but merkleizes progressively, so the
// Electra container would hand the aggregator a different signing root (#3009).
func versionedToAggregateAndProof(
va *spec.VersionedAttestation,
index phase0.ValidatorIndex,
selectionProof phase0.BLSSignature,
) (ssz.Marshaler, spec.DataVersion, error) {
switch va.Version {
case spec.DataVersionPhase0:
if va.Phase0 == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &phase0.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Phase0,
SelectionProof: selectionProof,
}, va.Version, nil
case spec.DataVersionAltair:
if va.Altair == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &phase0.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Altair,
SelectionProof: selectionProof,
}, va.Version, nil
case spec.DataVersionBellatrix:
if va.Bellatrix == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &phase0.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Bellatrix,
SelectionProof: selectionProof,
}, va.Version, nil
case spec.DataVersionCapella:
if va.Capella == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &phase0.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Capella,
SelectionProof: selectionProof,
}, va.Version, nil
case spec.DataVersionDeneb:
if va.Deneb == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &phase0.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Deneb,
SelectionProof: selectionProof,
}, va.Version, nil
case spec.DataVersionElectra:
if va.Electra == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &electra.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Electra,
SelectionProof: selectionProof,
}, va.Version, nil
case spec.DataVersionFulu:
if va.Fulu == nil {
return nil, DataVersionNil, errMultiClient(fmt.Errorf("aggregate attestation %s data is nil", va.Version.String()), "AggregateAttestation")
}
return &electra.AggregateAndProof{
AggregatorIndex: index,
Aggregate: va.Fulu,
SelectionProof: selectionProof,
}, va.Version, nil
default:
return nil, DataVersionNil, fmt.Errorf("unknown data version: %d", va.Version)
}
}

// waitTwoThirdsIntoSlot waits until two-third of the slot has transpired (SECONDS_PER_SLOT * 2 / 3 seconds after the start of slot)
func (gc *GoClient) waitTwoThirdsIntoSlot(ctx context.Context, slot phase0.Slot) error {
config := gc.getBeaconConfig()
oneInterval := config.IntervalDuration()
finalTime := config.SlotStartTime(slot).Add(2 * oneInterval)
wait := time.Until(finalTime)
if wait <= 0 {
return nil
aggregate, version, err := versionedAggregateToSSZ(va)
if err != nil {
return nil, DataVersionNil, err
}

select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(wait):
return nil
switch aggregate := aggregate.(type) {
case *phase0.Attestation:
return &phase0.AggregateAndProof{AggregatorIndex: index, Aggregate: aggregate, SelectionProof: selectionProof}, version, nil
case *electra.Attestation:
return &electra.AggregateAndProof{AggregatorIndex: index, Aggregate: aggregate, SelectionProof: selectionProof}, version, nil
case *eth2gloas.Attestation:
return &eth2gloas.AggregateAndProof{AggregatorIndex: index, Aggregate: aggregate, SelectionProof: selectionProof}, version, nil
default:
return nil, DataVersionNil, fmt.Errorf("unexpected aggregate attestation type %T", aggregate)
}
}
Loading
Loading