fedimint_core/
session_outcome.rs

1use bitcoin::hashes::{sha256, Hash};
2use parity_scale_codec::{Decode, Encode};
3
4use crate::encoding::{Decodable, Encodable};
5use crate::epoch::ConsensusItem;
6use crate::PeerId;
7
8/// A consensus item accepted in the consensus
9///
10/// If two correct nodes obtain two ordered items from the broadcast they
11/// are guaranteed to be in the same order. However, an ordered items is
12/// only guaranteed to be seen by all correct nodes if a correct node decides to
13/// accept it.
14#[derive(Clone, Debug, PartialEq, Eq, Encodable, Decodable)]
15pub struct AcceptedItem {
16    pub item: ConsensusItem,
17    pub peer: PeerId,
18}
19
20/// Items ordered in a single session that have been accepted by Fedimint
21/// consensus.
22///
23/// A running Federation produces a [`SessionOutcome`] every couple of minutes.
24/// Therefore, just like in Bitcoin, a [`SessionOutcome`] might be empty if no
25/// items are ordered in that time or all ordered items are discarded by
26/// Fedimint Consensus.
27///
28/// When session is closed it is signed over by the peers and produces a
29/// [`SignedSessionOutcome`].
30#[derive(Clone, Debug, PartialEq, Eq, Encodable, Decodable)]
31pub struct SessionOutcome {
32    pub items: Vec<AcceptedItem>,
33}
34
35impl SessionOutcome {
36    /// A blocks header consists of 40 bytes formed by its index in big endian
37    /// bytes concatenated with the merkle root build from the consensus
38    /// hashes of its [`AcceptedItem`]s or 32 zero bytes if the block is
39    /// empty. The use of a merkle tree allows for efficient inclusion
40    /// proofs of accepted consensus items for clients.
41    pub fn header(&self, index: u64) -> [u8; 40] {
42        let mut header = [0; 40];
43
44        header[..8].copy_from_slice(&index.to_be_bytes());
45
46        let leaf_hashes = self
47            .items
48            .iter()
49            .map(Encodable::consensus_hash::<sha256::Hash>);
50
51        if let Some(root) = bitcoin::merkle_tree::calculate_root(leaf_hashes) {
52            header[8..].copy_from_slice(&root.to_byte_array());
53        }
54
55        header
56    }
57}
58
59#[derive(Clone, Debug, Encodable, Decodable, Encode, Decode, PartialEq, Eq, Hash)]
60pub struct SchnorrSignature(pub [u8; 64]);
61
62/// A [`SessionOutcome`], signed by the Federation.
63///
64/// A signed block combines a block with the naive threshold secp schnorr
65/// signature for its header created by the federation. The signed blocks allow
66/// clients and recovering guardians to verify the federations consensus
67/// history. After a signed block has been created it is stored in the database.
68#[derive(Clone, Debug, Encodable, Decodable, Eq, PartialEq)]
69pub struct SignedSessionOutcome {
70    pub session_outcome: SessionOutcome,
71    pub signatures: std::collections::BTreeMap<PeerId, SchnorrSignature>,
72}
73
74#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
75pub enum SessionStatus {
76    Initial,
77    Pending(Vec<AcceptedItem>),
78    Complete(SessionOutcome),
79}