fedimint_server/consensus/
debug.rs

1use std::fmt;
2
3use bitcoin::hashes::{sha256, Hash as _};
4use fedimint_core::encoding::Encodable as _;
5use fedimint_core::session_outcome::AcceptedItem;
6
7use crate::ConsensusItem;
8
9/// A newtype for a nice [`fmt::Debug`] of a [`ConsensusItem`]
10pub struct DebugConsensusItem<'ci>(pub &'ci ConsensusItem);
11
12impl<'ci> fmt::Debug for DebugConsensusItem<'ci> {
13    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14        match self.0 {
15            ConsensusItem::Module(mci) => {
16                f.write_fmt(format_args!(
17                    "Module CI: module={} ci={}",
18                    mci.module_instance_id(),
19                    mci
20                ))?;
21            }
22            ConsensusItem::Transaction(tx) => {
23                f.write_fmt(format_args!(
24                    "Transaction txid={}, inputs_num={}, outputs_num={}",
25                    tx.tx_hash(),
26                    tx.inputs.len(),
27                    tx.outputs.len(),
28                ))?;
29                // TODO: This is kind of lengthy, and maybe could be conditionally enabled
30                // via an env var or something.
31                for input in &tx.inputs {
32                    // TODO: add pretty print fn to interface
33                    f.write_fmt(format_args!("\n    Input: {input}"))?;
34                }
35                for output in &tx.outputs {
36                    f.write_fmt(format_args!("\n    Output: {output}")).unwrap();
37                }
38            }
39            ConsensusItem::Default { variant, .. } => {
40                f.write_fmt(format_args!("Unknown CI variant: {variant}"))?;
41            }
42        }
43        Ok(())
44    }
45}
46
47/// A compact citem formatter, useful for debugging in case of consensus failure
48///
49/// Unlike [`DebugConsensusItem`], this one is used when a (potentially) long
50/// list of citems are dumped, so it needs to be very compact.
51pub struct DebugConsensusItemCompact<'a>(pub &'a AcceptedItem);
52
53impl<'a> fmt::Display for DebugConsensusItemCompact<'a> {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        let mut engine = sha256::HashEngine::default();
56        let len = self
57            .0
58            .consensus_encode(&mut engine)
59            .map_err(|_| fmt::Error)?;
60        let hash = *sha256::Hash::from_engine(engine).as_byte_array();
61        f.write_fmt(format_args!(
62            "{}; peer={}; len={}; ",
63            hex::encode(&hash[0..12]),
64            self.0.peer,
65            len
66        ))?;
67
68        match &self.0.item {
69            ConsensusItem::Transaction(ref tx) => {
70                f.write_fmt(format_args!("txid={}; ", tx.tx_hash()))?;
71                f.write_str("inputs_module_ids=")?;
72                for (i, input) in tx.inputs.iter().enumerate() {
73                    if i != 0 {
74                        f.write_str(",")?;
75                    }
76                    f.write_fmt(format_args!("{}", input.module_instance_id()))?;
77                }
78                f.write_str("; outputs_module_ids=")?;
79                for (i, output) in tx.outputs.iter().enumerate() {
80                    if i != 0 {
81                        f.write_str(",")?;
82                    }
83                    f.write_fmt(format_args!("{}", output.module_instance_id()))?;
84                }
85            }
86            ConsensusItem::Module(module_citem) => {
87                f.write_fmt(format_args!(
88                    "citem={}; ",
89                    module_citem.module_instance_id()
90                ))?;
91            }
92            ConsensusItem::Default { variant, .. } => {
93                f.write_fmt(format_args!("unknown variant={variant}"))?;
94            }
95        }
96
97        Ok(())
98    }
99}