fedimint_server/consensus/
debug.rs

1use std::fmt;
2
3use bitcoin::hashes::{Hash as _, sha256};
4use fedimint_core::encoding::{CountWrite, 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 mut count = CountWrite::from(&mut engine);
57        self.0
58            .consensus_encode(&mut count)
59            .map_err(|_| fmt::Error)?;
60
61        let count = count.count();
62        let hash = *sha256::Hash::from_engine(engine).as_byte_array();
63        f.write_fmt(format_args!(
64            "{}; peer={}; len={count}; ",
65            hex::encode(&hash[0..12]),
66            self.0.peer,
67        ))?;
68
69        match &self.0.item {
70            ConsensusItem::Transaction(tx) => {
71                f.write_fmt(format_args!("txid={}; ", tx.tx_hash()))?;
72                f.write_str("inputs_module_ids=")?;
73                for (i, input) in tx.inputs.iter().enumerate() {
74                    if i != 0 {
75                        f.write_str(",")?;
76                    }
77                    f.write_fmt(format_args!("{}", input.module_instance_id()))?;
78                }
79                f.write_str("; outputs_module_ids=")?;
80                for (i, output) in tx.outputs.iter().enumerate() {
81                    if i != 0 {
82                        f.write_str(",")?;
83                    }
84                    f.write_fmt(format_args!("{}", output.module_instance_id()))?;
85                }
86            }
87            ConsensusItem::Module(module_citem) => {
88                f.write_fmt(format_args!(
89                    "citem={}; ",
90                    module_citem.module_instance_id()
91                ))?;
92            }
93            ConsensusItem::Default { variant, .. } => {
94                f.write_fmt(format_args!("unknown variant={variant}"))?;
95            }
96        }
97
98        Ok(())
99    }
100}