Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
abac19c
Add support for Heze block production
conache Jul 22, 2026
4b5074f
Use latest fork for state_by_root_pruned_from_fork_choice
conache Jul 22, 2026
602508c
Merge branch 'unstable' into heze-block-production
conache Jul 29, 2026
d676427
Merge branch 'unstable' into heze-block-production
conache Jul 30, 2026
6fa22e9
Merge branch 'unstable' into heze-block-production
conache Aug 1, 2026
b89aa8d
Merge branch 'unstable' into heze-block-production
conache Aug 3, 2026
d473556
Fix formatting
conache Aug 3, 2026
26e43d0
feat(types): add inclusion list committee derivation (EIP-7805)
rahulbarmann Jul 10, 2026
2476042
Merge branch 'unstable' of https://github.com/sigp/lighthouse into he…
conache Aug 4, 2026
e0bd77f
Merge branch 'unstable' into focil/il-committee-helpers
chong-he Aug 5, 2026
ab96ea5
Merge branch 'unstable' into heze-block-production
conache Aug 5, 2026
2d2aeaf
Address comments
rahulbarmann Aug 5, 2026
3cff380
Merge branch 'unstable' into focil/il-committee-helpers
rahulbarmann Aug 5, 2026
fd4782e
Merge branch 'unstable' into heze-block-production
conache Aug 6, 2026
49870eb
Merge branch 'unstable' into heze-block-production
conache Aug 8, 2026
d1c54ac
Merge branch 'unstable' into focil/il-committee-helpers
rahulbarmann Aug 10, 2026
0b0cdf7
Merge branch 'unstable' into heze-block-production
conache Aug 10, 2026
d8a3403
Merge branch 'unstable' into heze-block-production
conache Aug 10, 2026
5aaf89e
Address feedback from @eserilev
rahulbarmann Aug 10, 2026
a19248a
Merge branch 'unstable' into focil/il-committee-helpers
rahulbarmann Aug 10, 2026
db57fb7
Add clearer comments
conache Aug 10, 2026
fd951f1
Merge branch 'unstable' into heze-block-production
conache Aug 11, 2026
02250f9
Merge branch 'unstable' into heze-block-production
conache Aug 12, 2026
924aae4
Merge remote-tracking branch 'rahulbarmann/focil/il-committee-helpers…
conache Aug 12, 2026
25089c8
Add main il duties to the beacon chain
conache Jul 24, 2026
5f6a268
Wire up il duties endpoint
conache Jul 27, 2026
82506f4
Add endpoint tests
conache Jul 27, 2026
4357bbd
Drop requests with no indices
conache Jul 28, 2026
4d8a471
Add tests for behavior at the fork boundary
conache Jul 28, 2026
b2a56da
Accept requests for pre-Heze epochs
conache Aug 4, 2026
d77ec9a
Include formatting
conache Aug 4, 2026
23f2330
Correct comment
conache Aug 4, 2026
13214bf
Drop inclusion_list_committee_root
conache Aug 21, 2026
0c50701
Merge branch 'unstable' into il-duties-endpoint
conache Aug 24, 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
45 changes: 44 additions & 1 deletion beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ use crate::{
use bls::{PublicKey, PublicKeyBytes, Signature};
use eth2::beacon_response::ForkVersionedResponse;
use eth2::types::{
EventKind, PtcDuty, SseBlobSidecar, SseBlock, SseDataColumnSidecar,
EventKind, InclusionListDuty, PtcDuty, SseBlobSidecar, SseBlock, SseDataColumnSidecar,
SseExtendedPayloadAttributes, SseHead, SseHeadV2,
};
use execution_layer::{
Expand Down Expand Up @@ -1759,6 +1759,49 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok((duties, dependent_root))
}

/// Get inclusion list committee duties for validators at a given epoch ([New in Heze:EIP7805]).
pub fn compute_inclusion_list_duties(
&self,
state: &BeaconState<T::EthSpec>,
epoch: Epoch,
validator_indices: &[u64],
dependent_block_root: Hash256,
) -> Result<(Vec<Option<InclusionListDuty>>, Hash256), Error> {
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch)
.map_err(Error::IncorrectStateForAttestation)?;

let dependent_root =
state.attester_shuffling_decision_root(dependent_block_root, relative_epoch)?;

let mut assignments: HashMap<u64, Slot> = HashMap::new();
for slot in epoch.slot_iter(T::EthSpec::slots_per_epoch()) {
let committee = state.get_inclusion_list_committee(slot)?;
for validator_index in &committee {
assignments.entry(*validator_index).or_insert(slot);
}
}

let pubkey_cache = self.validator_pubkey_cache.read();

let duties = validator_indices
.iter()
.map(|&validator_index| {
let Some(&pubkey) = pubkey_cache.get_pubkey_bytes(validator_index as usize) else {
return None;
};
assignments
.get(&validator_index)
.map(|&slot| InclusionListDuty {
pubkey,
validator_index,
slot,
})
})
.collect::<Vec<_>>();

Ok((duties, dependent_root))
}

