Skip to content

Commit 01d667a

Browse files
committed
Fix attestation source divergence (leanSpec PR #506)
produce_attestation_data used store.latest_justified (global max across all forks) as the attestation source, while build_block filters by head_state.latest_justified. When a non-head fork block advances the store-wide justified past the head chain's justified, every attestation gets rejected during block building, producing blocks with 0 attestations and stalling justification/finalization indefinitely. Align with the 3sf-mini reference: derive the attestation source from the head state's justified checkpoint. Also handle the genesis edge case where the zero-hash root must be substituted with the real genesis block root, matching the existing logic in build_block. Update LEAN_SPEC_COMMIT_HASH to include the new spec test that exercises the divergence scenario.
1 parent 38229f5 commit 01d667a

File tree

2 files changed

+25
-5
lines changed

2 files changed

+25
-5
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ docker-build: ## 🐳 Build the Docker image
2424
-t ghcr.io/lambdaclass/ethlambda:$(DOCKER_TAG) .
2525
@echo
2626

27-
LEAN_SPEC_COMMIT_HASH:=d39d10195414921e979e2fdd43723d89cee13c8b
27+
LEAN_SPEC_COMMIT_HASH:=9c30436bf4c073d1a994f37a3241e83ef5a3ce6f
2828

2929
leanSpec:
3030
git clone https://github.com/leanEthereum/leanSpec.git --single-branch

crates/blockchain/src/store.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -740,12 +740,32 @@ pub fn get_attestation_target(store: &Store) -> Checkpoint {
740740
}
741741

742742
/// Produce attestation data for the given slot.
743+
///
744+
/// The attestation source comes from the head state's justified checkpoint,
745+
/// not the store-wide global max. This ensures voting aligns with the block
746+
/// builder's attestation filter (which also uses head state).
747+
///
748+
/// See: <https://github.com/leanEthereum/leanSpec/pull/506>
743749
pub fn produce_attestation_data(store: &Store, slot: u64) -> AttestationData {
744-
// Get the head block the validator sees for this slot
750+
let head_root = store.head();
751+
let head_state = store.get_state(&head_root).expect("head state exists");
752+
753+
// Derive source from head state's justified checkpoint.
754+
// At genesis the checkpoint root is H256::ZERO; substitute the real
755+
// genesis block root so attestation validation can look it up.
756+
let source = if head_state.latest_block_header.slot == 0 {
757+
Checkpoint {
758+
root: head_root,
759+
slot: head_state.latest_justified.slot,
760+
}
761+
} else {
762+
head_state.latest_justified
763+
};
764+
745765
let head_checkpoint = Checkpoint {
746-
root: store.head(),
766+
root: head_root,
747767
slot: store
748-
.get_block_header(&store.head())
768+
.get_block_header(&head_root)
749769
.expect("head block exists")
750770
.slot,
751771
};
@@ -758,7 +778,7 @@ pub fn produce_attestation_data(store: &Store, slot: u64) -> AttestationData {
758778
slot,
759779
head: head_checkpoint,
760780
target: target_checkpoint,
761-
source: store.latest_justified(),
781+
source,
762782
}
763783
}
764784

0 commit comments

Comments
 (0)