Implement API for block rewards (#2628)
## Proposed Changes Add an API endpoint for retrieving detailed information about block rewards. For information on usage see [the docs](https://github.com/sigp/lighthouse/blob/block-rewards-api/book/src/api-lighthouse.md#lighthouseblock_rewards), and the source.
This commit is contained in:
parent
013a3cc3e0
commit
e70daaa3b6
97
beacon_node/beacon_chain/src/block_reward.rs
Normal file
97
beacon_node/beacon_chain/src/block_reward.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||||
|
use eth2::lighthouse::{AttestationRewards, BlockReward, BlockRewardMeta};
|
||||||
|
use operation_pool::{AttMaxCover, MaxCover};
|
||||||
|
use state_processing::per_block_processing::altair::sync_committee::compute_sync_aggregate_rewards;
|
||||||
|
use types::{BeaconBlockRef, BeaconState, EthSpec, Hash256, RelativeEpoch};
|
||||||
|
|
||||||
|
impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||||
|
pub fn compute_block_reward(
|
||||||
|
&self,
|
||||||
|
block: BeaconBlockRef<'_, T::EthSpec>,
|
||||||
|
block_root: Hash256,
|
||||||
|
state: &BeaconState<T::EthSpec>,
|
||||||
|
) -> Result<BlockReward, BeaconChainError> {
|
||||||
|
if block.slot() != state.slot() {
|
||||||
|
return Err(BeaconChainError::BlockRewardSlotError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let active_indices = state.get_cached_active_validator_indices(RelativeEpoch::Current)?;
|
||||||
|
let total_active_balance = state.get_total_balance(active_indices, &self.spec)?;
|
||||||
|
let mut per_attestation_rewards = block
|
||||||
|
.body()
|
||||||
|
.attestations()
|
||||||
|
.iter()
|
||||||
|
.map(|att| {
|
||||||
|
AttMaxCover::new(att, state, total_active_balance, &self.spec)
|
||||||
|
.ok_or(BeaconChainError::BlockRewardAttestationError)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
// Update the attestation rewards for each previous attestation included.
|
||||||
|
// This is O(n^2) in the number of attestations n.
|
||||||
|
for i in 0..per_attestation_rewards.len() {
|
||||||
|
let (updated, to_update) = per_attestation_rewards.split_at_mut(i + 1);
|
||||||
|
let latest_att = &updated[i];
|
||||||
|
|
||||||
|
for att in to_update {
|
||||||
|
att.update_covering_set(latest_att.object(), latest_att.covering_set());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut prev_epoch_total = 0;
|
||||||
|
let mut curr_epoch_total = 0;
|
||||||
|
|
||||||
|
for cover in &per_attestation_rewards {
|
||||||
|
for &reward in cover.fresh_validators_rewards.values() {
|
||||||
|
if cover.att.data.slot.epoch(T::EthSpec::slots_per_epoch()) == state.current_epoch()
|
||||||
|
{
|
||||||
|
curr_epoch_total += reward;
|
||||||
|
} else {
|
||||||
|
prev_epoch_total += reward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let attestation_total = prev_epoch_total + curr_epoch_total;
|
||||||
|
|
||||||
|
// Drop the covers.
|
||||||
|
let per_attestation_rewards = per_attestation_rewards
|
||||||
|
.into_iter()
|
||||||
|
.map(|cover| cover.fresh_validators_rewards)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let attestation_rewards = AttestationRewards {
|
||||||
|
total: attestation_total,
|
||||||
|
prev_epoch_total,
|
||||||
|
curr_epoch_total,
|
||||||
|
per_attestation_rewards,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sync committee rewards.
|
||||||
|
let sync_committee_rewards = 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)?;
|
||||||
|
sync_aggregate.sync_committee_bits.num_set_bits() as u64 * proposer_reward_per_bit
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Total, metadata
|
||||||
|
let total = attestation_total + sync_committee_rewards;
|
||||||
|
|
||||||
|
let meta = BlockRewardMeta {
|
||||||
|
slot: block.slot(),
|
||||||
|
parent_slot: state.latest_block_header().slot,
|
||||||
|
proposer_index: block.proposer_index(),
|
||||||
|
graffiti: block.body().graffiti().as_utf8_lossy(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(BlockReward {
|
||||||
|
total,
|
||||||
|
block_root,
|
||||||
|
meta,
|
||||||
|
attestation_rewards,
|
||||||
|
sync_committee_rewards,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -53,6 +53,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
metrics, BeaconChain, BeaconChainError, BeaconChainTypes,
|
metrics, BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||||
};
|
};
|
||||||
|
use eth2::types::EventKind;
|
||||||
use fork_choice::{ForkChoice, ForkChoiceStore, PayloadVerificationStatus};
|
use fork_choice::{ForkChoice, ForkChoiceStore, PayloadVerificationStatus};
|
||||||
use parking_lot::RwLockReadGuard;
|
use parking_lot::RwLockReadGuard;
|
||||||
use proto_array::Block as ProtoBlock;
|
use proto_array::Block as ProtoBlock;
|
||||||
@ -1165,6 +1166,18 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
|
|||||||
|
|
||||||
metrics::stop_timer(committee_timer);
|
metrics::stop_timer(committee_timer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have block reward listeners, compute the block reward and push it to the
|
||||||
|
* event handler.
|
||||||
|
*/
|
||||||
|
if let Some(ref event_handler) = chain.event_handler {
|
||||||
|
if event_handler.has_block_reward_subscribers() {
|
||||||
|
let block_reward =
|
||||||
|
chain.compute_block_reward(block.message(), block_root, &state)?;
|
||||||
|
event_handler.register(EventKind::BlockReward(block_reward));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform `per_block_processing` on the block and state, returning early if the block is
|
* Perform `per_block_processing` on the block and state, returning early if the block is
|
||||||
* invalid.
|
* invalid.
|
||||||
|
@ -137,6 +137,9 @@ pub enum BeaconChainError {
|
|||||||
AltairForkDisabled,
|
AltairForkDisabled,
|
||||||
ExecutionLayerMissing,
|
ExecutionLayerMissing,
|
||||||
ExecutionForkChoiceUpdateFailed(execution_layer::Error),
|
ExecutionForkChoiceUpdateFailed(execution_layer::Error),
|
||||||
|
BlockRewardSlotError,
|
||||||
|
BlockRewardAttestationError,
|
||||||
|
BlockRewardSyncError,
|
||||||
HeadMissingFromForkChoice(Hash256),
|
HeadMissingFromForkChoice(Hash256),
|
||||||
FinalizedBlockMissingFromForkChoice(Hash256),
|
FinalizedBlockMissingFromForkChoice(Hash256),
|
||||||
InvalidFinalizedPayloadShutdownError(TrySendError<ShutdownReason>),
|
InvalidFinalizedPayloadShutdownError(TrySendError<ShutdownReason>),
|
||||||
|
@ -15,6 +15,7 @@ pub struct ServerSentEventHandler<T: EthSpec> {
|
|||||||
chain_reorg_tx: Sender<EventKind<T>>,
|
chain_reorg_tx: Sender<EventKind<T>>,
|
||||||
contribution_tx: Sender<EventKind<T>>,
|
contribution_tx: Sender<EventKind<T>>,
|
||||||
late_head: Sender<EventKind<T>>,
|
late_head: Sender<EventKind<T>>,
|
||||||
|
block_reward_tx: Sender<EventKind<T>>,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
|||||||
let (chain_reorg_tx, _) = broadcast::channel(capacity);
|
let (chain_reorg_tx, _) = broadcast::channel(capacity);
|
||||||
let (contribution_tx, _) = broadcast::channel(capacity);
|
let (contribution_tx, _) = broadcast::channel(capacity);
|
||||||
let (late_head, _) = broadcast::channel(capacity);
|
let (late_head, _) = broadcast::channel(capacity);
|
||||||
|
let (block_reward_tx, _) = broadcast::channel(capacity);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
attestation_tx,
|
attestation_tx,
|
||||||
@ -42,6 +44,7 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
|||||||
chain_reorg_tx,
|
chain_reorg_tx,
|
||||||
contribution_tx,
|
contribution_tx,
|
||||||
late_head,
|
late_head,
|
||||||
|
block_reward_tx,
|
||||||
log,
|
log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +70,8 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
|||||||
.map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)),
|
.map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)),
|
||||||
EventKind::LateHead(late_head) => self.late_head.send(EventKind::LateHead(late_head))
|
EventKind::LateHead(late_head) => self.late_head.send(EventKind::LateHead(late_head))
|
||||||
.map(|count| trace!(self.log, "Registering server-sent late head event"; "receiver_count" => count)),
|
.map(|count| trace!(self.log, "Registering server-sent late head event"; "receiver_count" => count)),
|
||||||
|
EventKind::BlockReward(block_reward) => self.block_reward_tx.send(EventKind::BlockReward(block_reward))
|
||||||
|
.map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)),
|
||||||
};
|
};
|
||||||
if let Err(SendError(event)) = result {
|
if let Err(SendError(event)) = result {
|
||||||
trace!(self.log, "No receivers registered to listen for event"; "event" => ?event);
|
trace!(self.log, "No receivers registered to listen for event"; "event" => ?event);
|
||||||
@ -105,6 +110,10 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
|||||||
self.late_head.subscribe()
|
self.late_head.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subscribe_block_reward(&self) -> Receiver<EventKind<T>> {
|
||||||
|
self.block_reward_tx.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_attestation_subscribers(&self) -> bool {
|
pub fn has_attestation_subscribers(&self) -> bool {
|
||||||
self.attestation_tx.receiver_count() > 0
|
self.attestation_tx.receiver_count() > 0
|
||||||
}
|
}
|
||||||
@ -136,4 +145,8 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
|||||||
pub fn has_late_head_subscribers(&self) -> bool {
|
pub fn has_late_head_subscribers(&self) -> bool {
|
||||||
self.late_head.receiver_count() > 0
|
self.late_head.receiver_count() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_block_reward_subscribers(&self) -> bool {
|
||||||
|
self.block_reward_tx.receiver_count() > 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ mod beacon_chain;
|
|||||||
mod beacon_fork_choice_store;
|
mod beacon_fork_choice_store;
|
||||||
mod beacon_proposer_cache;
|
mod beacon_proposer_cache;
|
||||||
mod beacon_snapshot;
|
mod beacon_snapshot;
|
||||||
|
pub mod block_reward;
|
||||||
mod block_times_cache;
|
mod block_times_cache;
|
||||||
mod block_verification;
|
mod block_verification;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
80
beacon_node/http_api/src/block_rewards.rs
Normal file
80
beacon_node/http_api/src/block_rewards.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped};
|
||||||
|
use eth2::lighthouse::{BlockReward, BlockRewardsQuery};
|
||||||
|
use slog::{warn, Logger};
|
||||||
|
use state_processing::BlockReplayer;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use warp_utils::reject::{beacon_chain_error, beacon_state_error, custom_bad_request};
|
||||||
|
|
||||||
|
pub fn get_block_rewards<T: BeaconChainTypes>(
|
||||||
|
query: BlockRewardsQuery,
|
||||||
|
chain: Arc<BeaconChain<T>>,
|
||||||
|
log: Logger,
|
||||||
|
) -> Result<Vec<BlockReward>, warp::Rejection> {
|
||||||
|
let start_slot = query.start_slot;
|
||||||
|
let end_slot = query.end_slot;
|
||||||
|
let prior_slot = start_slot - 1;
|
||||||
|
|
||||||
|
if start_slot > end_slot || start_slot == 0 {
|
||||||
|
return Err(custom_bad_request(format!(
|
||||||
|
"invalid start and end: {}, {}",
|
||||||
|
start_slot, end_slot
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let end_block_root = chain
|
||||||
|
.block_root_at_slot(end_slot, WhenSlotSkipped::Prev)
|
||||||
|
.map_err(beacon_chain_error)?
|
||||||
|
.ok_or_else(|| custom_bad_request(format!("block at end slot {} unknown", end_slot)))?;
|
||||||
|
|
||||||
|
let blocks = chain
|
||||||
|
.store
|
||||||
|
.load_blocks_to_replay(start_slot, end_slot, end_block_root)
|
||||||
|
.map_err(|e| beacon_chain_error(e.into()))?;
|
||||||
|
|
||||||
|
let state_root = chain
|
||||||
|
.state_root_at_slot(prior_slot)
|
||||||
|
.map_err(beacon_chain_error)?
|
||||||
|
.ok_or_else(|| custom_bad_request(format!("prior state at slot {} unknown", prior_slot)))?;
|
||||||
|
|
||||||
|
let mut state = chain
|
||||||
|
.get_state(&state_root, Some(prior_slot))
|
||||||
|
.and_then(|maybe_state| maybe_state.ok_or(BeaconChainError::MissingBeaconState(state_root)))
|
||||||
|
.map_err(beacon_chain_error)?;
|
||||||
|
|
||||||
|
state
|
||||||
|
.build_all_caches(&chain.spec)
|
||||||
|
.map_err(beacon_state_error)?;
|
||||||
|
|
||||||
|
let mut block_rewards = Vec::with_capacity(blocks.len());
|
||||||
|
|
||||||
|
let block_replayer = BlockReplayer::new(state, &chain.spec)
|
||||||
|
.pre_block_hook(Box::new(|state, block| {
|
||||||
|
// Compute block reward.
|
||||||
|
let block_reward =
|
||||||
|
chain.compute_block_reward(block.message(), block.canonical_root(), state)?;
|
||||||
|
block_rewards.push(block_reward);
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
|
.state_root_iter(
|
||||||
|
chain
|
||||||
|
.forwards_iter_state_roots_until(prior_slot, end_slot)
|
||||||
|
.map_err(beacon_chain_error)?,
|
||||||
|
)
|
||||||
|
.no_signature_verification()
|
||||||
|
.minimal_block_root_verification()
|
||||||
|
.apply_blocks(blocks, None)
|
||||||
|
.map_err(beacon_chain_error)?;
|
||||||
|
|
||||||
|
if block_replayer.state_root_miss() {
|
||||||
|
warn!(
|
||||||
|
log,
|
||||||
|
"Block reward state root miss";
|
||||||
|
"start_slot" => start_slot,
|
||||||
|
"end_slot" => end_slot,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(block_replayer);
|
||||||
|
|
||||||
|
Ok(block_rewards)
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
mod attester_duties;
|
mod attester_duties;
|
||||||
mod block_id;
|
mod block_id;
|
||||||
|
mod block_rewards;
|
||||||
mod database;
|
mod database;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod proposer_duties;
|
mod proposer_duties;
|
||||||
@ -2540,6 +2541,16 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let get_lighthouse_block_rewards = warp::path("lighthouse")
|
||||||
|
.and(warp::path("block_rewards"))
|
||||||
|
.and(warp::query::<eth2::lighthouse::BlockRewardsQuery>())
|
||||||
|
.and(warp::path::end())
|
||||||
|
.and(chain_filter.clone())
|
||||||
|
.and(log_filter.clone())
|
||||||
|
.and_then(|query, chain, log| {
|
||||||
|
blocking_json_task(move || block_rewards::get_block_rewards(query, chain, log))
|
||||||
|
});
|
||||||
|
|
||||||
let get_events = eth1_v1
|
let get_events = eth1_v1
|
||||||
.and(warp::path("events"))
|
.and(warp::path("events"))
|
||||||
.and(warp::path::end())
|
.and(warp::path::end())
|
||||||
@ -2576,6 +2587,9 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
api_types::EventTopic::LateHead => {
|
api_types::EventTopic::LateHead => {
|
||||||
event_handler.subscribe_late_head()
|
event_handler.subscribe_late_head()
|
||||||
}
|
}
|
||||||
|
api_types::EventTopic::BlockReward => {
|
||||||
|
event_handler.subscribe_block_reward()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
receivers.push(BroadcastStream::new(receiver).map(|msg| {
|
receivers.push(BroadcastStream::new(receiver).map(|msg| {
|
||||||
@ -2661,6 +2675,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.or(get_lighthouse_beacon_states_ssz.boxed())
|
.or(get_lighthouse_beacon_states_ssz.boxed())
|
||||||
.or(get_lighthouse_staking.boxed())
|
.or(get_lighthouse_staking.boxed())
|
||||||
.or(get_lighthouse_database_info.boxed())
|
.or(get_lighthouse_database_info.boxed())
|
||||||
|
.or(get_lighthouse_block_rewards.boxed())
|
||||||
.or(get_events.boxed()),
|
.or(get_events.boxed()),
|
||||||
)
|
)
|
||||||
.or(warp::post().and(
|
.or(warp::post().and(
|
||||||
|
@ -6,15 +6,16 @@ mod metrics;
|
|||||||
mod persistence;
|
mod persistence;
|
||||||
mod sync_aggregate_id;
|
mod sync_aggregate_id;
|
||||||
|
|
||||||
|
pub use attestation::AttMaxCover;
|
||||||
|
pub use max_cover::MaxCover;
|
||||||
pub use persistence::{
|
pub use persistence::{
|
||||||
PersistedOperationPool, PersistedOperationPoolAltair, PersistedOperationPoolBase,
|
PersistedOperationPool, PersistedOperationPoolAltair, PersistedOperationPoolBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::sync_aggregate_id::SyncAggregateId;
|
use crate::sync_aggregate_id::SyncAggregateId;
|
||||||
use attestation::AttMaxCover;
|
|
||||||
use attestation_id::AttestationId;
|
use attestation_id::AttestationId;
|
||||||
use attester_slashing::AttesterSlashingMaxCover;
|
use attester_slashing::AttesterSlashingMaxCover;
|
||||||
use max_cover::{maximum_cover, MaxCover};
|
use max_cover::maximum_cover;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use state_processing::per_block_processing::errors::AttestationValidationError;
|
use state_processing::per_block_processing::errors::AttestationValidationError;
|
||||||
use state_processing::per_block_processing::{
|
use state_processing::per_block_processing::{
|
||||||
|
@ -407,4 +407,44 @@ The endpoint will return immediately. See the beacon node logs for an indication
|
|||||||
### `/lighthouse/database/historical_blocks`
|
### `/lighthouse/database/historical_blocks`
|
||||||
|
|
||||||
Manually provide `SignedBeaconBlock`s to backfill the database. This is intended
|
Manually provide `SignedBeaconBlock`s to backfill the database. This is intended
|
||||||
for use by Lighthouse developers during testing only.
|
for use by Lighthouse developers during testing only.
|
||||||
|
|
||||||
|
### `/lighthouse/block_rewards`
|
||||||
|
|
||||||
|
Fetch information about the block rewards paid to proposers for a range of consecutive blocks.
|
||||||
|
|
||||||
|
Two query parameters are required:
|
||||||
|
|
||||||
|
* `start_slot` (inclusive): the slot of the first block to compute rewards for.
|
||||||
|
* `end_slot` (inclusive): the slot of the last block to compute rewards for.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:5052/lighthouse/block_rewards?start_slot=1&end_slot=32" | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"block_root": "0x51576c2fcf0ab68d7d93c65e6828e620efbb391730511ffa35584d6c30e51410",
|
||||||
|
"attestation_rewards": {
|
||||||
|
"total": 4941156,
|
||||||
|
},
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
|
||||||
|
* Presently only attestation rewards are computed.
|
||||||
|
* The output format is verbose and subject to change. Please see [`BlockReward`][block_reward_src]
|
||||||
|
in the source.
|
||||||
|
* For maximum efficiency the `start_slot` should satisfy `start_slot % slots_per_restore_point == 1`.
|
||||||
|
This is because the state _prior_ to the `start_slot` needs to be loaded from the database, and
|
||||||
|
loading a state on a boundary is most efficient.
|
||||||
|
|
||||||
|
[block_reward_src]:
|
||||||
|
https://github.com/sigp/lighthouse/tree/unstable/common/eth2/src/lighthouse/block_reward.rs
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#[cfg(feature = "lighthouse")]
|
#[cfg(feature = "lighthouse")]
|
||||||
pub mod lighthouse;
|
pub mod lighthouse;
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
pub mod lighthouse_vc;
|
pub mod lighthouse_vc;
|
||||||
pub mod mixin;
|
pub mod mixin;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
@ -245,6 +246,7 @@ impl BeaconNodeHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a HTTP POST request, returning a JSON response.
|
/// Perform a HTTP POST request, returning a JSON response.
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
async fn post_with_response<T: Serialize, U: IntoUrl, R: DeserializeOwned>(
|
async fn post_with_response<T: Serialize, U: IntoUrl, R: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
url: U,
|
url: U,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! This module contains endpoints that are non-standard and only available on Lighthouse servers.
|
//! This module contains endpoints that are non-standard and only available on Lighthouse servers.
|
||||||
|
|
||||||
|
mod block_rewards;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ok_or_error,
|
ok_or_error,
|
||||||
types::{BeaconState, ChainSpec, Epoch, EthSpec, GenericResponse, ValidatorId},
|
types::{BeaconState, ChainSpec, Epoch, EthSpec, GenericResponse, ValidatorId},
|
||||||
@ -12,6 +14,7 @@ use ssz::four_byte_option_impl;
|
|||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
use store::{AnchorInfo, Split};
|
use store::{AnchorInfo, Split};
|
||||||
|
|
||||||
|
pub use block_rewards::{AttestationRewards, BlockReward, BlockRewardMeta, BlockRewardsQuery};
|
||||||
pub use lighthouse_network::{types::SyncState, PeerInfo};
|
pub use lighthouse_network::{types::SyncState, PeerInfo};
|
||||||
|
|
||||||
// Define "legacy" implementations of `Option<T>` which use four bytes for encoding the union
|
// Define "legacy" implementations of `Option<T>` which use four bytes for encoding the union
|
||||||
|
54
common/eth2/src/lighthouse/block_rewards.rs
Normal file
54
common/eth2/src/lighthouse/block_rewards.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use types::{Hash256, Slot};
|
||||||
|
|
||||||
|
/// Details about the rewards paid to a block proposer for proposing a block.
|
||||||
|
///
|
||||||
|
/// All rewards in GWei.
|
||||||
|
///
|
||||||
|
/// Presently this only counts attestation rewards, but in future should be expanded
|
||||||
|
/// to include information on slashings and sync committee aggregates too.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct BlockReward {
|
||||||
|
/// Sum of all reward components.
|
||||||
|
pub total: u64,
|
||||||
|
/// Block root of the block that these rewards are for.
|
||||||
|
pub block_root: Hash256,
|
||||||
|
/// Metadata about the block, particularly reward-relevant metadata.
|
||||||
|
pub meta: BlockRewardMeta,
|
||||||
|
/// Rewards due to attestations.
|
||||||
|
pub attestation_rewards: AttestationRewards,
|
||||||
|
/// Sum of rewards due to sync committee signatures.
|
||||||
|
pub sync_committee_rewards: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct BlockRewardMeta {
|
||||||
|
pub slot: Slot,
|
||||||
|
pub parent_slot: Slot,
|
||||||
|
pub proposer_index: u64,
|
||||||
|
pub graffiti: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AttestationRewards {
|
||||||
|
/// Total block reward from attestations included.
|
||||||
|
pub total: u64,
|
||||||
|
/// Total rewards from previous epoch attestations.
|
||||||
|
pub prev_epoch_total: u64,
|
||||||
|
/// Total rewards from current epoch attestations.
|
||||||
|
pub curr_epoch_total: u64,
|
||||||
|
/// Vec of attestation rewards for each attestation included.
|
||||||
|
///
|
||||||
|
/// Each element of the vec is a map from validator index to reward.
|
||||||
|
pub per_attestation_rewards: Vec<HashMap<u64, u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query parameters for the `/lighthouse/block_rewards` endpoint.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct BlockRewardsQuery {
|
||||||
|
/// Lower slot limit for block rewards returned (inclusive).
|
||||||
|
pub start_slot: Slot,
|
||||||
|
/// Upper slot limit for block rewards returned (inclusive).
|
||||||
|
pub end_slot: Slot,
|
||||||
|
}
|
@ -10,6 +10,9 @@ use std::str::{from_utf8, FromStr};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
use crate::lighthouse::BlockReward;
|
||||||
|
|
||||||
/// An API error serializable to JSON.
|
/// An API error serializable to JSON.
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@ -839,6 +842,8 @@ pub enum EventKind<T: EthSpec> {
|
|||||||
ChainReorg(SseChainReorg),
|
ChainReorg(SseChainReorg),
|
||||||
ContributionAndProof(Box<SignedContributionAndProof<T>>),
|
ContributionAndProof(Box<SignedContributionAndProof<T>>),
|
||||||
LateHead(SseLateHead),
|
LateHead(SseLateHead),
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
BlockReward(BlockReward),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> EventKind<T> {
|
impl<T: EthSpec> EventKind<T> {
|
||||||
@ -852,6 +857,8 @@ impl<T: EthSpec> EventKind<T> {
|
|||||||
EventKind::ChainReorg(_) => "chain_reorg",
|
EventKind::ChainReorg(_) => "chain_reorg",
|
||||||
EventKind::ContributionAndProof(_) => "contribution_and_proof",
|
EventKind::ContributionAndProof(_) => "contribution_and_proof",
|
||||||
EventKind::LateHead(_) => "late_head",
|
EventKind::LateHead(_) => "late_head",
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
EventKind::BlockReward(_) => "block_reward",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -904,6 +911,10 @@ impl<T: EthSpec> EventKind<T> {
|
|||||||
ServerError::InvalidServerSentEvent(format!("Contribution and Proof: {:?}", e))
|
ServerError::InvalidServerSentEvent(format!("Contribution and Proof: {:?}", e))
|
||||||
})?,
|
})?,
|
||||||
))),
|
))),
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
"block_reward" => Ok(EventKind::BlockReward(serde_json::from_str(data).map_err(
|
||||||
|
|e| ServerError::InvalidServerSentEvent(format!("Block Reward: {:?}", e)),
|
||||||
|
)?)),
|
||||||
_ => Err(ServerError::InvalidServerSentEvent(
|
_ => Err(ServerError::InvalidServerSentEvent(
|
||||||
"Could not parse event tag".to_string(),
|
"Could not parse event tag".to_string(),
|
||||||
)),
|
)),
|
||||||
@ -929,6 +940,8 @@ pub enum EventTopic {
|
|||||||
ChainReorg,
|
ChainReorg,
|
||||||
ContributionAndProof,
|
ContributionAndProof,
|
||||||
LateHead,
|
LateHead,
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
BlockReward,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for EventTopic {
|
impl FromStr for EventTopic {
|
||||||
@ -944,6 +957,8 @@ impl FromStr for EventTopic {
|
|||||||
"chain_reorg" => Ok(EventTopic::ChainReorg),
|
"chain_reorg" => Ok(EventTopic::ChainReorg),
|
||||||
"contribution_and_proof" => Ok(EventTopic::ContributionAndProof),
|
"contribution_and_proof" => Ok(EventTopic::ContributionAndProof),
|
||||||
"late_head" => Ok(EventTopic::LateHead),
|
"late_head" => Ok(EventTopic::LateHead),
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
"block_reward" => Ok(EventTopic::BlockReward),
|
||||||
_ => Err("event topic cannot be parsed.".to_string()),
|
_ => Err("event topic cannot be parsed.".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -960,6 +975,8 @@ impl fmt::Display for EventTopic {
|
|||||||
EventTopic::ChainReorg => write!(f, "chain_reorg"),
|
EventTopic::ChainReorg => write!(f, "chain_reorg"),
|
||||||
EventTopic::ContributionAndProof => write!(f, "contribution_and_proof"),
|
EventTopic::ContributionAndProof => write!(f, "contribution_and_proof"),
|
||||||
EventTopic::LateHead => write!(f, "late_head"),
|
EventTopic::LateHead => write!(f, "late_head"),
|
||||||
|
#[cfg(feature = "lighthouse")]
|
||||||
|
EventTopic::BlockReward => write!(f, "block_reward"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,19 +42,7 @@ pub fn process_sync_aggregate<T: EthSpec>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute participant and proposer rewards
|
// Compute participant and proposer rewards
|
||||||
let total_active_balance = state.get_total_active_balance()?;
|
let (participant_reward, proposer_reward) = compute_sync_aggregate_rewards(state, spec)?;
|
||||||
let total_active_increments =
|
|
||||||
total_active_balance.safe_div(spec.effective_balance_increment)?;
|
|
||||||
let total_base_rewards = get_base_reward_per_increment(total_active_balance, spec)?
|
|
||||||
.safe_mul(total_active_increments)?;
|
|
||||||
let max_participant_rewards = total_base_rewards
|
|
||||||
.safe_mul(SYNC_REWARD_WEIGHT)?
|
|
||||||
.safe_div(WEIGHT_DENOMINATOR)?
|
|
||||||
.safe_div(T::slots_per_epoch())?;
|
|
||||||
let participant_reward = max_participant_rewards.safe_div(T::SyncCommitteeSize::to_u64())?;
|
|
||||||
let proposer_reward = participant_reward
|
|
||||||
.safe_mul(PROPOSER_WEIGHT)?
|
|
||||||
.safe_div(WEIGHT_DENOMINATOR.safe_sub(PROPOSER_WEIGHT)?)?;
|
|
||||||
|
|
||||||
// Apply participant and proposer rewards
|
// Apply participant and proposer rewards
|
||||||
let committee_indices = state.get_sync_committee_indices(¤t_sync_committee)?;
|
let committee_indices = state.get_sync_committee_indices(¤t_sync_committee)?;
|
||||||
@ -73,3 +61,26 @@ pub fn process_sync_aggregate<T: EthSpec>(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the `(participant_reward, proposer_reward)` for a sync aggregate.
|
||||||
|
///
|
||||||
|
/// The `state` should be the pre-state from the same slot as the block containing the aggregate.
|
||||||
|
pub fn compute_sync_aggregate_rewards<T: EthSpec>(
|
||||||
|
state: &BeaconState<T>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(u64, u64), BlockProcessingError> {
|
||||||
|
let total_active_balance = state.get_total_active_balance()?;
|
||||||
|
let total_active_increments =
|
||||||
|
total_active_balance.safe_div(spec.effective_balance_increment)?;
|
||||||
|
let total_base_rewards = get_base_reward_per_increment(total_active_balance, spec)?
|
||||||
|
.safe_mul(total_active_increments)?;
|
||||||
|
let max_participant_rewards = total_base_rewards
|
||||||
|
.safe_mul(SYNC_REWARD_WEIGHT)?
|
||||||
|
.safe_div(WEIGHT_DENOMINATOR)?
|
||||||
|
.safe_div(T::slots_per_epoch())?;
|
||||||
|
let participant_reward = max_participant_rewards.safe_div(T::SyncCommitteeSize::to_u64())?;
|
||||||
|
let proposer_reward = participant_reward
|
||||||
|
.safe_mul(PROPOSER_WEIGHT)?
|
||||||
|
.safe_div(WEIGHT_DENOMINATOR.safe_sub(PROPOSER_WEIGHT)?)?;
|
||||||
|
Ok((participant_reward, proposer_reward))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user