pub fn get_aggregated_attestation(
&self,
attestation: AttestationRef<T::EthSpec>,
Expand Down
46 changes: 32 additions & 14 deletions beacon_node/beacon_chain/src/block_production/gloas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ use tree_hash::TreeHash;
use types::consts::gloas::BUILDER_INDEX_SELF_BUILD;
use types::{
Address, Attestation, AttestationGloas, AttesterSlashing, AttesterSlashingGloas, BeaconBlock,
BeaconBlockBodyGloas, BeaconBlockGloas, BeaconState, BeaconStateError, BlobsList, BuilderIndex,
ChainSpec, Deposit, Eth1Data, EthSpec, ExecutionBlockHash, ExecutionPayloadBid,
ExecutionPayloadEnvelope, ExecutionPayloadGloas, ExecutionRequestsGloas, FullPayload, Graffiti,
Hash256, IndexedAttestation, KzgProofs, PayloadAttestation, ProposerSlashing, RelativeEpoch,
SignedBeaconBlock, SignedBlsToExecutionChange, SignedExecutionPayloadBid,
SignedExecutionPayloadEnvelope, SignedVoluntaryExit, Slot, SyncAggregate, Uint256, Withdrawal,
Withdrawals,
BeaconBlockBodyGloas, BeaconBlockBodyHeze, BeaconBlockGloas, BeaconBlockHeze, BeaconState,
BeaconStateError, BlobsList, BuilderIndex, ChainSpec, Deposit, Eth1Data, EthSpec,
ExecutionBlockHash, ExecutionPayloadBid, ExecutionPayloadEnvelope, ExecutionPayloadGloas,
ExecutionRequestsGloas, FullPayload, Graffiti, Hash256, IndexedAttestation, KzgProofs,
PayloadAttestation, ProposerSlashing, RelativeEpoch, SignedBeaconBlock,
SignedBlsToExecutionChange, SignedExecutionPayloadBid, SignedExecutionPayloadEnvelope,
SignedVoluntaryExit, Slot, SyncAggregate, Uint256, Withdrawal, Withdrawals,
};

use crate::payload_bid_verification::payload_bid_cache::BidParent;
Expand Down Expand Up @@ -650,13 +650,31 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
_phantom: PhantomData::<FullPayload<T::EthSpec>>,
},
}),
// TODO(heze): construct a `BeaconBlockHeze` here once Heze block production is
// wired up end-to-end (get_payload, envelope handling, etc).
BeaconState::Heze(_) => {
return Err(BlockProductionError::InvalidBlockVariant(
"Block production disabled for Heze".to_owned(),
));
}
BeaconState::Heze(_) => BeaconBlock::Heze(BeaconBlockHeze {
slot,
proposer_index,
parent_root,
state_root: Hash256::ZERO,
body: BeaconBlockBodyHeze {
randao_reveal,
eth1_data,
graffiti,
// The operation list lengths are bounded by the op pool packing limits above.
proposer_slashings: ProgressiveVariableList::from_iter(proposer_slashings),
attester_slashings: ProgressiveVariableList::from_iter(attester_slashings),
attestations: ProgressiveVariableList::from_iter(attestations),
deposits: ProgressiveVariableList::from_iter(deposits),
voluntary_exits: ProgressiveVariableList::from_iter(voluntary_exits),
sync_aggregate,
bls_to_execution_changes: ProgressiveVariableList::from_iter(
bls_to_execution_changes,
),
parent_execution_requests,
signed_execution_payload_bid,
payload_attestations: ProgressiveVariableList::from_iter(payload_attestations),
_phantom: PhantomData::<FullPayload<T::EthSpec>>,
},
}),
};

let signed_beacon_block = SignedBeaconBlock::from_block(
Expand Down
52 changes: 52 additions & 0 deletions beacon_node/beacon_chain/tests/store_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,58 @@ async fn proposer_lookahead_excludes_slashed_proposer_only_after_first_two_gloas
);
}

