Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Electra alpha8 spec updates #6496

Open
wants to merge 8 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion beacon_node/execution_layer/src/engine_api/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ impl HttpJsonRpc {
new_payload_request_electra.versioned_hashes,
new_payload_request_electra.parent_beacon_block_root,
new_payload_request_electra
.execution_requests_list
.execution_requests
.get_execution_requests_list(),
]);

Expand Down
7 changes: 4 additions & 3 deletions beacon_node/execution_layer/src/engine_api/json_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,8 @@ impl<E: EthSpec> TryFrom<JsonExecutionRequests> for ExecutionRequests<E> {

for (i, request) in value.0.into_iter().enumerate() {
// hex string
let decoded_bytes = hex::decode(request).map_err(|e| format!("Invalid hex {:?}", e))?;
let decoded_bytes = hex::decode(request.strip_prefix("0x").unwrap_or(&request))
.map_err(|e| format!("Invalid hex {:?}", e))?;
match RequestPrefix::from_prefix(i as u8) {
Some(RequestPrefix::Deposit) => {
requests.deposits = DepositRequests::<E>::from_ssz_bytes(&decoded_bytes)
Expand Down Expand Up @@ -431,7 +432,7 @@ pub struct JsonGetPayloadResponse<E: EthSpec> {
#[superstruct(only(V3, V4))]
pub should_override_builder: bool,
#[superstruct(only(V4))]
pub requests: JsonExecutionRequests,
pub execution_requests: JsonExecutionRequests,
}

impl<E: EthSpec> TryFrom<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
Expand Down Expand Up @@ -464,7 +465,7 @@ impl<E: EthSpec> TryFrom<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
block_value: response.block_value,
blobs_bundle: response.blobs_bundle.into(),
should_override_builder: response.should_override_builder,
requests: response.requests.try_into()?,
requests: response.execution_requests.try_into()?,
}))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub struct NewPayloadRequest<'block, E: EthSpec> {
#[superstruct(only(Deneb, Electra))]
pub parent_beacon_block_root: Hash256,
#[superstruct(only(Electra))]
pub execution_requests_list: &'block ExecutionRequests<E>,
pub execution_requests: &'block ExecutionRequests<E>,
}

impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
Expand Down Expand Up @@ -185,7 +185,7 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
.map(kzg_commitment_to_versioned_hash)
.collect(),
parent_beacon_block_root: block_ref.parent_root,
execution_requests_list: &block_ref.body.execution_requests,
execution_requests: &block_ref.body.execution_requests,
})),
}
}
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/execution_layer/src/test_utils/handle_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ pub async fn handle_rpc<E: EthSpec>(
.into(),
should_override_builder: false,
// TODO(electra): add EL requests in mock el
requests: Default::default(),
execution_requests: Default::default(),
})
.unwrap()
}
Expand Down
6 changes: 3 additions & 3 deletions beacon_node/store/src/partial_beacon_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ where
pub earliest_consolidation_epoch: Epoch,

#[superstruct(only(Electra))]
pub pending_balance_deposits: List<PendingBalanceDeposit, E::PendingBalanceDepositsLimit>,
pub pending_deposits: List<PendingDeposit, E::PendingDepositsLimit>,
#[superstruct(only(Electra))]
pub pending_partial_withdrawals:
List<PendingPartialWithdrawal, E::PendingPartialWithdrawalsLimit>,
Expand Down Expand Up @@ -290,7 +290,7 @@ impl<E: EthSpec> PartialBeaconState<E> {
earliest_exit_epoch,
consolidation_balance_to_consume,
earliest_consolidation_epoch,
pending_balance_deposits,
pending_deposits,
pending_partial_withdrawals,
pending_consolidations
],
Expand Down Expand Up @@ -563,7 +563,7 @@ impl<E: EthSpec> TryInto<BeaconState<E>> for PartialBeaconState<E> {
earliest_exit_epoch,
consolidation_balance_to_consume,
earliest_consolidation_epoch,
pending_balance_deposits,
pending_deposits,
pending_partial_withdrawals,
pending_consolidations
],
Expand Down
3 changes: 3 additions & 0 deletions consensus/state_processing/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ pub fn initialize_beacon_state_from_eth1<E: EthSpec>(
let post = upgrade_state_to_electra(&mut state, Epoch::new(0), Epoch::new(0), spec)?;
state = post;

// TODO(electra): do we need to iterate over an empty pending_deposits list here and increase balance?
// in accordance with `initialize_beacon_state_from_eth1` function from the spec
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because we have already populated the validators array in line 42 and 45


// Remove intermediate Deneb fork from `state.fork`.
state.fork_mut().previous_version = spec.electra_fork_version;

Expand Down
4 changes: 3 additions & 1 deletion consensus/state_processing/src/per_block_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
// Consume pending partial withdrawals
let partial_withdrawals_count =
if let Ok(partial_withdrawals) = state.pending_partial_withdrawals() {
let mut partial_withdrawals_count = 0;
for withdrawal in partial_withdrawals {
if withdrawal.withdrawable_epoch > epoch
|| withdrawals.len() == spec.max_pending_partials_per_withdrawals_sweep as usize
Expand Down Expand Up @@ -546,8 +547,9 @@ pub fn get_expected_withdrawals<E: EthSpec>(
});
withdrawal_index.safe_add_assign(1)?;
}
partial_withdrawals_count.safe_add_assign(1)?;
}
Some(withdrawals.len())
Some(partial_withdrawals_count)
} else {
None
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::per_block_processing::errors::{BlockProcessingError, IntoWithIndex};
use crate::VerifySignatures;
use types::consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR};
use types::typenum::U33;
use types::validator::is_compounding_withdrawal_credential;

