mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 12:47:05 +00:00
gloas genesis fix
This commit is contained in:
@@ -46,8 +46,8 @@ use tracing::{debug, error, info, warn};
|
||||
use tree_hash::TreeHash;
|
||||
use types::data::CustodyIndex;
|
||||
use types::{
|
||||
BeaconState, BlobSidecarList, ChainSpec, ColumnIndex, DataColumnSidecarList, Epoch, EthSpec,
|
||||
Hash256, SignedBeaconBlock, Slot,
|
||||
BeaconBlock, BeaconState, BlobSidecarList, ChainSpec, ColumnIndex, DataColumnSidecarList,
|
||||
Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
/// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing
|
||||
@@ -1178,9 +1178,21 @@ fn make_genesis_block<E: EthSpec>(
|
||||
genesis_state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<SignedBeaconBlock<E>, String> {
|
||||
// For Gloas, genesis_block() populates the bid in the block body. However, if
|
||||
// the genesis state was produced by an external tool (e.g. ethereum-genesis-generator),
|
||||
// its latest_block_header.body_root may correspond to an empty block. In that case,
|
||||
// use an empty block so the stored block root matches what fork choice derives from
|
||||
// the state's latest_block_header.
|
||||
let mut block = genesis_block(genesis_state, spec)
|
||||
.map_err(|e| format!("Error building genesis block: {:?}", e))?;
|
||||
|
||||
let state_body_root = genesis_state.latest_block_header().body_root;
|
||||
if state_body_root != block.body_root()
|
||||
&& state_body_root == BeaconBlock::<E>::empty(spec).body_root()
|
||||
{
|
||||
block = BeaconBlock::empty(spec);
|
||||
}
|
||||
|
||||
*block.state_root_mut() = genesis_state
|
||||
.update_tree_hash_cache()
|
||||
.map_err(|e| format!("Error hashing genesis state: {:?}", e))?;
|
||||
|
||||
@@ -64,7 +64,7 @@ impl GossipVerifiedProposerPreferences {
|
||||
ctx: &GossipVerificationContext<'_, T>,
|
||||
) -> Result<Self, ProposerPreferencesError> {
|
||||
let proposal_slot = signed_preferences.message.proposal_slot;
|
||||
let checkpoint_root = signed_preferences.message.checkpoint_root;
|
||||
let dependent_root = signed_preferences.message.dependent_root;
|
||||
let validator_index = signed_preferences.message.validator_index;
|
||||
let cached_head = ctx.canonical_head.cached_head();
|
||||
let current_slot = ctx
|
||||
@@ -75,7 +75,7 @@ impl GossipVerifiedProposerPreferences {
|
||||
|
||||
if ctx
|
||||
.gossip_verified_proposer_preferences_cache
|
||||
.get_seen_validator(&proposal_slot, checkpoint_root, validator_index)
|
||||
.get_seen_validator(&proposal_slot, dependent_root, validator_index)
|
||||
{
|
||||
return Err(ProposerPreferencesError::AlreadySeen {
|
||||
validator_index,
|
||||
@@ -163,7 +163,7 @@ mod tests {
|
||||
|
||||
fn make_preferences(proposal_slot: Slot, validator_index: u64) -> ProposerPreferences {
|
||||
ProposerPreferences {
|
||||
checkpoint_root: types::Hash256::ZERO,
|
||||
dependent_root: types::Hash256::ZERO,
|
||||
proposal_slot,
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
|
||||
@@ -37,24 +37,24 @@ impl GossipVerifiedProposerPreferenceCache {
|
||||
pub fn get_seen_validator(
|
||||
&self,
|
||||
slot: &Slot,
|
||||
checkpoint_root: Hash256,
|
||||
dependent_root: Hash256,
|
||||
validator_index: u64,
|
||||
) -> bool {
|
||||
self.seen
|
||||
.read()
|
||||
.get(slot)
|
||||
.is_some_and(|seen| seen.contains(&(checkpoint_root, validator_index)))
|
||||
.is_some_and(|seen| seen.contains(&(dependent_root, validator_index)))
|
||||
}
|
||||
|
||||
pub fn insert_seen_validator(&self, preferences: &GossipVerifiedProposerPreferences) {
|
||||
let slot = preferences.signed_preferences.message.proposal_slot;
|
||||
let checkpoint_root = preferences.signed_preferences.message.checkpoint_root;
|
||||
let dependent_root = preferences.signed_preferences.message.dependent_root;
|
||||
let validator_index = preferences.signed_preferences.message.validator_index;
|
||||
self.seen
|
||||
.write()
|
||||
.entry(slot)
|
||||
.or_default()
|
||||
.insert((checkpoint_root, validator_index));
|
||||
.insert((dependent_root, validator_index));
|
||||
}
|
||||
|
||||
pub fn prune(&self, current_slot: Slot) {
|
||||
|
||||
@@ -256,6 +256,41 @@ fn validator_index_out_of_bounds() {
|
||||
));
|
||||
}
|
||||
|
||||
/// Same (slot, validator_index) but different dependent_root should NOT be deduplicated.
|
||||
#[test]
|
||||
fn same_validator_different_dependent_root_not_deduplicated() {
|
||||
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
|
||||
return;
|
||||
}
|
||||
let ctx = TestContext::new();
|
||||
let slot = Slot::new(1);
|
||||
|
||||
let verified_a = GossipVerifiedProposerPreferences {
|
||||
signed_preferences: Arc::new(SignedProposerPreferences {
|
||||
message: ProposerPreferences {
|
||||
proposal_slot: slot,
|
||||
validator_index: 42,
|
||||
dependent_root: Hash256::repeat_byte(0xaa),
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
}),
|
||||
};
|
||||
ctx.preferences_cache.insert_seen_validator(&verified_a);
|
||||
|
||||
// Different dependent_root — should not be seen.
|
||||
assert!(
|
||||
!ctx.preferences_cache
|
||||
.get_seen_validator(&slot, Hash256::repeat_byte(0xbb), 42,)
|
||||
);
|
||||
// Same dependent_root — should be seen.
|
||||
assert!(
|
||||
ctx.preferences_cache
|
||||
.get_seen_validator(&slot, Hash256::repeat_byte(0xaa), 42,)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(gloas) add successful proposer preferences check once we have proposer preferences signing logic
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1026,6 +1026,28 @@ where
|
||||
assert_ne!(slot, 0, "can't produce a block at slot 0");
|
||||
assert!(slot >= state.slot());
|
||||
|
||||
// For Gloas, blinded and full blocks are structurally identical (no payload in body).
|
||||
// Produce via the Gloas path and convert to blinded.
|
||||
if self.spec.fork_name_at_slot::<E>(slot).gloas_enabled() {
|
||||
let (block_contents, _envelope, pending_state) =
|
||||
Box::pin(self.make_block_with_envelope(state, slot)).await;
|
||||
let (signed_block, _blobs) = block_contents;
|
||||
let signed_blinded = signed_block.clone_as_blinded();
|
||||
let (mut blinded_block, _signature) = signed_blinded.deconstruct();
|
||||
block_modifier(&mut blinded_block);
|
||||
let proposer_index = pending_state
|
||||
.get_beacon_proposer_index(slot, &self.spec)
|
||||
.unwrap();
|
||||
// Re-sign after modification.
|
||||
let signed_blinded = blinded_block.sign(
|
||||
&self.validator_keypairs[proposer_index].sk,
|
||||
&pending_state.fork(),
|
||||
pending_state.genesis_validators_root(),
|
||||
&self.spec,
|
||||
);
|
||||
return (signed_blinded, pending_state);
|
||||
}
|
||||
|
||||
complete_state_advance(&mut state, None, slot, &self.spec)
|
||||
.expect("should be able to advance state to slot");
|
||||
|
||||
@@ -1248,6 +1270,21 @@ where
|
||||
assert_ne!(slot, 0, "can't produce a block at slot 0");
|
||||
assert!(slot >= state.slot());
|
||||
|
||||
// For Gloas forks, delegate to make_block_with_envelope which uses the
|
||||
// Gloas-specific block production path, and return the pre-state.
|
||||
if self.spec.fork_name_at_slot::<E>(slot).gloas_enabled() {
|
||||
let pre_state = {
|
||||
let mut s = state.clone();
|
||||
complete_state_advance(&mut s, None, slot, &self.spec)
|
||||
.expect("should be able to advance state to slot");
|
||||
s.build_caches(&self.spec).expect("should build caches");
|
||||
s
|
||||
};
|
||||
let (block_contents, _envelope, _state) =
|
||||
Box::pin(self.make_block_with_envelope(state, slot)).await;
|
||||
return (block_contents, pre_state);
|
||||
}
|
||||
|
||||
complete_state_advance(&mut state, None, slot, &self.spec)
|
||||
.expect("should be able to advance state to slot");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user