/// Ensure the harness can produce and import a chain across the Heze fork boundary
#[tokio::test]
async fn heze_block_production_across_boundary() {
let gloas_fork_epoch = Epoch::new(1);
let heze_fork_epoch = Epoch::new(2);
let mut spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
spec.gloas_fork_epoch = Some(gloas_fork_epoch);
spec.heze_fork_epoch = Some(heze_fork_epoch);

let db_path = tempdir().unwrap();
let store = get_store_generic(&db_path, Default::default(), spec.clone());
let validators_keypairs =
types::test_utils::generate_deterministic_keypairs(LOW_VALIDATOR_COUNT);
let harness = TestHarness::builder(E::default())
.spec(spec.into())
.keypairs(validators_keypairs)
.fresh_disk_store(store)
.mock_execution_layer()
.build();
let all_validators = harness.get_all_validators();

// Build through the first Heze epoch, ending at the first slot of the next epoch
let last_slot = (heze_fork_epoch + 1).start_slot(E::slots_per_epoch());
let slots: Vec<Slot> = (1..=last_slot.as_u64()).map(Into::into).collect();
let state = harness.get_current_state();
let (_, _, head_block_root, head_state) = harness
.add_attested_blocks_at_slots(state, &slots, &all_validators)
.await;

assert_eq!(head_state.current_epoch(), heze_fork_epoch + 1);

let head_block_root: Hash256 = head_block_root.into();
let head_block = harness
.chain
.store
.get_blinded_block(&head_block_root)
.unwrap()
.expect("head block should be stored");
assert!(
matches!(head_block, SignedBeaconBlock::Heze(_)),
"the head block should be a Heze block"
);
assert!(
harness
.chain
.get_payload_envelope(&head_block_root)
.unwrap()
.is_some(),
"the Heze block's execution payload envelope should be stored"
);
}

// Ensure blocks from abandoned forks are pruned from the Hot DB
#[tokio::test]
async fn prunes_abandoned_fork_between_two_finalized_checkpoints() {
Expand Down
12 changes: 4 additions & 8 deletions beacon_node/execution_layer/src/engine_api/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,9 @@ impl HttpJsonRpc {
let params = json!([JsonPayloadIdRequest::from(payload_id)]);

match fork_name {
ForkName::Gloas => {
// TODO(heze): deliberately reusing the Gloas response containers while the Heze
// payload is identical to the Gloas one. Switch to the Heze types if it diverges
ForkName::Gloas | ForkName::Heze => {
let response: JsonGetPayloadResponseGloas<E> = self
.rpc_request(
ENGINE_GET_PAYLOAD_V6,
Expand All @@ -1073,7 +1075,6 @@ impl HttpJsonRpc {
.try_into()
.map_err(Error::BadResponse)
}
// TODO(heze): add a Heze arm once Heze payload retrieval is implemented.
_ => Err(Error::UnsupportedForkVariant(format!(
"called get_payload_v6 with {}",
fork_name
Expand Down Expand Up @@ -1444,18 +1445,13 @@ impl HttpJsonRpc {
Err(Error::RequiredMethodUnsupported("engine_getPayloadv5"))
}
}
ForkName::Gloas => {
ForkName::Gloas | ForkName::Heze => {
if engine_capabilities.get_payload_v6 {
self.get_payload_v6(fork_name, payload_id).await
} else {
Err(Error::RequiredMethodUnsupported("engine_getPayloadV6"))
}
}
// TODO(heze): implement the Heze getPayload path once the engine API for Heze
// is specified.
ForkName::Heze => Err(Error::UnsupportedForkVariant(
"getPayload not implemented for Heze".to_string(),
)),
ForkName::Base | ForkName::Altair => Err(Error::UnsupportedForkVariant(format!(
"called get_payload with {}",
fork_name
Expand Down
4 changes: 0 additions & 4 deletions beacon_node/execution_layer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,6 @@ impl<E: EthSpec> From<GetPayloadResponseGloas<E>> for BlockProposalContentsGloas
}
}

// TODO(heze): add a `BlockProposalContentsHeze` here once Heze block production is wired up.

pub enum BlockProposalContents<E: EthSpec, Payload: AbstractExecPayload<E>> {
Payload {
payload: Payload,
Expand Down Expand Up @@ -952,8 +950,6 @@ impl<E: EthSpec> ExecutionLayer<E> {
Ok(payload_response.into())
}

// TODO(heze): add a `get_payload_heze` here once Heze block production is wired up.

/// Maps to the `engine_getPayload` JSON-RPC call.
///
/// However, it will attempt to call `self.prepare_payload` if it cannot find an existing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,13 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
.push(ProgressiveVariableList::<u8>::new(tx.into()));
}
}
ExecutionPayload::Heze(payload) => {
for tx in Vec::from(transactions) {
payload
.transactions
.push(ProgressiveVariableList::<u8>::new(tx.into()));
}
}
_ => {
for tx in Vec::from(transactions) {
execution_payload
Expand Down
Loading
Loading