use tracing::warn;
use crate::{
components::{fetcher::FetchResult, linear_chain_sync::event::BlockByHeightResult},
contract_runtime::{BlockAndExecutionEffects, ContractRuntimeAnnouncement, ExecutionPreState},
effect::{
announcements::ControlAnnouncement,
requests::{ContractRuntimeRequest, StorageRequest},
EffectBuilder, EffectOptionExt, Effects,
},
fatal,
types::{Block, BlockByHeight, BlockHash, Deploy, FinalizedBlock},
};
use super::{event::BlockByHashResult, Event, ReactorEventT};
pub(super) fn fetch_block_by_hash<I: Clone + Send + 'static, REv>(
effect_builder: EffectBuilder<REv>,
peer: I,
block_hash: BlockHash,
) -> Effects<Event<I>>
where
REv: ReactorEventT<I>,
{
let cloned = peer.clone();
effect_builder.fetch_block(block_hash, peer).map_or_else(
move |fetch_result| match fetch_result {
FetchResult::FromStorage(block) => {
Event::GetBlockHashResult(block_hash, BlockByHashResult::FromStorage(block))
}
FetchResult::FromPeer(block, peer) => {
Event::GetBlockHashResult(block_hash, BlockByHashResult::FromPeer(block, peer))
}
},
move || Event::GetBlockHashResult(block_hash, BlockByHashResult::Absent(cloned)),
)
}
pub(super) fn fetch_block_at_height<I: Send + Clone + 'static, REv>(
effect_builder: EffectBuilder<REv>,
peer: I,
block_height: u64,
) -> Effects<Event<I>>
where
REv: ReactorEventT<I>,
{
let cloned = peer.clone();
effect_builder
.fetch_block_by_height(block_height, peer.clone())
.map_or_else(
move |fetch_result| match fetch_result {
FetchResult::FromPeer(result, _) => match *result {
BlockByHeight::Absent(ret_height) => {
warn!(
"Fetcher returned result for invalid height. Expected {}, got {}",
block_height, ret_height
);
Event::GetBlockHeightResult(block_height, BlockByHeightResult::Absent(peer))
}
BlockByHeight::Block(block) => Event::GetBlockHeightResult(
block_height,
BlockByHeightResult::FromPeer(block, peer),
),
},
FetchResult::FromStorage(result) => match *result {
BlockByHeight::Absent(_) => {
panic!("Should not return `Absent` in `FromStorage`.")
}
BlockByHeight::Block(block) => Event::GetBlockHeightResult(
block_height,
BlockByHeightResult::FromStorage(block),
),
},
},
move || Event::GetBlockHeightResult(block_height, BlockByHeightResult::Absent(cloned)),
)
}
pub(super) async fn execute_block<REv>(
effect_builder: EffectBuilder<REv>,
block_to_execute: Block,
initial_execution_pre_state: ExecutionPreState,
) where
REv: From<StorageRequest>
+ From<ControlAnnouncement>
+ From<ContractRuntimeRequest>
+ From<ContractRuntimeAnnouncement>,
{
let protocol_version = block_to_execute.protocol_version();
let execution_pre_state =
if block_to_execute.height() == initial_execution_pre_state.next_block_height() {
initial_execution_pre_state
} else {
match effect_builder
.get_block_at_height_from_storage(block_to_execute.height() - 1)
.await
{
None => {
fatal!(
effect_builder,
"Could not get block at height {}",
block_to_execute.height() - 1
)
.await;
return;
}
Some(parent_block) => ExecutionPreState::from(parent_block.header()),
}
};
let finalized_block = FinalizedBlock::from(block_to_execute);
let deploy_hashes = finalized_block.deploy_hashes().to_vec();
let mut deploys: Vec<Deploy> = Vec::with_capacity(deploy_hashes.len());
for maybe_deploy in effect_builder.get_deploys_from_storage(deploy_hashes).await {
if let Some(deploy) = maybe_deploy {
deploys.push(deploy.into_naive())
} else {
fatal!(
effect_builder,
"Could not fetch deploys for finalized block: {:?}",
finalized_block
)
.await;
return;
}
}
let transfer_hashes = finalized_block.transfer_hashes().to_vec();
let mut transfers: Vec<Deploy> = Vec::with_capacity(transfer_hashes.len());
for maybe_transfer in effect_builder
.get_deploys_from_storage(transfer_hashes)
.await
{
if let Some(transfer) = maybe_transfer {
transfers.push(transfer.into_naive())
} else {
fatal!(
effect_builder,
"Could not fetch deploys for finalized block: {:?}",
finalized_block
)
.await;
return;
}
}
let BlockAndExecutionEffects {
block,
execution_results,
..
} = match effect_builder
.execute_finalized_block(
protocol_version,
execution_pre_state,
finalized_block,
deploys,
transfers,
)
.await
{
Ok(child_block) => child_block,
Err(error) => {
fatal!(effect_builder, "Fatal error: {}", error).await;
return;
}
};
effect_builder
.announce_linear_chain_block(block, execution_results)
.await;
}