-
Notifications
You must be signed in to change notification settings - Fork 801
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement block_rewards API (per-validator reward) (#3907)
## Issue Addressed [#3661](#3661) ## Proposed Changes `/eth/v1/beacon/rewards/blocks/{block_id}` ``` { "execution_optimistic": false, "finalized": false, "data": { "proposer_index": "123", "total": "123", "attestations": "123", "sync_aggregate": "123", "proposer_slashings": "123", "attester_slashings": "123" } } ``` The issue contains the implementation of three per-validator reward APIs: * `sync_committee_rewards` * [`attestation_rewards`](#3822) * `block_rewards` This PR only implements the `block_rewards`. The endpoints can be viewed in the Ethereum Beacon nodes API browser: [https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Rewards](https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Rewards) ## Additional Info The implementation of [consensus client reward APIs](https://github.com/eth-protocol-fellows/cohort-three/blob/master/projects/project-ideas.md#consensus-client-reward-apis) is part of the [EPF](https://github.com/eth-protocol-fellows/cohort-three). Co-authored-by: kevinbogner <kevbogner@gmail.com> Co-authored-by: navie <naviechan@gmail.com>
- Loading branch information
1 parent
4d07e40
commit e442385
Showing
10 changed files
with
335 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes}; | ||
use eth2::lighthouse::StandardBlockReward; | ||
use operation_pool::RewardCache; | ||
use safe_arith::SafeArith; | ||
use slog::error; | ||
use state_processing::{ | ||
common::{ | ||
altair, get_attestation_participation_flag_indices, get_attesting_indices_from_state, | ||
}, | ||
per_block_processing::{ | ||
altair::sync_committee::compute_sync_aggregate_rewards, get_slashable_indices, | ||
}, | ||
}; | ||
use store::{ | ||
consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR}, | ||
RelativeEpoch, | ||
}; | ||
use types::{BeaconBlockRef, BeaconState, BeaconStateError, ExecPayload, Hash256}; | ||
|
||
type BeaconBlockSubRewardValue = u64; | ||
|
||
impl<T: BeaconChainTypes> BeaconChain<T> { | ||
pub fn compute_beacon_block_reward<Payload: ExecPayload<T::EthSpec>>( | ||
&self, | ||
block: BeaconBlockRef<'_, T::EthSpec, Payload>, | ||
block_root: Hash256, | ||
state: &mut BeaconState<T::EthSpec>, | ||
) -> Result<StandardBlockReward, BeaconChainError> { | ||
if block.slot() != state.slot() { | ||
return Err(BeaconChainError::BlockRewardSlotError); | ||
} | ||
|
||
state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?; | ||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; | ||
|
||
let proposer_index = block.proposer_index(); | ||
|
||
let sync_aggregate_reward = | ||
self.compute_beacon_block_sync_aggregate_reward(block, state)?; | ||
|
||
let proposer_slashing_reward = self | ||
.compute_beacon_block_proposer_slashing_reward(block, state) | ||
.map_err(|e| { | ||
error!( | ||
self.log, | ||
"Error calculating proposer slashing reward"; | ||
"error" => ?e | ||
); | ||
BeaconChainError::BlockRewardError | ||
})?; | ||
|
||
let attester_slashing_reward = self | ||
.compute_beacon_block_attester_slashing_reward(block, state) | ||
.map_err(|e| { | ||
error!( | ||
self.log, | ||
"Error calculating attester slashing reward"; | ||
"error" => ?e | ||
); | ||
BeaconChainError::BlockRewardError | ||
})?; | ||
|
||
let block_attestation_reward = if let BeaconState::Base(_) = state { | ||
self.compute_beacon_block_attestation_reward_base(block, block_root, state) | ||
.map_err(|e| { | ||
error!( | ||
self.log, | ||
"Error calculating base block attestation reward"; | ||
"error" => ?e | ||
); | ||
BeaconChainError::BlockRewardAttestationError | ||
})? | ||
} else { | ||
self.compute_beacon_block_attestation_reward_altair(block, state) | ||
.map_err(|e| { | ||
error!( | ||
self.log, | ||
"Error calculating altair block attestation reward"; | ||
"error" => ?e | ||
); | ||
BeaconChainError::BlockRewardAttestationError | ||
})? | ||
}; | ||
|
||
let total_reward = sync_aggregate_reward | ||
.safe_add(proposer_slashing_reward)? | ||
.safe_add(attester_slashing_reward)? | ||
.safe_add(block_attestation_reward)?; | ||
|
||
Ok(StandardBlockReward { | ||
proposer_index, | ||
total: total_reward, | ||
attestations: block_attestation_reward, | ||
sync_aggregate: sync_aggregate_reward, | ||
proposer_slashings: proposer_slashing_reward, | ||
attester_slashings: attester_slashing_reward, | ||
}) | ||
} | ||
|
||
fn compute_beacon_block_sync_aggregate_reward<Payload: ExecPayload<T::EthSpec>>( | ||
&self, | ||
block: BeaconBlockRef<'_, T::EthSpec, Payload>, | ||
state: &BeaconState<T::EthSpec>, | ||
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> { | ||
if let Ok(sync_aggregate) = block.body().sync_aggregate() { | ||
let (_, proposer_reward_per_bit) = compute_sync_aggregate_rewards(state, &self.spec) | ||
.map_err(|_| BeaconChainError::BlockRewardSyncError)?; | ||
Ok(sync_aggregate.sync_committee_bits.num_set_bits() as u64 * proposer_reward_per_bit) | ||
} else { | ||
Ok(0) | ||
} | ||
} | ||
|
||
fn compute_beacon_block_proposer_slashing_reward<Payload: ExecPayload<T::EthSpec>>( | ||
&self, | ||
block: BeaconBlockRef<'_, T::EthSpec, Payload>, | ||
state: &BeaconState<T::EthSpec>, | ||
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> { | ||
let mut proposer_slashing_reward = 0; | ||
|
||
let proposer_slashings = block.body().proposer_slashings(); | ||
|
||
for proposer_slashing in proposer_slashings { | ||
proposer_slashing_reward.safe_add_assign( | ||
state | ||
.get_validator(proposer_slashing.proposer_index() as usize)? | ||
.effective_balance | ||
.safe_div(self.spec.whistleblower_reward_quotient)?, | ||
)?; | ||
} | ||
|
||
Ok(proposer_slashing_reward) | ||
} | ||
|
||
fn compute_beacon_block_attester_slashing_reward<Payload: ExecPayload<T::EthSpec>>( | ||
&self, | ||
block: BeaconBlockRef<'_, T::EthSpec, Payload>, | ||
state: &BeaconState<T::EthSpec>, | ||
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> { | ||
let mut attester_slashing_reward = 0; | ||
|
||
let attester_slashings = block.body().attester_slashings(); | ||
|
||
for attester_slashing in attester_slashings { | ||
for attester_index in get_slashable_indices(state, attester_slashing)? { | ||
attester_slashing_reward.safe_add_assign( | ||
state | ||
.get_validator(attester_index as usize)? | ||
.effective_balance | ||
.safe_div(self.spec.whistleblower_reward_quotient)?, | ||
)?; | ||
} | ||
} | ||
|
||
Ok(attester_slashing_reward) | ||
} | ||
|
||
fn compute_beacon_block_attestation_reward_base<Payload: ExecPayload<T::EthSpec>>( | ||
&self, | ||
block: BeaconBlockRef<'_, T::EthSpec, Payload>, | ||
block_root: Hash256, | ||
state: &BeaconState<T::EthSpec>, | ||
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> { | ||
// Call compute_block_reward in the base case | ||
// Since base does not have sync aggregate, we only grab attesation portion of the returned | ||
// value | ||
let mut reward_cache = RewardCache::default(); | ||
let block_attestation_reward = self | ||
.compute_block_reward(block, block_root, state, &mut reward_cache, true)? | ||
.attestation_rewards | ||
.total; | ||
|
||
Ok(block_attestation_reward) | ||
} | ||
|
||
fn compute_beacon_block_attestation_reward_altair<Payload: ExecPayload<T::EthSpec>>( | ||
&self, | ||
block: BeaconBlockRef<'_, T::EthSpec, Payload>, | ||
state: &mut BeaconState<T::EthSpec>, | ||
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> { | ||
let total_active_balance = state.get_total_active_balance()?; | ||
let base_reward_per_increment = | ||
altair::BaseRewardPerIncrement::new(total_active_balance, &self.spec)?; | ||
|
||
let mut total_proposer_reward = 0; | ||
|
||
let proposer_reward_denominator = WEIGHT_DENOMINATOR | ||
.safe_sub(PROPOSER_WEIGHT)? | ||
.safe_mul(WEIGHT_DENOMINATOR)? | ||
.safe_div(PROPOSER_WEIGHT)?; | ||
|
||
for attestation in block.body().attestations() { | ||
let data = &attestation.data; | ||
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64(); | ||
let participation_flag_indices = get_attestation_participation_flag_indices( | ||
state, | ||
data, | ||
inclusion_delay, | ||
&self.spec, | ||
)?; | ||
|
||
let attesting_indices = get_attesting_indices_from_state(state, attestation)?; | ||
|
||
let mut proposer_reward_numerator = 0; | ||
for index in attesting_indices { | ||
let index = index as usize; | ||
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() { | ||
let epoch_participation = | ||
state.get_epoch_participation_mut(data.target.epoch)?; | ||
let validator_participation = epoch_participation | ||
.get_mut(index) | ||
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?; | ||
|
||
if participation_flag_indices.contains(&flag_index) | ||
&& !validator_participation.has_flag(flag_index)? | ||
{ | ||
validator_participation.add_flag(flag_index)?; | ||
proposer_reward_numerator.safe_add_assign( | ||
altair::get_base_reward( | ||
state, | ||
index, | ||
base_reward_per_increment, | ||
&self.spec, | ||
)? | ||
.safe_mul(weight)?, | ||
)?; | ||
} | ||
} | ||
} | ||
total_proposer_reward.safe_add_assign( | ||
proposer_reward_numerator.safe_div(proposer_reward_denominator)?, | ||
)?; | ||
} | ||
|
||
Ok(total_proposer_reward) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use crate::sync_committee_rewards::get_state_before_applying_block; | ||
use crate::BlockId; | ||
use crate::ExecutionOptimistic; | ||
use beacon_chain::{BeaconChain, BeaconChainTypes}; | ||
use eth2::lighthouse::StandardBlockReward; | ||
use std::sync::Arc; | ||
use warp_utils::reject::beacon_chain_error; | ||
//// The difference between block_rewards and beacon_block_rewards is the later returns block | ||
//// reward format that satisfies beacon-api specs | ||
pub fn compute_beacon_block_rewards<T: BeaconChainTypes>( | ||
chain: Arc<BeaconChain<T>>, | ||
block_id: BlockId, | ||
) -> Result<(StandardBlockReward, ExecutionOptimistic), warp::Rejection> { | ||
let (block, execution_optimistic) = block_id.blinded_block(&chain)?; | ||
|
||
let block_ref = block.message(); | ||
|
||
let block_root = block.canonical_root(); | ||
|
||
let mut state = get_state_before_applying_block(chain.clone(), &block)?; | ||
|
||
let rewards = chain | ||
.compute_beacon_block_reward(block_ref, block_root, &mut state) | ||
.map_err(beacon_chain_error)?; | ||
|
||
Ok((rewards, execution_optimistic)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.