Skip to content

Commit

Permalink
Extend block reward APIs (#3290)
Browse files Browse the repository at this point in the history
## Proposed Changes

Add a new HTTP endpoint `POST /lighthouse/analysis/block_rewards` which takes a vec of `BeaconBlock`s as input and outputs the `BlockReward`s for them.

Augment the `BlockReward` struct with the attestation data for attestations in the block, which simplifies access to this information from blockprint. Using attestation data I've been able to make blockprint up to 95% accurate across Prysm/Lighthouse/Teku/Nimbus. I hope to go even higher using a bunch of synthetic blocks produced for Prysm/Nimbus/Lodestar, which are underrepresented in the current training data.
  • Loading branch information
michaelsproul committed Jun 29, 2022
1 parent 3645392 commit 53b2b50
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 16 additions & 3 deletions beacon_node/beacon_chain/src/block_reward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ 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, ExecPayload, Hash256, RelativeEpoch};
use types::{BeaconBlockRef, BeaconState, EthSpec, ExecPayload, Hash256};

impl<T: BeaconChainTypes> BeaconChain<T> {
pub fn compute_block_reward<Payload: ExecPayload<T::EthSpec>>(
&self,
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
block_root: Hash256,
state: &BeaconState<T::EthSpec>,
include_attestations: bool,
) -> 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 total_active_balance = state.get_total_active_balance()?;
let mut per_attestation_rewards = block
.body()
.attestations()
Expand Down Expand Up @@ -60,11 +60,24 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.map(|cover| cover.fresh_validators_rewards)
.collect();

// Add the attestation data if desired.
let attestations = if include_attestations {
block
.body()
.attestations()
.iter()
.map(|a| a.data.clone())
.collect()
} else {
vec![]
};

let attestation_rewards = AttestationRewards {
total: attestation_total,
prev_epoch_total,
curr_epoch_total,
per_attestation_rewards,
attestations,
};

// Sync committee rewards.
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/beacon_chain/src/block_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,7 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
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)?;
chain.compute_block_reward(block.message(), block_root, &state, true)?;
event_handler.register(EventKind::BlockReward(block_reward));
}
}
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/http_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ execution_layer = {path = "../execution_layer"}
parking_lot = "0.12.0"
safe_arith = {path = "../../consensus/safe_arith"}
task_executor = { path = "../../common/task_executor" }

lru = "0.7.7"

[dev-dependencies]
store = { path = "../store" }
Expand Down
100 changes: 96 additions & 4 deletions beacon_node/http_api/src/block_rewards.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped};
use eth2::lighthouse::{BlockReward, BlockRewardsQuery};
use slog::{warn, Logger};
use lru::LruCache;
use slog::{debug, warn, Logger};
use state_processing::BlockReplayer;
use std::sync::Arc;
use warp_utils::reject::{beacon_chain_error, beacon_state_error, custom_bad_request};
use types::BeaconBlock;
use warp_utils::reject::{
beacon_chain_error, beacon_state_error, custom_bad_request, custom_server_error,
};

const STATE_CACHE_SIZE: usize = 2;

/// Fetch block rewards for blocks from the canonical chain.
pub fn get_block_rewards<T: BeaconChainTypes>(
query: BlockRewardsQuery,
chain: Arc<BeaconChain<T>>,
Expand Down Expand Up @@ -50,8 +57,12 @@ pub fn get_block_rewards<T: BeaconChainTypes>(
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)?;
let block_reward = chain.compute_block_reward(
block.message(),
block.canonical_root(),
state,
query.include_attestations,
)?;
block_rewards.push(block_reward);
Ok(())
}))
Expand All @@ -78,3 +89,84 @@ pub fn get_block_rewards<T: BeaconChainTypes>(

Ok(block_rewards)
}

