mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-31 13:17:09 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
@@ -27,8 +27,10 @@ slot_clock = { path = "../../common/slot_clock" }
|
||||
eth2_ssz = "0.4.1"
|
||||
bs58 = "0.4.0"
|
||||
futures = "0.3.8"
|
||||
parking_lot = "0.11.0"
|
||||
execution_layer = {path = "../execution_layer"}
|
||||
parking_lot = "0.12.0"
|
||||
safe_arith = {path = "../../consensus/safe_arith"}
|
||||
task_executor = { path = "../../common/task_executor" }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -36,6 +38,7 @@ store = { path = "../store" }
|
||||
environment = { path = "../../lighthouse/environment" }
|
||||
tree_hash = "0.4.1"
|
||||
sensitive_url = { path = "../../common/sensitive_url" }
|
||||
logging = { path = "../../common/logging" }
|
||||
|
||||
[[test]]
|
||||
name = "bn_http_api_tests"
|
||||
|
||||
@@ -7,7 +7,7 @@ use state_processing::{
|
||||
per_epoch_processing::EpochProcessingSummary, BlockReplayError, BlockReplayer,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use types::{BeaconState, BeaconStateError, EthSpec, Hash256, SignedBeaconBlock};
|
||||
use types::{BeaconState, BeaconStateError, EthSpec, Hash256};
|
||||
use warp_utils::reject::{beacon_chain_error, custom_bad_request, custom_server_error};
|
||||
|
||||
const MAX_REQUEST_RANGE_EPOCHS: usize = 100;
|
||||
@@ -112,7 +112,7 @@ pub fn get_attestation_performance<T: BeaconChainTypes>(
|
||||
)
|
||||
})?;
|
||||
let first_block = chain
|
||||
.get_block(first_block_root)
|
||||
.get_blinded_block(first_block_root)
|
||||
.and_then(|maybe_block| {
|
||||
maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*first_block_root))
|
||||
})
|
||||
@@ -120,7 +120,7 @@ pub fn get_attestation_performance<T: BeaconChainTypes>(
|
||||
|
||||
// Load the block of the prior slot which will be used to build the starting state.
|
||||
let prior_block = chain
|
||||
.get_block(&first_block.parent_root())
|
||||
.get_blinded_block(&first_block.parent_root())
|
||||
.and_then(|maybe_block| {
|
||||
maybe_block
|
||||
.ok_or_else(|| BeaconChainError::MissingBeaconBlock(first_block.parent_root()))
|
||||
@@ -197,13 +197,13 @@ pub fn get_attestation_performance<T: BeaconChainTypes>(
|
||||
.iter()
|
||||
.map(|root| {
|
||||
chain
|
||||
.get_block(root)
|
||||
.get_blinded_block(root)
|
||||
.and_then(|maybe_block| {
|
||||
maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*root))
|
||||
})
|
||||
.map_err(beacon_chain_error)
|
||||
})
|
||||
.collect::<Result<Vec<SignedBeaconBlock<T::EthSpec>>, _>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
replayer = replayer
|
||||
.apply_blocks(blocks, None)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, WhenSlotSkipped};
|
||||
use eth2::types::BlockId as CoreBlockId;
|
||||
use std::str::FromStr;
|
||||
use types::{Hash256, SignedBeaconBlock, Slot};
|
||||
use types::{BlindedPayload, Hash256, SignedBeaconBlock, Slot};
|
||||
|
||||
/// Wraps `eth2::types::BlockId` and provides a simple way to obtain a block or root for a given
|
||||
/// `BlockId`.
|
||||
@@ -52,7 +52,55 @@ impl BlockId {
|
||||
}
|
||||
|
||||
/// Return the `SignedBeaconBlock` identified by `self`.
|
||||
pub fn block<T: BeaconChainTypes>(
|
||||
pub fn blinded_block<T: BeaconChainTypes>(
|
||||
&self,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<SignedBeaconBlock<T::EthSpec, BlindedPayload<T::EthSpec>>, warp::Rejection> {
|
||||
match &self.0 {
|
||||
CoreBlockId::Head => chain
|
||||
.head_beacon_block()
|
||||
.map(Into::into)
|
||||
.map_err(warp_utils::reject::beacon_chain_error),
|
||||
CoreBlockId::Slot(slot) => {
|
||||
let root = self.root(chain)?;
|
||||
chain
|
||||
.get_blinded_block(&root)
|
||||
.map_err(warp_utils::reject::beacon_chain_error)
|
||||
.and_then(|block_opt| match block_opt {
|
||||
Some(block) => {
|
||||
if block.slot() != *slot {
|
||||
return Err(warp_utils::reject::custom_not_found(format!(
|
||||
"slot {} was skipped",
|
||||
slot
|
||||
)));
|
||||
}
|
||||
Ok(block)
|
||||
}
|
||||
None => Err(warp_utils::reject::custom_not_found(format!(
|
||||
"beacon block with root {}",
|
||||
root
|
||||
))),
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
let root = self.root(chain)?;
|
||||
chain
|
||||
.get_blinded_block(&root)
|
||||
.map_err(warp_utils::reject::beacon_chain_error)
|
||||
.and_then(|root_opt| {
|
||||
root_opt.ok_or_else(|| {
|
||||
warp_utils::reject::custom_not_found(format!(
|
||||
"beacon block with root {}",
|
||||
root
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `SignedBeaconBlock` identified by `self`.
|
||||
pub async fn full_block<T: BeaconChainTypes>(
|
||||
&self,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<SignedBeaconBlock<T::EthSpec>, warp::Rejection> {
|
||||
@@ -64,6 +112,7 @@ impl BlockId {
|
||||
let root = self.root(chain)?;
|
||||
chain
|
||||
.get_block(&root)
|
||||
.await
|
||||
.map_err(warp_utils::reject::beacon_chain_error)
|
||||
.and_then(|block_opt| match block_opt {
|
||||
Some(block) => {
|
||||
@@ -85,6 +134,7 @@ impl BlockId {
|
||||
let root = self.root(chain)?;
|
||||
chain
|
||||
.get_block(&root)
|
||||
.await
|
||||
.map_err(warp_utils::reject::beacon_chain_error)
|
||||
.and_then(|root_opt| {
|
||||
root_opt.ok_or_else(|| {
|
||||
|
||||
@@ -10,8 +10,8 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
BeaconCommittee, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256,
|
||||
OwnedBeaconCommittee, RelativeEpoch, SignedBeaconBlock, Slot,
|
||||
BeaconCommittee, BeaconState, BeaconStateError, BlindedPayload, ChainSpec, Epoch, EthSpec,
|
||||
Hash256, OwnedBeaconCommittee, RelativeEpoch, SignedBeaconBlock, Slot,
|
||||
};
|
||||
use warp_utils::reject::{beacon_chain_error, custom_bad_request, custom_server_error};
|
||||
|
||||
@@ -104,7 +104,7 @@ impl<T: EthSpec> PackingEfficiencyHandler<T> {
|
||||
|
||||
fn apply_block(
|
||||
&mut self,
|
||||
block: &SignedBeaconBlock<T>,
|
||||
block: &SignedBeaconBlock<T, BlindedPayload<T>>,
|
||||
) -> Result<usize, PackingEfficiencyError> {
|
||||
let block_body = block.message().body();
|
||||
let attestations = block_body.attestations();
|
||||
@@ -251,7 +251,7 @@ pub fn get_block_packing_efficiency<T: BeaconChainTypes>(
|
||||
.ok_or_else(|| custom_server_error("no blocks were loaded".to_string()))?;
|
||||
|
||||
let first_block = chain
|
||||
.get_block(first_block_root)
|
||||
.get_blinded_block(first_block_root)
|
||||
.and_then(|maybe_block| {
|
||||
maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*first_block_root))
|
||||
})
|
||||
@@ -309,7 +309,7 @@ pub fn get_block_packing_efficiency<T: BeaconChainTypes>(
|
||||
};
|
||||
|
||||
let pre_block_hook = |_state: &mut BeaconState<T::EthSpec>,
|
||||
block: &SignedBeaconBlock<T::EthSpec>|
|
||||
block: &SignedBeaconBlock<_, BlindedPayload<_>>|
|
||||
-> Result<(), PackingEfficiencyError> {
|
||||
let slot = block.slot();
|
||||
|
||||
@@ -363,13 +363,13 @@ pub fn get_block_packing_efficiency<T: BeaconChainTypes>(
|
||||
.iter()
|
||||
.map(|root| {
|
||||
chain
|
||||
.get_block(root)
|
||||
.get_blinded_block(root)
|
||||
.and_then(|maybe_block| {
|
||||
maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*root))
|
||||
})
|
||||
.map_err(beacon_chain_error)
|
||||
})
|
||||
.collect::<Result<Vec<SignedBeaconBlock<T::EthSpec>>, _>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
replayer = replayer
|
||||
.apply_blocks(blocks, None)
|
||||
|
||||
@@ -2,17 +2,19 @@ use beacon_chain::store::{metadata::CURRENT_SCHEMA_VERSION, AnchorInfo};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use eth2::lighthouse::DatabaseInfo;
|
||||
use std::sync::Arc;
|
||||
use types::SignedBeaconBlock;
|
||||
use types::SignedBlindedBeaconBlock;
|
||||
|
||||
pub fn info<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
) -> Result<DatabaseInfo, warp::Rejection> {
|
||||
let store = &chain.store;
|
||||
let split = store.get_split_info();
|
||||
let config = store.get_config().clone();
|
||||
let anchor = store.get_anchor_info();
|
||||
|
||||
Ok(DatabaseInfo {
|
||||
schema_version: CURRENT_SCHEMA_VERSION.as_u64(),
|
||||
config,
|
||||
split,
|
||||
anchor,
|
||||
})
|
||||
@@ -20,10 +22,10 @@ pub fn info<T: BeaconChainTypes>(
|
||||
|
||||
pub fn historical_blocks<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
blocks: Vec<SignedBeaconBlock<T::EthSpec>>,
|
||||
blocks: Vec<SignedBlindedBeaconBlock<T::EthSpec>>,
|
||||
) -> Result<AnchorInfo, warp::Rejection> {
|
||||
chain
|
||||
.import_historical_block_batch(&blocks)
|
||||
.import_historical_block_batch(blocks)
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
|
||||
let anchor = chain.store.get_anchor_info().ok_or_else(|| {
|
||||
|
||||
@@ -45,10 +45,12 @@ use std::sync::Arc;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, BeaconStateError, CommitteeCache, ConfigAndPreset, Epoch,
|
||||
EthSpec, ForkName, ProposerPreparationData, ProposerSlashing, RelativeEpoch, Signature,
|
||||
SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit,
|
||||
Slot, SyncCommitteeMessage, SyncContributionData,
|
||||
Attestation, AttesterSlashing, BeaconBlockBodyMerge, BeaconBlockMerge, BeaconStateError,
|
||||
BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload,
|
||||
ProposerPreparationData, ProposerSlashing, RelativeEpoch, Signature, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedBeaconBlockMerge, SignedBlindedBeaconBlock,
|
||||
SignedContributionAndProof, SignedVoluntaryExit, Slot, SyncCommitteeMessage,
|
||||
SyncContributionData,
|
||||
};
|
||||
use version::{
|
||||
add_consensus_version_header, fork_versioned_response, inconsistent_fork_rejection,
|
||||
@@ -825,10 +827,10 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
(None, None) => chain
|
||||
.head_beacon_block()
|
||||
.map_err(warp_utils::reject::beacon_chain_error)
|
||||
.map(|block| (block.canonical_root(), block))?,
|
||||
.map(|block| (block.canonical_root(), block.into()))?,
|
||||
// Only the parent root parameter, do a forwards-iterator lookup.
|
||||
(None, Some(parent_root)) => {
|
||||
let parent = BlockId::from_root(parent_root).block(&chain)?;
|
||||
let parent = BlockId::from_root(parent_root).blinded_block(&chain)?;
|
||||
let (root, _slot) = chain
|
||||
.forwards_iter_block_roots(parent.slot())
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?
|
||||
@@ -846,14 +848,14 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
})?;
|
||||
|
||||
BlockId::from_root(root)
|
||||
.block(&chain)
|
||||
.blinded_block(&chain)
|
||||
.map(|block| (root, block))?
|
||||
}
|
||||
// Slot is supplied, search by slot and optionally filter by
|
||||
// parent root.
|
||||
(Some(slot), parent_root_opt) => {
|
||||
let root = BlockId::from_slot(slot).root(&chain)?;
|
||||
let block = BlockId::from_root(root).block(&chain)?;
|
||||
let block = BlockId::from_root(root).blinded_block(&chain)?;
|
||||
|
||||
// If the parent root was supplied, check that it matches the block
|
||||
// obtained via a slot lookup.
|
||||
@@ -898,7 +900,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and_then(|block_id: BlockId, chain: Arc<BeaconChain<T>>| {
|
||||
blocking_json_task(move || {
|
||||
let root = block_id.root(&chain)?;
|
||||
let block = BlockId::from_root(root).block(&chain)?;
|
||||
let block = BlockId::from_root(root).blinded_block(&chain)?;
|
||||
|
||||
let canonical = chain
|
||||
.block_root_at_slot(block.slot(), WhenSlotSkipped::None)
|
||||
@@ -1022,6 +1024,116 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
},
|
||||
);
|
||||
|
||||
/*
|
||||
* beacon/blocks
|
||||
*/
|
||||
|
||||
// POST beacon/blocks
|
||||
let post_beacon_blinded_blocks = eth1_v1
|
||||
.and(warp::path("beacon"))
|
||||
.and(warp::path("blinded_blocks"))
|
||||
.and(warp::path::end())
|
||||
.and(warp::body::json())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(log_filter.clone())
|
||||
.and_then(
|
||||
|block: SignedBeaconBlock<T::EthSpec, BlindedPayload<_>>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
_log: Logger| {
|
||||
blocking_json_task(move || {
|
||||
if let Some(el) = chain.execution_layer.as_ref() {
|
||||
//FIXME(sean): we may not always receive the payload in this response because it
|
||||
// should be the relay's job to propogate the block. However, since this block is
|
||||
// already signed and sent this might be ok (so long as the relay validates
|
||||
// the block before revealing the payload).
|
||||
|
||||
//FIXME(sean) additionally, this endpoint should serve blocks prior to Bellatrix, and should
|
||||
// be able to support the normal block proposal flow, because at some point full block endpoints
|
||||
// will be deprecated from the beacon API. This will entail creating full blocks in
|
||||
// `validator/blinded_blocks`, caching their payloads, and transforming them into blinded
|
||||
// blocks. We will access the payload of those blocks here. This flow should happen if the
|
||||
// execution layer has no payload builders or if we have not yet finalized post-merge transition.
|
||||
let payload = el
|
||||
.block_on(|el| el.propose_blinded_beacon_block(&block))
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_server_error(format!(
|
||||
"proposal failed: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
let new_block = SignedBeaconBlock::Merge(SignedBeaconBlockMerge {
|
||||
message: BeaconBlockMerge {
|
||||
slot: block.message().slot(),
|
||||
proposer_index: block.message().proposer_index(),
|
||||
parent_root: block.message().parent_root(),
|
||||
state_root: block.message().state_root(),
|
||||
body: BeaconBlockBodyMerge {
|
||||
randao_reveal: block.message().body().randao_reveal().clone(),
|
||||
eth1_data: block.message().body().eth1_data().clone(),
|
||||
graffiti: *block.message().body().graffiti(),
|
||||
proposer_slashings: block
|
||||
.message()
|
||||
.body()
|
||||
.proposer_slashings()
|
||||
.clone(),
|
||||
attester_slashings: block
|
||||
.message()
|
||||
.body()
|
||||
.attester_slashings()
|
||||
.clone(),
|
||||
attestations: block.message().body().attestations().clone(),
|
||||
deposits: block.message().body().deposits().clone(),
|
||||
voluntary_exits: block
|
||||
.message()
|
||||
.body()
|
||||
.voluntary_exits()
|
||||
.clone(),
|
||||
sync_aggregate: block
|
||||
.message()
|
||||
.body()
|
||||
.sync_aggregate()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
execution_payload: payload.into(),
|
||||
},
|
||||
},
|
||||
signature: block.signature().clone(),
|
||||
});
|
||||
|
||||
// Send the block, regardless of whether or not it is valid. The API
|
||||
// specification is very clear that this is the desired behaviour.
|
||||
publish_pubsub_message(
|
||||
&network_tx,
|
||||
PubsubMessage::BeaconBlock(Box::new(new_block.clone())),
|
||||
)?;
|
||||
|
||||
match chain.process_block(new_block) {
|
||||
Ok(_) => {
|
||||
// Update the head since it's likely this block will become the new
|
||||
// head.
|
||||
chain
|
||||
.fork_choice()
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format!("{:?}", e);
|
||||
|
||||
Err(warp_utils::reject::broadcast_without_import(msg))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(warp_utils::reject::custom_server_error(
|
||||
"no execution layer found".to_string(),
|
||||
))
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let block_id_or_err = warp::path::param::<BlockId>().or_else(|_| async {
|
||||
Err(warp_utils::reject::custom_bad_request(
|
||||
"Invalid block ID".to_string(),
|
||||
@@ -1050,8 +1162,8 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
block_id: BlockId,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
accept_header: Option<api_types::Accept>| {
|
||||
blocking_task(move || {
|
||||
let block = block_id.block(&chain)?;
|
||||
async move {
|
||||
let block = block_id.full_block(&chain).await?;
|
||||
let fork_name = block
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
@@ -1070,7 +1182,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.map(|res| warp::reply::json(&res).into_response()),
|
||||
}
|
||||
.map(|resp| add_consensus_version_header(resp, fork_name))
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1096,7 +1208,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and_then(|block_id: BlockId, chain: Arc<BeaconChain<T>>| {
|
||||
blocking_json_task(move || {
|
||||
block_id
|
||||
.block(&chain)
|
||||
.blinded_block(&chain)
|
||||
.map(|block| block.message().body().attestations().clone())
|
||||
.map(api_types::GenericResponse::from)
|
||||
})
|
||||
@@ -1899,7 +2011,69 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
};
|
||||
|
||||
let (block, _) = chain
|
||||
.produce_block_with_verification(
|
||||
.produce_block_with_verification::<FullPayload<T::EthSpec>>(
|
||||
randao_reveal,
|
||||
slot,
|
||||
query.graffiti.map(Into::into),
|
||||
randao_verification,
|
||||
)
|
||||
.map_err(warp_utils::reject::block_production_error)?;
|
||||
let fork_name = block
|
||||
.to_ref()
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
fork_versioned_response(endpoint_version, fork_name, block)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// GET validator/blinded_blocks/{slot}
|
||||
let get_validator_blinded_blocks = any_version
|
||||
.and(warp::path("validator"))
|
||||
.and(warp::path("blinded_blocks"))
|
||||
.and(warp::path::param::<Slot>().or_else(|_| async {
|
||||
Err(warp_utils::reject::custom_bad_request(
|
||||
"Invalid slot".to_string(),
|
||||
))
|
||||
}))
|
||||
.and(warp::path::end())
|
||||
.and(not_while_syncing_filter.clone())
|
||||
.and(warp::query::<api_types::ValidatorBlocksQuery>())
|
||||
.and(chain_filter.clone())
|
||||
.and_then(
|
||||
|endpoint_version: EndpointVersion,
|
||||
slot: Slot,
|
||||
query: api_types::ValidatorBlocksQuery,
|
||||
chain: Arc<BeaconChain<T>>| {
|
||||
blocking_json_task(move || {
|
||||
let randao_reveal = query.randao_reveal.as_ref().map_or_else(
|
||||
|| {
|
||||
if query.verify_randao {
|
||||
Err(warp_utils::reject::custom_bad_request(
|
||||
"randao_reveal is mandatory unless verify_randao=false".into(),
|
||||
))
|
||||
} else {
|
||||
Ok(Signature::empty())
|
||||
}
|
||||
},
|
||||
|sig_bytes| {
|
||||
sig_bytes.try_into().map_err(|e| {
|
||||
warp_utils::reject::custom_bad_request(format!(
|
||||
"randao reveal is not a valid BLS signature: {:?}",
|
||||
e
|
||||
))
|
||||
})
|
||||
},
|
||||
)?;
|
||||
|
||||
let randao_verification = if query.verify_randao {
|
||||
ProduceBlockVerification::VerifyRandao
|
||||
} else {
|
||||
ProduceBlockVerification::NoVerification
|
||||
};
|
||||
|
||||
let (block, _) = chain
|
||||
.produce_block_with_verification::<BlindedPayload<T::EthSpec>>(
|
||||
randao_reveal,
|
||||
slot,
|
||||
query.graffiti.map(Into::into),
|
||||
@@ -1965,6 +2139,12 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
query.slot,
|
||||
&query.attestation_data_root,
|
||||
)
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_bad_request(format!(
|
||||
"unable to fetch aggregate: {:?}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
.map(api_types::GenericResponse::from)
|
||||
.ok_or_else(|| {
|
||||
warp_utils::reject::custom_not_found(
|
||||
@@ -2607,7 +2787,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(chain_filter.clone())
|
||||
.and(log_filter.clone())
|
||||
.and_then(
|
||||
|blocks: Vec<SignedBeaconBlock<T::EthSpec>>,
|
||||
|blocks: Vec<SignedBlindedBeaconBlock<T::EthSpec>>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
log: Logger| {
|
||||
info!(
|
||||
@@ -2766,6 +2946,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.or(get_node_peer_count.boxed())
|
||||
.or(get_validator_duties_proposer.boxed())
|
||||
.or(get_validator_blocks.boxed())
|
||||
.or(get_validator_blinded_blocks.boxed())
|
||||
.or(get_validator_attestation_data.boxed())
|
||||
.or(get_validator_aggregate_attestation.boxed())
|
||||
.or(get_validator_sync_committee_contribution.boxed())
|
||||
@@ -2791,6 +2972,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.or(warp::post().and(
|
||||
post_beacon_blocks
|
||||
.boxed()
|
||||
.or(post_beacon_blinded_blocks.boxed())
|
||||
.or(post_beacon_pool_attestations.boxed())
|
||||
.or(post_beacon_pool_attester_slashings.boxed())
|
||||
.or(post_beacon_pool_proposer_slashings.boxed())
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//! Generic tests that make use of the (newer) `InteractiveApiTester`
|
||||
use crate::common::*;
|
||||
use beacon_chain::test_utils::{AttestationStrategy, BlockStrategy};
|
||||
use eth2::types::DepositContractData;
|
||||
use types::{EthSpec, MainnetEthSpec};
|
||||
use tree_hash::TreeHash;
|
||||
use types::{EthSpec, FullPayload, MainnetEthSpec, Slot};
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
|
||||
@@ -30,3 +32,96 @@ async fn deposit_contract_custom_network() {
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
// Test that running fork choice before proposing results in selection of the correct head.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
pub async fn fork_choice_before_proposal() {
|
||||
// Validator count needs to be at least 32 or proposer boost gets set to 0 when computing
|
||||
// `validator_count // 32`.
|
||||
let validator_count = 32;
|
||||
let all_validators = (0..validator_count).collect::<Vec<_>>();
|
||||
let num_initial: u64 = 31;
|
||||
|
||||
let tester = InteractiveTester::<E>::new(None, validator_count).await;
|
||||
let harness = &tester.harness;
|
||||
|
||||
// Create some chain depth.
|
||||
harness.advance_slot();
|
||||
harness.extend_chain(
|
||||
num_initial as usize,
|
||||
BlockStrategy::OnCanonicalHead,
|
||||
AttestationStrategy::AllValidators,
|
||||
);
|
||||
|
||||
// We set up the following block graph, where B is a block that is temporarily orphaned by C,
|
||||
// but is then reinstated and built upon by D.
|
||||
//
|
||||
// A | B | - | D |
|
||||
// ^ | - | C |
|
||||
let slot_a = Slot::new(num_initial);
|
||||
let slot_b = slot_a + 1;
|
||||
let slot_c = slot_a + 2;
|
||||
let slot_d = slot_a + 3;
|
||||
|
||||
let state_a = harness.get_current_state();
|
||||
let (block_b, state_b) = harness.make_block(state_a.clone(), slot_b);
|
||||
let block_root_b = harness.process_block(slot_b, block_b).unwrap();
|
||||
|
||||
// Create attestations to B but keep them in reserve until after C has been processed.
|
||||
let attestations_b = harness.make_attestations(
|
||||
&all_validators,
|
||||
&state_b,
|
||||
state_b.tree_hash_root(),
|
||||
block_root_b,
|
||||
slot_b,
|
||||
);
|
||||
|
||||
let (block_c, state_c) = harness.make_block(state_a, slot_c);
|
||||
let block_root_c = harness.process_block(slot_c, block_c.clone()).unwrap();
|
||||
|
||||
// Create attestations to C from a small number of validators and process them immediately.
|
||||
let attestations_c = harness.make_attestations(
|
||||
&all_validators[..validator_count / 2],
|
||||
&state_c,
|
||||
state_c.tree_hash_root(),
|
||||
block_root_c,
|
||||
slot_c,
|
||||
);
|
||||
harness.process_attestations(attestations_c);
|
||||
|
||||
// Apply the attestations to B, but don't re-run fork choice.
|
||||
harness.process_attestations(attestations_b);
|
||||
|
||||
// Due to proposer boost, the head should be C during slot C.
|
||||
assert_eq!(
|
||||
harness.chain.head_info().unwrap().block_root,
|
||||
block_root_c.into()
|
||||
);
|
||||
|
||||
// Ensure that building a block via the HTTP API re-runs fork choice and builds block D upon B.
|
||||
// Manually prod the per-slot task, because the slot timer doesn't run in the background in
|
||||
// these tests.
|
||||
harness.advance_slot();
|
||||
harness.chain.per_slot_task();
|
||||
|
||||
let proposer_index = state_b
|
||||
.get_beacon_proposer_index(slot_d, &harness.chain.spec)
|
||||
.unwrap();
|
||||
let randao_reveal = harness
|
||||
.sign_randao_reveal(&state_b, proposer_index, slot_d)
|
||||
.into();
|
||||
let block_d = tester
|
||||
.client
|
||||
.get_validator_blocks::<E, FullPayload<E>>(slot_d, &randao_reveal, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
|
||||
// Head is now B.
|
||||
assert_eq!(
|
||||
harness.chain.head_info().unwrap().block_root,
|
||||
block_root_b.into()
|
||||
);
|
||||
// D's parent is B.
|
||||
assert_eq!(block_d.parent_root(), block_root_b.into());
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use slot_clock::SlotClock;
|
||||
use state_processing::per_slot_processing;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use task_executor::test_utils::TestRuntime;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::time::Duration;
|
||||
use tree_hash::TreeHash;
|
||||
@@ -63,6 +64,7 @@ struct ApiTester {
|
||||
network_rx: mpsc::UnboundedReceiver<NetworkMessage<E>>,
|
||||
local_enr: Enr,
|
||||
external_peer_id: PeerId,
|
||||
_runtime: TestRuntime,
|
||||
}
|
||||
|
||||
impl ApiTester {
|
||||
@@ -121,8 +123,7 @@ impl ApiTester {
|
||||
harness.chain.slot().unwrap(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|vec| vec.into_iter().map(|(attestation, _subnet_id)| attestation))
|
||||
.flatten()
|
||||
.flat_map(|vec| vec.into_iter().map(|(attestation, _subnet_id)| attestation))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert!(
|
||||
@@ -186,7 +187,7 @@ impl ApiTester {
|
||||
external_peer_id,
|
||||
} = create_api_server(chain.clone(), log).await;
|
||||
|
||||
tokio::spawn(server);
|
||||
harness.runtime.task_executor.spawn(server, "api_server");
|
||||
|
||||
let client = BeaconNodeHttpClient::new(
|
||||
SensitiveUrl::parse(&format!(
|
||||
@@ -213,6 +214,7 @@ impl ApiTester {
|
||||
network_rx,
|
||||
local_enr,
|
||||
external_peer_id,
|
||||
_runtime: harness.runtime,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,8 +246,7 @@ impl ApiTester {
|
||||
harness.chain.slot().unwrap(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|vec| vec.into_iter().map(|(attestation, _subnet_id)| attestation))
|
||||
.flatten()
|
||||
.flat_map(|vec| vec.into_iter().map(|(attestation, _subnet_id)| attestation))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let attester_slashing = harness.make_attester_slashing(vec![0, 1]);
|
||||
@@ -265,7 +266,7 @@ impl ApiTester {
|
||||
external_peer_id,
|
||||
} = create_api_server(chain.clone(), log).await;
|
||||
|
||||
tokio::spawn(server);
|
||||
harness.runtime.task_executor.spawn(server, "api_server");
|
||||
|
||||
let client = BeaconNodeHttpClient::new(
|
||||
SensitiveUrl::parse(&format!(
|
||||
@@ -292,6 +293,7 @@ impl ApiTester {
|
||||
network_rx,
|
||||
local_enr,
|
||||
external_peer_id,
|
||||
_runtime: harness.runtime,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,9 +764,9 @@ impl ApiTester {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_block(&self, block_id: BlockId) -> Option<SignedBeaconBlock<E>> {
|
||||
let root = self.get_block_root(block_id);
|
||||
root.and_then(|root| self.chain.get_block(&root).unwrap())
|
||||
async fn get_block(&self, block_id: BlockId) -> Option<SignedBeaconBlock<E>> {
|
||||
let root = self.get_block_root(block_id)?;
|
||||
self.chain.get_block(&root).await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn test_beacon_headers_all_slots(self) -> Self {
|
||||
@@ -859,7 +861,11 @@ impl ApiTester {
|
||||
}
|
||||
}
|
||||
|
||||
let block_opt = block_root_opt.and_then(|root| self.chain.get_block(&root).unwrap());
|
||||
let block_opt = if let Some(root) = block_root_opt {
|
||||
self.chain.get_block(&root).await.unwrap()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if block_opt.is_none() && result.is_none() {
|
||||
continue;
|
||||
@@ -945,7 +951,7 @@ impl ApiTester {
|
||||
|
||||
pub async fn test_beacon_blocks(self) -> Self {
|
||||
for block_id in self.interesting_block_ids() {
|
||||
let expected = self.get_block(block_id);
|
||||
let expected = self.get_block(block_id).await;
|
||||
|
||||
if let BlockId::Slot(slot) = block_id {
|
||||
if expected.is_none() {
|
||||
@@ -1030,6 +1036,7 @@ impl ApiTester {
|
||||
|
||||
let expected = self
|
||||
.get_block(block_id)
|
||||
.await
|
||||
.map(|block| block.message().body().attestations().clone().into());
|
||||
|
||||
if let BlockId::Slot(slot) = block_id {
|
||||
@@ -1902,7 +1909,7 @@ impl ApiTester {
|
||||
|
||||
let block = self
|
||||
.client
|
||||
.get_validator_blocks::<E>(slot, &randao_reveal, None)
|
||||
.get_validator_blocks::<E, FullPayload<E>>(slot, &randao_reveal, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
@@ -1925,7 +1932,12 @@ impl ApiTester {
|
||||
|
||||
let block = self
|
||||
.client
|
||||
.get_validator_blocks_with_verify_randao::<E>(slot, None, None, Some(false))
|
||||
.get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
|
||||
slot,
|
||||
None,
|
||||
None,
|
||||
Some(false),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
@@ -1976,13 +1988,13 @@ impl ApiTester {
|
||||
|
||||
// Check failure with no `verify_randao` passed.
|
||||
self.client
|
||||
.get_validator_blocks::<E>(slot, &bad_randao_reveal, None)
|
||||
.get_validator_blocks::<E, FullPayload<E>>(slot, &bad_randao_reveal, None)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
// Check failure with `verify_randao=true`.
|
||||
self.client
|
||||
.get_validator_blocks_with_verify_randao::<E>(
|
||||
.get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
|
||||
slot,
|
||||
Some(&bad_randao_reveal),
|
||||
None,
|
||||
@@ -1993,14 +2005,16 @@ impl ApiTester {
|
||||
|
||||
// Check failure with no randao reveal provided.
|
||||
self.client
|
||||
.get_validator_blocks_with_verify_randao::<E>(slot, None, None, None)
|
||||
.get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
|
||||
slot, None, None, None,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
// Check success with `verify_randao=false`.
|
||||
let block = self
|
||||
.client
|
||||
.get_validator_blocks_with_verify_randao::<E>(
|
||||
.get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
|
||||
slot,
|
||||
Some(&bad_randao_reveal),
|
||||
None,
|
||||
@@ -2378,8 +2392,7 @@ impl ApiTester {
|
||||
.unwrap();
|
||||
let attesting_validators: Vec<usize> = committees
|
||||
.into_iter()
|
||||
.map(|committee| committee.committee.iter().cloned())
|
||||
.flatten()
|
||||
.flat_map(|committee| committee.committee.iter().cloned())
|
||||
.collect();
|
||||
// All attesters should now be considered live
|
||||
let expected = expected
|
||||
|
||||
Reference in New Issue
Block a user