use std::default::Default;
use futures::channel::mpsc;
use log::{debug, error, warn};
use crate::{
data_io::{
chain_info::{AuxFinalizationChainInfoProvider, CachedChainInfoProvider},
legacy::{proposal::ProposalStatus, status_provider::get_proposal_status, PhronData},
ChainInfoProvider,
},
mpsc::TrySendError,
BlockId, SessionBoundaries,
};
type InterpretersChainInfoProvider<CIP> =
CachedChainInfoProvider<AuxFinalizationChainInfoProvider<CIP>>;
pub struct OrderedDataInterpreter<CIP>
where
CIP: ChainInfoProvider,
{
blocks_to_finalize_tx: mpsc::UnboundedSender<BlockId>,
chain_info_provider: InterpretersChainInfoProvider<CIP>,
last_finalized_by_phron: BlockId,
session_boundaries: SessionBoundaries,
}
fn get_last_block_prev_session<CIP>(
session_boundaries: SessionBoundaries,
chain_info: &mut CIP,
) -> BlockId
where
CIP: ChainInfoProvider,
{
if session_boundaries.first_block() > 0 {
let last_prev_session_num = session_boundaries.first_block() - 1;
chain_info.get_finalized_at(last_prev_session_num).expect(
"Last block of previous session must have been finalized before starting the current",
)
} else {
chain_info
.get_finalized_at(0)
.expect("Genesis block must be available")
}
}
impl<CIP> OrderedDataInterpreter<CIP>
where
CIP: ChainInfoProvider,
{
pub fn new(
blocks_to_finalize_tx: mpsc::UnboundedSender<BlockId>,
mut chain_info: CIP,
session_boundaries: SessionBoundaries,
) -> Self {
let last_finalized_by_phron =
get_last_block_prev_session(session_boundaries.clone(), &mut chain_info);
let chain_info_provider =
AuxFinalizationChainInfoProvider::new(chain_info, last_finalized_by_phron.clone());
let chain_info_provider =
CachedChainInfoProvider::new(chain_info_provider, Default::default());
OrderedDataInterpreter {
blocks_to_finalize_tx,
chain_info_provider,
last_finalized_by_phron,
session_boundaries,
}
}
pub fn set_last_finalized(&mut self, block: BlockId) {
self.last_finalized_by_phron = block;
}
pub fn chain_info_provider(&mut self) -> &mut InterpretersChainInfoProvider<CIP> {
&mut self.chain_info_provider
}
pub fn send_block_to_finalize(&mut self, block: BlockId) -> Result<(), TrySendError<BlockId>> {
self.blocks_to_finalize_tx.unbounded_send(block)
}
pub fn blocks_to_finalize_from_data(&mut self, new_data: PhronData) -> Vec<BlockId> {
let unvalidated_proposal = new_data.head_proposal;
let proposal = match unvalidated_proposal.validate_bounds(&self.session_boundaries) {
Ok(proposal) => proposal,
Err(error) => {
warn!(target: "phron-finality", "Incorrect proposal {:?} passed through data availability, session bounds: {:?}, error: {:?}", unvalidated_proposal, self.session_boundaries, error);
return Vec::new();
}
};
use ProposalStatus::*;
let status = get_proposal_status(&mut self.chain_info_provider, &proposal, None);
match status {
Finalize(blocks) => blocks,
Ignore => {
debug!(target: "phron-finality", "Ignoring proposal {:?} in interpreter.", proposal);
Vec::new()
}
Pending(pending_status) => {
panic!(
"Pending proposal {proposal:?} with status {pending_status:?} encountered in Data."
);
}
}
}
pub fn data_finalized(&mut self, data: PhronData) {
for block in self.blocks_to_finalize_from_data(data) {
self.set_last_finalized(block.clone());
self.chain_info_provider()
.inner()
.update_aux_finalized(block.clone());
if let Err(err) = self.send_block_to_finalize(block) {
error!(target: "phron-finality", "Error in sending a block from FinalizationHandler, {}", err);
}
}
}
}