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}