pub fn process_operations<E: EthSpec, Payload: AbstractExecPayload<E>>(
state: &mut BeaconState<E>,
Expand Down Expand Up @@ -450,59 +449,69 @@ pub fn apply_deposit<E: EthSpec>(

if let Some(index) = validator_index {
// [Modified in Electra:EIP7251]
if let Ok(pending_balance_deposits) = state.pending_balance_deposits_mut() {
pending_balance_deposits.push(PendingBalanceDeposit { index, amount })?;

let validator = state
.validators()
.get(index as usize)
.ok_or(BeaconStateError::UnknownValidator(index as usize))?;

if is_compounding_withdrawal_credential(deposit_data.withdrawal_credentials, spec)
&& validator.has_eth1_withdrawal_credential(spec)
&& is_valid_deposit_signature(&deposit_data, spec).is_ok()
{
state.switch_to_compounding_validator(index as usize, spec)?;
}
if let Ok(pending_deposits) = state.pending_deposits_mut() {
pending_deposits.push(PendingDeposit {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
amount,
signature: deposit_data.signature,
slot: spec.genesis_slot, // Use `genesis_slot` to distinguish from a pending deposit request
})?;
} else {
// Update the existing validator balance.
increase_balance(state, index as usize, amount)?;
}
} else {
}
// New validator
else {
// The signature should be checked for new validators. Return early for a bad
// signature.
if is_valid_deposit_signature(&deposit_data, spec).is_err() {
return Ok(());
}

let new_validator_index = state.validators().len();

// [Modified in Electra:EIP7251]
let (effective_balance, state_balance) = if state.fork_name_unchecked() >= ForkName::Electra
{
(0, 0)
if state.fork_name_unchecked() >= ForkName::Electra {
ethDreamer marked this conversation as resolved.
Show resolved Hide resolved
let amount = 0;
// Create a new validator.
let mut validator = Validator {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
activation_eligibility_epoch: spec.far_future_epoch,
activation_epoch: spec.far_future_epoch,
exit_epoch: spec.far_future_epoch,
withdrawable_epoch: spec.far_future_epoch,
effective_balance: 0,
slashed: false,
};
let max_effective_balance =
validator.get_max_effective_balance(spec, state.fork_name_unchecked());
validator.effective_balance = std::cmp::min(
amount.safe_sub(amount.safe_rem(spec.effective_balance_increment)?)?,
max_effective_balance,
Comment on lines +487 to +491
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is unnecessary, amount is hardcoded to 0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not unnecessary (this is logic for setting effective balance which is in the spec), but it IS a bug because amount has been hardcoded to zero. I think this bit of code is getting more and more difficult to manage as we add forks so I've created a PR to simplify this whole piece of code:

#6515

it needs a review to ensure we're compliant with the spec across all forks. But if we merge that into this PR it'll simplify this a ton.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm this isn't a bug because the spec set amount to 0 much earlier as well.. but the simplification should still be done.

);

state.validators_mut().push(validator)?;
// state balances is set to 0 in electra
state.balances_mut().push(0)?;
} else {
(
std::cmp::min(
let validator = Validator {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
activation_eligibility_epoch: spec.far_future_epoch,
activation_epoch: spec.far_future_epoch,
exit_epoch: spec.far_future_epoch,
withdrawable_epoch: spec.far_future_epoch,
effective_balance: std::cmp::min(
amount.safe_sub(amount.safe_rem(spec.effective_balance_increment)?)?,
spec.max_effective_balance,
),
amount,
)
};
// Create a new validator.
let validator = Validator {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
activation_eligibility_epoch: spec.far_future_epoch,
activation_epoch: spec.far_future_epoch,
exit_epoch: spec.far_future_epoch,
withdrawable_epoch: spec.far_future_epoch,
effective_balance,
slashed: false,
slashed: false,
};
state.validators_mut().push(validator)?;
// state balances is set to 0 in electra
state.balances_mut().push(amount)?;
};
state.validators_mut().push(validator)?;
state.balances_mut().push(state_balance)?;

// Altair or later initializations.
if let Ok(previous_epoch_participation) = state.previous_epoch_participation_mut() {
Expand All @@ -516,10 +525,13 @@ pub fn apply_deposit<E: EthSpec>(
}

// [New in Electra:EIP7251]
if let Ok(pending_balance_deposits) = state.pending_balance_deposits_mut() {
pending_balance_deposits.push(PendingBalanceDeposit {
index: new_validator_index as u64,
if let Ok(pending_deposits) = state.pending_deposits_mut() {
pending_deposits.push(PendingDeposit {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
amount,
signature: deposit_data.signature,
slot: spec.genesis_slot, // Use `genesis_slot` to distinguish from a pending deposit request
})?;
}
}
Expand Down Expand Up @@ -633,13 +645,18 @@ pub fn process_deposit_requests<E: EthSpec>(
if state.deposit_requests_start_index()? == spec.unset_deposit_requests_start_index {
*state.deposit_requests_start_index_mut()? = request.index
}
let deposit_data = DepositData {
pubkey: request.pubkey,
withdrawal_credentials: request.withdrawal_credentials,
amount: request.amount,
signature: request.signature.clone().into(),
};
apply_deposit(state, deposit_data, None, false, spec)?
let slot = state.slot();

// [New in Electra:EIP7251]
if let Ok(pending_deposits) = state.pending_deposits_mut() {
pending_deposits.push(PendingDeposit {
pubkey: request.pubkey,
withdrawal_credentials: request.withdrawal_credentials,
amount: request.amount,
signature: request.signature.clone(),
slot,
})?;
}
}

Ok(())
Expand All @@ -658,11 +675,73 @@ pub fn process_consolidation_requests<E: EthSpec>(
Ok(())
}

fn is_valid_switch_to_compounding_request<E: EthSpec>(
state: &BeaconState<E>,
consolidation_request: &ConsolidationRequest,
spec: &ChainSpec,
) -> Result<bool, Error> {
// Switch to compounding requires source and target be equal
if consolidation_request.source_pubkey != consolidation_request.target_pubkey {
return Ok(false);
}

// Verify pubkey exists
let Some(source_index) = state
.pubkey_cache()
.get(&consolidation_request.source_pubkey)
else {
// source validator doesn't exist
return Ok(false);
};

let source_validator = state.get_validator(source_index)?;
Comment on lines +688 to +697
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This last line is the only reason this function returns a Result<bool> rather than a bool. But it shouldn't ever return an error if the line above returned a pubkey. But since you need to fetch this validator here anyway, just combine them so this function has no reliance on the pubkey cache:

let source_validator = match state.get_validator(source_index) {
    Ok(v) => v,
    Err(_) => return false, 
};

then you can refactor this whole function to just return a bool

// Verify the source withdrawal credentials
if let Some(withdrawal_address) = source_validator.get_eth1_withdrawal_credential(spec) {
if withdrawal_address != consolidation_request.source_address {
return Ok(false);
}
} else {
// Source doen't have execution withdrawal credentials
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Source doen't have execution withdrawal credentials
// Source doesn't have eth1 withdrawal credentials

the distinction here is important

return Ok(false);
}

// Verify the source is active
let current_epoch = state.current_epoch();
if !source_validator.is_active_at(current_epoch) {
return Ok(false);
}
// Verify exits for source has not been initiated
if source_validator.exit_epoch != spec.far_future_epoch {
return Ok(false);
}

Ok(true)
}

pub fn process_consolidation_request<E: EthSpec>(
state: &mut BeaconState<E>,
consolidation_request: &ConsolidationRequest,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
dbg!("here");
pawanjay176 marked this conversation as resolved.
Show resolved Hide resolved
if is_valid_switch_to_compounding_request(state, consolidation_request, spec)? {
let Some(source_index) = state
.pubkey_cache()
.get(&consolidation_request.source_pubkey)
else {
// source validator doesn't exist. This is unreachable as `is_valid_switch_to_compounding_request`
// will return false in that case.
Comment on lines +732 to +733
Copy link
Member

@ethDreamer ethDreamer Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you implement the function above, the previous function no longer relies on the pubkey cache, so technically this could get hit if the cache is stale.. in which case I think we'd probably wanna build it and check again?

return Ok(());
};
state.switch_to_compounding_validator(source_index, spec)?;
return Ok(());
}

// Verify that source != target, so a consolidation cannot be used as an exit.
if consolidation_request.source_pubkey == consolidation_request.target_pubkey {
return Ok(());
}

// If the pending consolidations queue is full, consolidation requests are ignored
if state.pending_consolidations()?.len() == E::PendingConsolidationsLimit::to_usize() {
return Ok(());
Expand All @@ -686,10 +765,6 @@ pub fn process_consolidation_request<E: EthSpec>(
// target validator doesn't exist
return Ok(());
};
// Verify that source != target, so a consolidation cannot be used as an exit.
if source_index == target_index {
return Ok(());
}

let source_validator = state.get_validator(source_index)?;
// Verify the source withdrawal credentials
Expand Down Expand Up @@ -736,5 +811,10 @@ pub fn process_consolidation_request<E: EthSpec>(
target_index: target_index as u64,
})?;

let target_validator = state.get_validator(target_index)?;
// Churn any target excess active balance of target and raise its max
if target_validator.has_eth1_withdrawal_credential(spec) {
state.switch_to_compounding_validator(target_index, spec)?;
}
Ok(())
}
Loading
Loading