/// Compute block rewards for blocks passed in as input.
pub fn compute_block_rewards<T: BeaconChainTypes>(
blocks: Vec<BeaconBlock<T::EthSpec>>,
chain: Arc<BeaconChain<T>>,
log: Logger,
) -> Result<Vec<BlockReward>, warp::Rejection> {
let mut block_rewards = Vec::with_capacity(blocks.len());
let mut state_cache = LruCache::new(STATE_CACHE_SIZE);

for block in blocks {
let parent_root = block.parent_root();

// Check LRU cache for a constructed state from a previous iteration.
let state = if let Some(state) = state_cache.get(&(parent_root, block.slot())) {
debug!(
log,
"Re-using cached state for block rewards";
"parent_root" => ?parent_root,
"slot" => block.slot(),
);
state
} else {
debug!(
log,
"Fetching state for block rewards";
"parent_root" => ?parent_root,
"slot" => block.slot()
);
let parent_block = chain
.get_blinded_block(&parent_root)
.map_err(beacon_chain_error)?
.ok_or_else(|| {
custom_bad_request(format!(
"parent block not known or not canonical: {:?}",
parent_root
))
})?;

let parent_state = chain
.get_state(&parent_block.state_root(), Some(parent_block.slot()))
.map_err(beacon_chain_error)?
.ok_or_else(|| {
custom_bad_request(format!(
"no state known for parent block: {:?}",
parent_root
))
})?;

let block_replayer = BlockReplayer::new(parent_state, &chain.spec)
.no_signature_verification()
.state_root_iter([Ok((parent_block.state_root(), parent_block.slot()))].into_iter())
.minimal_block_root_verification()
.apply_blocks(vec![], Some(block.slot()))
.map_err(beacon_chain_error)?;

if block_replayer.state_root_miss() {
warn!(
log,
"Block reward state root miss";
"parent_slot" => parent_block.slot(),
"slot" => block.slot(),
);
}

state_cache
.get_or_insert((parent_root, block.slot()), || block_replayer.into_state())
.ok_or_else(|| {
custom_server_error("LRU cache insert should always succeed".into())
})?
};

// Compute block reward.
let block_reward = chain
.compute_block_reward(block.to_ref(), block.canonical_root(), state, true)
.map_err(beacon_chain_error)?;
block_rewards.push(block_reward);
}

Ok(block_rewards)
}
15 changes: 14 additions & 1 deletion beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,18 @@ pub fn serve<T: BeaconChainTypes>(
blocking_json_task(move || block_rewards::get_block_rewards(query, chain, log))
});

// POST lighthouse/analysis/block_rewards
let post_lighthouse_block_rewards = warp::path("lighthouse")
.and(warp::path("analysis"))
.and(warp::path("block_rewards"))
.and(warp::body::json())
.and(warp::path::end())
.and(chain_filter.clone())
.and(log_filter.clone())
.and_then(|blocks, chain, log| {
blocking_json_task(move || block_rewards::compute_block_rewards(blocks, chain, log))
});

// GET lighthouse/analysis/attestation_performance/{index}
let get_lighthouse_attestation_performance = warp::path("lighthouse")
.and(warp::path("analysis"))
Expand Down Expand Up @@ -2998,7 +3010,8 @@ pub fn serve<T: BeaconChainTypes>(
.or(post_validator_prepare_beacon_proposer.boxed())
.or(post_lighthouse_liveness.boxed())
.or(post_lighthouse_database_reconstruct.boxed())
.or(post_lighthouse_database_historical_blocks.boxed()),
.or(post_lighthouse_database_historical_blocks.boxed())
.or(post_lighthouse_block_rewards.boxed()),
))
.recover(warp_utils::reject::handle_rejection)
.with(slog_logging(log.clone()))
Expand Down
8 changes: 7 additions & 1 deletion common/eth2/src/lighthouse/block_rewards.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use types::{Hash256, Slot};
use types::{AttestationData, Hash256, Slot};

/// Details about the rewards paid to a block proposer for proposing a block.
///
Expand Down Expand Up @@ -42,6 +42,9 @@ pub struct AttestationRewards {
///
/// Each element of the vec is a map from validator index to reward.
pub per_attestation_rewards: Vec<HashMap<u64, u64>>,
/// The attestations themselves (optional).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attestations: Vec<AttestationData>,
}

/// Query parameters for the `/lighthouse/block_rewards` endpoint.
Expand All @@ -51,4 +54,7 @@ pub struct BlockRewardsQuery {
pub start_slot: Slot,
/// Upper slot limit for block rewards returned (inclusive).
pub end_slot: Slot,
/// Include the full attestations themselves?
#[serde(default)]
pub include_attestations: bool,
}

0 comments on commit 53b2b50

Please sign in to comment.