use exonum::{
blockchain::{Blockchain, ConsensusConfig, PersistentPool, TransactionCache},
crypto::Hash,
helpers::{Height, Round},
merkledb::Snapshot,
messages::{AnyTx, Verified},
};
use std::{collections::BTreeMap, fmt};
use crate::State;
pub type Pool<'a> = PersistentPool<'a, BTreeMap<Hash, Verified<AnyTx>>, &'a dyn Snapshot>;
#[derive(Debug)]
pub struct ProposeParams<'a> {
consensus_config: ConsensusConfig,
height: Height,
round: Round,
snapshot: &'a dyn Snapshot,
}
impl<'a> ProposeParams<'a> {
pub(crate) fn new(state: &State, snapshot: &'a dyn Snapshot) -> Self {
Self {
consensus_config: state.consensus_config().to_owned(),
height: state.epoch(),
round: state.round(),
snapshot,
}
}
pub fn consensus_config(&self) -> &ConsensusConfig {
&self.consensus_config
}
pub fn height(&self) -> Height {
self.height
}
pub fn round(&self) -> Round {
self.round
}
pub fn snapshot(&self) -> &'a dyn Snapshot {
self.snapshot
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ProposeTemplate {
Ordinary {
tx_hashes: Vec<Hash>,
},
Skip,
}
impl ProposeTemplate {
pub fn ordinary(tx_hashes: impl IntoIterator<Item = Hash>) -> Self {
Self::Ordinary {
tx_hashes: tx_hashes.into_iter().collect(),
}
}
}
pub trait ProposeBlock: Send {
fn propose_block(&mut self, pool: Pool<'_>, params: ProposeParams<'_>) -> ProposeTemplate;
}
impl fmt::Debug for dyn ProposeBlock {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_tuple("ProposeBlock").finish()
}
}
impl ProposeBlock for Box<dyn ProposeBlock> {
fn propose_block(&mut self, pool: Pool<'_>, params: ProposeParams<'_>) -> ProposeTemplate {
(**self).propose_block(pool, params)
}
}
#[derive(Debug, Clone)]
pub struct StandardProposer;
impl ProposeBlock for StandardProposer {
fn propose_block(&mut self, pool: Pool<'_>, params: ProposeParams<'_>) -> ProposeTemplate {
let max_transactions = params.consensus_config.txs_block_limit;
let snapshot = params.snapshot();
let tx_hashes = pool
.transactions()
.filter_map(|(tx_hash, tx)| {
if Blockchain::check_tx(snapshot, tx.as_ref()).is_ok() {
Some(tx_hash)
} else {
None
}
})
.take(max_transactions as usize);
ProposeTemplate::ordinary(tx_hashes)
}
}
#[derive(Debug, Clone)]
pub struct SkipEmptyBlocks;
impl ProposeBlock for SkipEmptyBlocks {
fn propose_block(&mut self, pool: Pool<'_>, params: ProposeParams<'_>) -> ProposeTemplate {
match StandardProposer.propose_block(pool, params) {
ProposeTemplate::Ordinary { tx_hashes } if tx_hashes.is_empty() => {
ProposeTemplate::Skip
}
other => other,
}
}
}