1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::default::Default;

use futures::channel::mpsc;
use log::{debug, error, warn};

use crate::{
    data_io::{
        chain_info::{AuxFinalizationChainInfoProvider, CachedChainInfoProvider},
        proposal::ProposalStatus,
        status_provider::get_proposal_status,
        PhronData, ChainInfoProvider,
    },
    mpsc::TrySendError,
    BlockId, SessionBoundaries,
};

type InterpretersChainInfoProvider<CIP> =
    CachedChainInfoProvider<AuxFinalizationChainInfoProvider<CIP>>;

/// Takes as input ordered `PhronData` from `PhronBFT` and pushes blocks that should be finalized
/// to an output channel. The other end of the channel is held by the aggregator whose goal is to
/// create multisignatures under the finalized blocks.
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 {
        // We are in session > 0, we take the last block of previous session.
        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 {
        // We are in session 0, we take the genesis block -- it is finalized by definition.
        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();
            }
        };

        // WARNING: If we ever enable block pruning, this code (and the code in Data Store) must be carefully
        // analyzed for possible safety violations.

        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);
            }
        }
    }
}