Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
6b03e84
refactor(beacon): name every fork at each fork gate
MegaRedHand Sep 24, 2026
10661ac
build: bump libssz for in-place ProgressiveList writes
MegaRedHand Sep 24, 2026
14dfe49
feat(ssz-tree): add a tree-backed ProgressiveList
MegaRedHand Sep 24, 2026
75995bd
test(beacon): run the v1.7.0-beta.2 spec fixtures
MegaRedHand Sep 24, 2026
dd6ad0d
refactor(beacon): read and write the registry by element
MegaRedHand Sep 24, 2026
8443fd9
refactor(beacon): name every fork at the gates bci added
MegaRedHand Sep 24, 2026
eaefff0
feat(types): add NewPayloadRequest and the partial data column contai…
MegaRedHand Sep 24, 2026
417e750
test(beacon): run the gossip vectors from the main fixture tree
MegaRedHand Sep 24, 2026
e3f39cd
feat(types): add the gloas preset, constants and containers
MegaRedHand Sep 24, 2026
5aab222
feat(beacon): add the gloas fork variant and config
MegaRedHand Sep 25, 2026
a6923f3
fix(beacon): stop retiring the eth1-bridge deposit path a second time…
MegaRedHand Sep 25, 2026
baa549c
fix(beacon): port the v1.7.0-beta.2 fork-choice changes
MegaRedHand Sep 25, 2026
c5c0f06
test(beacon): run the gloas ssz_static vectors
MegaRedHand Sep 25, 2026
2436bea
fix(beacon): use the minimal preset's consolidation churn quotient
MegaRedHand Sep 25, 2026
633b294
feat(beacon): add the gloas accessors, predicates and churn
MegaRedHand Sep 25, 2026
ae9a05b
feat(beacon): upgrade a fulu state to gloas
MegaRedHand Sep 25, 2026
07c1bb6
feat(beacon): add gloas epoch processing
MegaRedHand Sep 25, 2026
5b396db
feat(beacon): add gloas withdrawals, bids and parent payloads
MegaRedHand Sep 28, 2026
0d9ff37
feat(beacon): add the gloas fork choice nodes, weight and head
MegaRedHand Sep 28, 2026
a66c0c6
feat(beacon): add gloas block operations
MegaRedHand Sep 28, 2026
8200261
feat(beacon): carry the gloas fork choice across the fulu boundary
MegaRedHand Sep 28, 2026
435480e
feat(beacon): add the gloas state transition
MegaRedHand Sep 28, 2026
bf7cf12
feat(beacon): add the gloas fork-choice handlers
MegaRedHand Sep 28, 2026
69d9bd7
test(beacon): run the gloas fork-choice suite
MegaRedHand Sep 28, 2026
908e697
test(beacon): run every gloas suite
MegaRedHand Sep 28, 2026
e698f22
refactor(beacon): name gloas in every fork match
MegaRedHand Sep 29, 2026
9534e6a
docs: describe gloas in the beacon stf reference
MegaRedHand Sep 29, 2026
b9c9dd0
perf(beacon): one bottom-up head computation for every fork
MegaRedHand Sep 29, 2026
af123e1
fix(beacon): refuse gloas where the node cannot answer it
MegaRedHand Sep 30, 2026
f31ef79
refactor(beacon): share what gloas leaves unmodified
MegaRedHand Sep 30, 2026
5102c81
docs(beacon): correct stale gloas comments
MegaRedHand Sep 30, 2026
4137aa2
test(ssz-tree): cover progressive subtrees that span leaves
MegaRedHand Sep 30, 2026
1d667c4
docs(beacon): refresh the lib test counts
MegaRedHand Sep 30, 2026
225461a
feat(beacon): validate gloas data column sidecars
MegaRedHand Sep 30, 2026
beb3b83
refactor(beacon): split the gloas column rule and test its deviations
MegaRedHand Sep 30, 2026
d91c6e5
feat(beacon): add Platåberget as a built-in network
MegaRedHand Sep 30, 2026
2dd8f5e
feat(p2p): cross fork digests without a restart
MegaRedHand Sep 30, 2026
4f5893f
fix(p2p): tighten the fork-digest switch
MegaRedHand Sep 30, 2026
29d0981
test(p2p): make the fork-digest tests prove what they claim
MegaRedHand Sep 30, 2026
539e9f6
fix(beacon): time fork-choice events at arrival, not at the slot tick
MegaRedHand Sep 30, 2026
02eba80
feat(beacon): persist verified envelopes and block timeliness
MegaRedHand Sep 30, 2026
d7cc62b
fix(beacon): seed the PTC vote vectors a resumed store expects
MegaRedHand Sep 30, 2026
1ed3ec4
test(beacon): cover the live head walk on a resumed store
MegaRedHand Sep 30, 2026
f1f62da
feat(beacon): gloas beacon_block gossip rules
MegaRedHand Sep 30, 2026
3b661ca
fix(beacon): match gloas block gossip limits to the spec
MegaRedHand Sep 30, 2026
484947f
feat(beacon): receive, store and replay gloas data column sidecars
MegaRedHand Sep 30, 2026
07e4b60
refactor(beacon): move column parking into its own module
MegaRedHand Sep 30, 2026
7144c0a
fix(beacon): bound gloas column parking
MegaRedHand Sep 30, 2026
2ec76c2
feat(beacon): gloas attestation and aggregate gossip
MegaRedHand Sep 30, 2026
fe3a1fe
fix(beacon): bound gloas aggregation bits and judge pre-gloas payload…
MegaRedHand Oct 1, 2026
9edbcdf
feat(engine): newPayloadV5 for gloas envelopes
MegaRedHand Oct 1, 2026
f0ecbf1
fix(engine): refuse a mismatched envelope and quiet the V5 warning
MegaRedHand Oct 1, 2026
6692839
feat(beacon): gloas execution_payload and payload_attestation_message…
MegaRedHand Oct 1, 2026
0304c1c
fix(beacon): gate queued envelopes on their shape
MegaRedHand Oct 1, 2026
ce29681
feat(p2p): serve execution payload envelopes over req/resp
MegaRedHand Oct 1, 2026
1580cd5
refactor(p2p): move the envelope req/resp handlers into their own module
MegaRedHand Oct 1, 2026
4af0326
fix(p2p): pin MAX_REQUEST_PAYLOADS and record the head-envelope devia…
MegaRedHand Oct 1, 2026
894f793
feat(engine): forkchoiceUpdatedV4 for gloas
MegaRedHand Oct 1, 2026
1311ce4
feat(beacon): import gloas blocks and their execution payload envelopes
MegaRedHand Oct 1, 2026
2bfb08a
test(beacon): gate the fixture-driven envelope tests
MegaRedHand Oct 1, 2026
7738f2b
fix(beacon): keep an honest envelope recoverable
MegaRedHand Oct 1, 2026
61f9fa4
fix(beacon): re-ask for missing envelopes and bound the unmatched queue
MegaRedHand Oct 1, 2026
8deb2ca
feat(beacon): follow execution_payload and payload_attestation_messag…
MegaRedHand Oct 1, 2026
2bef861
fix(beacon): apply verified payload votes without re-verifying them
MegaRedHand Oct 1, 2026
071cd52
feat(beacon): follow gloas
MegaRedHand Oct 1, 2026
4e831ae
fix(beacon): pair the head's payload status with its root
MegaRedHand Oct 1, 2026
462ed1b
feat(p2p): fetch execution payload envelopes by root and by range
MegaRedHand Oct 1, 2026
95e6b68
refactor(p2p): move the envelope fetch client into its own module
MegaRedHand Oct 1, 2026
d93ec1f
fix(p2p): keep range envelopes in batch order
MegaRedHand Oct 1, 2026
0784bdb
fix(beacon): reconcile the merged gloas branches
MegaRedHand Oct 1, 2026
f4ab3b5
feat(rpc): serve gloas execution payload envelopes
MegaRedHand Oct 1, 2026
92f39d1
feat(beacon): fetch a gloas anchor's envelope during checkpoint sync
MegaRedHand Oct 1, 2026
b5861c1
fix(rpc): judge gloas optimism read-only and cautiously
MegaRedHand Oct 1, 2026
39bcb7c
fix(beacon): forward envelopes whose block state is not cached yet
MegaRedHand Oct 1, 2026
d5fa1fe
feat(beacon): validate gloas payloads with newPayloadV5 and forkchoic…
MegaRedHand Oct 1, 2026
029e8fb
docs(beacon): record how the execution client judges gloas payloads
MegaRedHand Oct 1, 2026
281acea
fix(beacon): follow the execution chain, and check consensus before a…
MegaRedHand Oct 1, 2026
4a8b244
fix(beacon): scope hash-mismatch verdicts to the envelope and probe a…
MegaRedHand Oct 1, 2026
1ab5abe
docs(beacon): describe the gloas follower as it runs
MegaRedHand Oct 1, 2026
1ab6dee
fix(beacon): ask for envelopes of held blocks at once and keep those …
MegaRedHand Oct 1, 2026
f1b0e33
fix(beacon): bound envelopes held for stored blocks
MegaRedHand Oct 1, 2026
d1ef2fe
fix(beacon): let the finalized block's own gloas columns through
MegaRedHand Oct 2, 2026
ee15eeb
chore(beacon): lay out the interfaces gloas validator duties will share
MegaRedHand Oct 5, 2026
bc02069
feat(validator): pick duty deadlines by the fork of the slot
MegaRedHand Oct 5, 2026
ba8a336
feat(validator): propose gloas blocks and reveal the self-built envelope
MegaRedHand Oct 5, 2026
a4f6294
feat(validator): attest and aggregate at gloas slots
MegaRedHand Oct 5, 2026
e75554b
feat(validator): vote on payload timeliness from the committee
MegaRedHand Oct 5, 2026
1ee2374
feat(beacon): serve duties and attestation data at gloas epochs
MegaRedHand Oct 5, 2026
5f5b94a
feat(beacon): accept gloas attestations and aggregates through the po…
MegaRedHand Oct 5, 2026
450bc0a
feat(beacon): key the payload attestation pool by slot, data and vali…
MegaRedHand Oct 5, 2026
3e06974
feat(beacon): share one payload attestation pool across p2p and the B…
MegaRedHand Oct 5, 2026
7e9d3d9
feat(beacon): gossip payload attestations a validator client signed
MegaRedHand Oct 5, 2026
4341234
feat(beacon): record when a block's envelope arrived for payload_present
MegaRedHand Oct 5, 2026
6a32f5d
feat(beacon): serve PTC duties, payload attestation data and the pool
MegaRedHand Oct 5, 2026
0648151
feat(engine): build gloas payloads through forkchoiceUpdatedV4 and ge…
MegaRedHand Oct 5, 2026
85a54ba
feat(beacon): assemble self-built gloas blocks and their payload enve…
MegaRedHand Oct 5, 2026
34d4935
feat(beacon): serve gloas block production and envelope publication
MegaRedHand Oct 5, 2026
868e013
feat(beacon): gossip a self-built payload envelope with its data columns
MegaRedHand Oct 5, 2026
390c3be
docs(beacon): document gloas validator duties
MegaRedHand Oct 5, 2026
6513e08
test(beacon): port the validator-client tests to the gloas-aware clie…
MegaRedHand Oct 5, 2026
5535deb
refactor(beacon): expose the per-slot payload attestation aggregation
MegaRedHand Oct 5, 2026
ef556a8
fix(beacon): list the payload attestation pool as aggregates
MegaRedHand Oct 5, 2026
ddcd935
test(beacon): drive the validator client end to end at a gloas slot
MegaRedHand Oct 5, 2026
8dc1640
docs(beacon): the payload attestation pool is listed as aggregates
MegaRedHand Oct 5, 2026
55adf1b
fix(rpc): accept blob-carrying blocks and envelopes over 2 MiB
MegaRedHand Oct 5, 2026
b8e9588
Serve GET /eth/v1/beacon/states/{state_id}/fork, GET /eth/v1/config/d…
pablodeymo Sep 29, 2026
b958364
feat(types): read the sync committee containers from JSON
MegaRedHand Oct 5, 2026
ea0e89b
feat(validator): beacon node API, signing, clock and duties for sync …
MegaRedHand Oct 6, 2026
19eeb32
feat(validator): serve sync committee duties in the slot loop
MegaRedHand Oct 6, 2026
4037229
feat(beacon): sync committee helpers and a pool for block production
MegaRedHand Oct 6, 2026
75085db
feat(beacon): validate sync committee gossip
MegaRedHand Oct 6, 2026
e3cf165
feat(beacon): pack sync committee votes and wire the pool through the…
MegaRedHand Oct 6, 2026
0d6c4c8
feat(rpc): serve the sync committee endpoints and pack the pooled agg…
MegaRedHand Oct 6, 2026
11cfdbf
feat(p2p): validate, relay and pool sync committee gossip
MegaRedHand Oct 6, 2026
729416c
test(rpc): drive the sync committee validator client against the real…
MegaRedHand Oct 6, 2026
07f0357
test(state-transition): skip the unselected-aggregator case on minimal
MegaRedHand Oct 7, 2026
b87637e
Merge main into feat/beacon-gloas
MegaRedHand Oct 22, 2026
2c53521
Merge feat/beacon-gloas into feat/beacon-plataberget-devnet
MegaRedHand Oct 22, 2026
9d8bcbd
Merge feat/beacon-plataberget-devnet into feat/beacon-gloas-live
MegaRedHand Oct 22, 2026
be417a3
Merge feat/beacon-gloas-live into feat/beacon-gloas-validator-duties
MegaRedHand Oct 22, 2026
c72427f
Merge feat/beacon-gloas-validator-duties into feat/beacon-sync-committee
MegaRedHand Oct 22, 2026
931a704
Merge main into feat/beacon-gloas
MegaRedHand Oct 22, 2026
a59bbd3
Merge feat/beacon-gloas into feat/beacon-plataberget-devnet
MegaRedHand Oct 22, 2026
42b467b
Merge feat/beacon-plataberget-devnet into feat/beacon-gloas-live
MegaRedHand Oct 22, 2026
6632055
fix(beacon): skip a proposer boost outside the justified subtree
MegaRedHand Oct 22, 2026
2bf2bc6
Merge feat/beacon-gloas into feat/beacon-plataberget-devnet
MegaRedHand Oct 22, 2026
87499d4
Merge feat/beacon-plataberget-devnet into feat/beacon-gloas-live
MegaRedHand Oct 22, 2026
7864f96
Merge feat/beacon-gloas-live into feat/beacon-gloas-validator-duties
MegaRedHand Oct 22, 2026
9211ac7
Merge feat/beacon-gloas-validator-duties into feat/beacon-sync-committee
MegaRedHand Oct 22, 2026
cdfd3c6
Merge main into feat/beacon-gloas
MegaRedHand Oct 22, 2026
cc1e28a
Merge feat/beacon-gloas into feat/beacon-plataberget-devnet
MegaRedHand Oct 22, 2026
e00690a
Merge feat/beacon-plataberget-devnet into feat/beacon-gloas-live
MegaRedHand Oct 22, 2026
f1684ff
Merge feat/beacon-gloas-live into feat/beacon-gloas-validator-duties
MegaRedHand Oct 22, 2026
0c59bc7
Merge feat/beacon-gloas-validator-duties into feat/beacon-sync-committee
MegaRedHand Oct 22, 2026
e8f0e05
chore(beacon): refresh Platåberget's bootnodes from upstream
MegaRedHand Oct 23, 2026
214543c
Merge feat/beacon-plataberget-devnet into feat/beacon-gloas-live
MegaRedHand Oct 23, 2026
c34b730
Merge feat/beacon-gloas-live into feat/beacon-gloas-validator-duties
MegaRedHand Oct 23, 2026
b2a463a
Merge feat/beacon-gloas-validator-duties into feat/beacon-sync-committee
MegaRedHand Oct 23, 2026
51ef2e3
Merge main into feat/beacon-sync-committee
MegaRedHand Oct 26, 2026
e2f16a8
Merge branch 'main' into feat/beacon-sync-committee
MegaRedHand Oct 26, 2026
4430bce
fix conflicts
MegaRedHand Oct 26, 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
18 changes: 16 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,13 @@ actual_slot = finalized_slot + 1 + relative_index
- Beacon wire: `validate_messages()` is on, so every beacon message waits for a verdict (~4.2s before gossipsub's cache evicts it). Rules in `state_transition::beacon::gossip` (cheap half inline, stateful half on a bounded `spawn_blocking` task); plumbing in `p2p/src/beacon/verdict.rs`. Lean gossip still auto-forwards
- Data columns: every check runs in p2p. A column gossip did not accept (`Queue`/`Overloaded`), every fetched column, and parked columns replayed after their parent imports go through `column::chain_checks` in `p2p/src/beacon/column_checks.rs`. The chain actor stores what it gets unchecked; only debug builds re-run `chain_checks` there
- Beacon subscribes seven global topics plus two node-id-derived subnet families: custody
columns and backbone attestation subnets. Gloas digests add two more topics,
columns and backbone attestation subnets. One of the seven is
`sync_committee_contribution_and_proof`; the four `sync_committee_{0..3}` subnets are
joined on demand only (a validator client's `sync_committee_subscriptions`, held
until its `until_epoch`, under every held digest) and advertised in MetaData
`syncnets`, never the ENR. Messages and contributions validate in p2p on their own
permit pool (`gossip::sync_committee`, `p2p/src/beacon/sync_committee.rs`), and
accepted ones go into the shared `SyncCommitteePool`. Gloas digests add two more topics,
`execution_payload` and `payload_attestation_message` (`BeaconTopics::for_fork`; earlier
digests never carry them). Gloas has its own rules for `beacon_block`,
`data_column_sidecar` (fork enum `DataColumnSidecar`, fork from the topic's digest),
Expand Down Expand Up @@ -805,7 +811,15 @@ transitions are in `ethlambda-types`, per the section above. Nothing above
`Eth-Blob-Data-Included: true`, both inside the proposal's attester-offset
budget; an envelope failure is logged and counted, not a failed proposal),
attest at `ATTESTATION_DUE_BPS_GLOAS`, aggregate at `AGGREGATE_DUE_BPS_GLOAS`,
then the PTC vote at `PAYLOAD_ATTESTATION_DUE_BPS`. `SlotClock` picks the
then the PTC vote at `PAYLOAD_ATTESTATION_DUE_BPS`. Sync committee duties
(`SyncCommitteeService`, `crates/validator/src/sync_committee.rs`) run at every fork:
one message per validator over the head root at `SYNC_MESSAGE_DUE_BPS(_GLOAS)`
(refused while the head is optimistic), then contributions for each selected
(validator, subnet) pair at `CONTRIBUTION_DUE_BPS(_GLOAS)`, with subscriptions
re-sent every epoch for the current and next period. The node pools the messages
and a block at slot N packs only `(N-1, parent_root)`, replaced by the empty
aggregate when `verified_sync_aggregate` fails (see `docs/spec_deviations.md`).
`SlotClock` picks the
offsets by each slot's own fork, and `/eth/v1/config/spec` supplies them. It
still keeps no slashing-protection record; PTC votes have an in-memory
`(validator, slot)` dedup only, and envelopes are unguarded.
Expand Down
14 changes: 6 additions & 8 deletions bin/ethlambda/src/beacon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,12 @@ pub fn wire_params(
"Backboning attestation subnets"
);

// Say plainly what is still advertised without being backed by behavior,
// so a running node never implies more than it does. Storing and serving
// the custodied columns logged above is no longer in that gap, and neither
// is the attestation subnet backbone; sync committee subnet subscription,
// and publishing, still are.
warn!(
"Advertising cgc={custody_group_count} while subscribing to no sync committee \
subnet, and publishing nothing"
// Say plainly what the node does about sync committees, so a running node
// never implies more than it does: its subnets are joined only when a
// validator client asks, and `syncnets` advertises exactly those.
info!(
"Advertising cgc={custody_group_count}; sync committee subnets are joined only on a \
validator client's request"
);

Ok(BeaconWireParams {
Expand Down
6 changes: 6 additions & 0 deletions bin/ethlambda/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,13 +702,18 @@ async fn run_node(options: Options) -> eyre::Result<()> {
// by block production and `GET .../pool/payload_attestations`.
let payload_attestation_pool =
ethlambda_state_transition::beacon::payload_attestation_pool::SharedPayloadAttestationPool::default();
// Filled by gossip and the Beacon API's sync committee endpoints, read by
// block production and the contribution endpoint.
let sync_committee_pool =
ethlambda_state_transition::beacon::sync_committee_pool::SharedSyncCommitteePool::default();
let p2p = P2P::spawn(
built,
setup.store.clone(),
setup.node_names,
discovery,
attestation_pool.clone(),
payload_attestation_pool.clone(),
sync_committee_pool.clone(),
)
.await
.wrap_err("failed to start discv5 discovery")?;
Expand Down Expand Up @@ -750,6 +755,7 @@ async fn run_node(options: Options) -> eyre::Result<()> {
p2p: rpc_p2p,
attestation_pool: attestation_pool.clone(),
payload_attestation_pool: payload_attestation_pool.clone(),
sync_committee_pool: sync_committee_pool.clone(),
custody_columns: rpc_custody_columns,
engine: rpc_engine,
events: rpc_events,
Expand Down
140 changes: 135 additions & 5 deletions crates/blockchain/state_transition/src/beacon/block_production.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ use super::bls;
use super::config::Config;
use super::error::{Error, Result, verify};
use super::helpers::accessors::{
CommitteeCache, get_beacon_proposer_index, get_block_root, get_current_epoch,
get_previous_epoch, get_randao_mix,
CommitteeCache, get_beacon_proposer_index, get_block_root, get_block_root_at_slot,
get_current_epoch, get_domain, get_previous_epoch, get_randao_mix,
};
use super::helpers::electra::{
get_attesting_indices, get_indexed_attestation, is_valid_indexed_attestation,
};
use super::helpers::misc::compute_signing_root;
use super::lean_boundary::lean_state_unreachable;
use super::stf::{self, ExecutionEngine};

Expand Down Expand Up @@ -108,6 +109,52 @@ pub fn empty_sync_aggregate() -> SyncAggregate {
}
}

/// `candidate` when it passes exactly the check `process_sync_aggregate` will
/// hold it to, else [`empty_sync_aggregate`].
///
/// `state` is the block's pre-state advanced to the block's slot. The
/// participants are `current_sync_committee`'s members by bits, signing the
/// block root at `state.slot - 1` under `DOMAIN_SYNC_COMMITTEE` at that slot's
/// epoch. A pooled aggregate can fail this when the proposer's parent is not
/// the root the committee signed, or when the head moved across a period: it
/// then costs the rewards, never the block.
pub fn verified_sync_aggregate(state: &BeaconState, candidate: SyncAggregate) -> SyncAggregate {
let Ok((committee, _)) = state.sync_committees() else {
return empty_sync_aggregate();
};
let participants: Vec<_> = committee
.pubkeys
.iter()
.enumerate()
.filter(|(position, _)| {
candidate
.sync_committee_bits
.get(*position)
.unwrap_or(false)
})
.map(|(_, pubkey)| *pubkey)
.collect();
let previous_slot = state.slot().saturating_sub(1);
let Ok(block_root) = get_block_root_at_slot(state, previous_slot) else {
return empty_sync_aggregate();
};
let domain = get_domain(
state,
constants::DOMAIN_SYNC_COMMITTEE,
Some(compute_epoch_at_slot(previous_slot)),
);
let signing_root = compute_signing_root(block_root, domain);
if bls::eth_fast_aggregate_verify(
&participants,
signing_root,
&candidate.sync_committee_signature,
) {
candidate
} else {
empty_sync_aggregate()
}
}

/// The inverse of `get_execution_requests_list`: the execution client's
/// EIP-7685 request list back into the block body's `ExecutionRequests`.
///
Expand Down Expand Up @@ -296,15 +343,18 @@ pub struct BlockInputs {
pub execution_payload: ExecutionPayload,
pub blob_kzg_commitments: Vec<KzgCommitment>,
pub execution_requests: ExecutionRequests,
/// The block's sync aggregate: [`empty_sync_aggregate`], or what
/// [`verified_sync_aggregate`] vouched for.
pub sync_aggregate: SyncAggregate,
}

/// The unsigned block for the slot `state` has been advanced to, with its
/// state root computed.
///
/// The body votes the state's own `eth1_data` and carries no deposits (the
/// deposit contract's log has been replaced by EIP-6110's requests), no
/// slashings, exits or credential changes (this node pools none), and an empty
/// sync aggregate. The block is run through `process_block` on a copy of
/// slashings, exits or credential changes (this node pools none), and the
/// sync aggregate it is given. The block is run through `process_block` on a copy of
/// `state` with an execution engine that accepts the payload, which is the
/// node's own execution client's payload; that run is also what rejects a body
/// the network would, before anything is signed.
Expand All @@ -321,7 +371,7 @@ pub fn assemble_block(
.attestations
.try_into()
.map_err(|_| Error::SpecAssert("len(attestations) <= MAX_ATTESTATIONS_ELECTRA"))?,
sync_aggregate: empty_sync_aggregate(),
sync_aggregate: inputs.sync_aggregate,
execution_payload: inputs.execution_payload,
blob_kzg_commitments: inputs
.blob_kzg_commitments
Expand Down Expand Up @@ -431,6 +481,7 @@ mod tests {
fn an_assembled_block_passes_process_block_and_names_its_post_state() {
let state = state_to_build_on();
let inputs = BlockInputs {
sync_aggregate: empty_sync_aggregate(),
randao_reveal: randao_reveal(&state),
graffiti: Bytes32::repeat_byte(7),
attestations: Vec::new(),
Expand Down Expand Up @@ -469,6 +520,7 @@ mod tests {
let mut payload = payload_for(&state);
payload.parent_hash = ExecutionBlockHash::repeat_byte(9);
let inputs = BlockInputs {
sync_aggregate: empty_sync_aggregate(),
randao_reveal: randao_reveal(&state),
graffiti: Bytes32::ZERO,
attestations: Vec::new(),
Expand Down Expand Up @@ -654,6 +706,84 @@ mod tests {
assert!(parse_execution_requests(&[vec![0x7f, 0]]).is_err());
}

/// A pool holding messages from `positions` of the committee, signed the way
/// `process_sync_aggregate` will check them (over the block root at
/// `state.slot - 1`, under the state's own domain), plus that root.
fn pooled_aggregate(
state: &BeaconState,
signed_root: Option<Root>,
positions: &[usize],
) -> (SyncAggregate, Root) {
use crate::beacon::sync_committee_pool::SyncCommitteePool;
use ethlambda_types::beacon::containers::altair::{
SYNC_SUBCOMMITTEE_SIZE, SyncCommitteeMessage,
};

let previous_slot = state.slot() - 1;
let parent_root = get_block_root_at_slot(state, previous_slot).unwrap();
let root = signed_root.unwrap_or(parent_root);
let domain = get_domain(
state,
constants::DOMAIN_SYNC_COMMITTEE,
Some(compute_epoch_at_slot(previous_slot)),
);
let signing_root = compute_signing_root(root, domain);
let (committee, _) = state.sync_committees().unwrap();
let mut pool = SyncCommitteePool::default();
for &position in positions {
let pubkey = committee.pubkeys[position];
let index = (0..64)
.find(|&index| state.validator(index).unwrap().pubkey == pubkey)
.expect("the committee is drawn from the registry");
let message = SyncCommitteeMessage {
slot: previous_slot,
beacon_block_root: parent_root,
validator_index: index,
signature: sign_for(index as usize, signing_root),
};
let seats = [(
(position / SYNC_SUBCOMMITTEE_SIZE) as u64,
position % SYNC_SUBCOMMITTEE_SIZE,
)];
pool.insert_message(&message, &seats);
}
(
pool.sync_aggregate(previous_slot, parent_root)
.expect("something was pooled"),
parent_root,
)
}

#[test]
fn a_pooled_sync_aggregate_passes_assemble_block() {
let state = state_to_build_on();
let (candidate, _) = pooled_aggregate(&state, None, &[0, 1, 5]);
let verified = verified_sync_aggregate(&state, candidate.clone());
assert_eq!(verified, candidate);
assert!(verified.sync_committee_bits.count_ones() >= 3);
let inputs = BlockInputs {
randao_reveal: randao_reveal(&state),
graffiti: Bytes32::repeat_byte(7),
attestations: Vec::new(),
execution_payload: payload_for(&state),
blob_kzg_commitments: Vec::new(),
execution_requests: ExecutionRequests::default(),
sync_aggregate: verified.clone(),
};
let block = assemble_block(&state, inputs, &Config::mainnet()).unwrap();
assert_eq!(block.body.sync_aggregate, verified);
}

#[test]
fn a_sync_aggregate_over_the_wrong_root_is_replaced_by_the_empty_one() {
let state = state_to_build_on();
let (wrong, _) = pooled_aggregate(&state, Some(Root::repeat_byte(1)), &[0, 1]);
assert_eq!(
verified_sync_aggregate(&state, wrong),
empty_sync_aggregate()
);
}

#[test]
fn the_empty_sync_aggregate_signs_with_the_point_at_infinity() {
let aggregate = empty_sync_aggregate();
Expand Down
Loading
Loading