fedimint_server/consensus/
transaction.rs1use fedimint_core::db::DatabaseTransaction;
2use fedimint_core::module::registry::ServerModuleRegistry;
3use fedimint_core::module::TransactionItemAmount;
4use fedimint_core::transaction::{Transaction, TransactionError, TRANSACTION_OVERFLOW_ERROR};
5use fedimint_core::{Amount, OutPoint};
6use rayon::iter::{IntoParallelIterator, ParallelIterator};
7
8use crate::metrics::{CONSENSUS_TX_PROCESSED_INPUTS, CONSENSUS_TX_PROCESSED_OUTPUTS};
9
10pub async fn process_transaction_with_dbtx(
11 modules: ServerModuleRegistry,
12 dbtx: &mut DatabaseTransaction<'_>,
13 transaction: &Transaction,
14) -> Result<(), TransactionError> {
15 let in_count = transaction.inputs.len();
16 let out_count = transaction.outputs.len();
17
18 dbtx.on_commit(move || {
19 CONSENSUS_TX_PROCESSED_INPUTS.observe(in_count as f64);
20 CONSENSUS_TX_PROCESSED_OUTPUTS.observe(out_count as f64);
21 });
22
23 transaction
28 .inputs
29 .clone()
30 .into_par_iter()
31 .try_for_each(|input| {
32 modules
33 .get_expect(input.module_instance_id())
34 .verify_input(&input)
35 })
36 .map_err(|_| TransactionError::InvalidWitnessLength)?;
37
38 let mut funding_verifier = FundingVerifier::default();
39 let mut public_keys = Vec::new();
40
41 for input in &transaction.inputs {
42 let meta = modules
43 .get_expect(input.module_instance_id())
44 .process_input(
45 &mut dbtx
46 .to_ref_with_prefix_module_id(input.module_instance_id())
47 .0,
48 input,
49 )
50 .await
51 .map_err(TransactionError::Input)?;
52
53 funding_verifier.add_input(meta.amount)?;
54 public_keys.push(meta.pub_key);
55 }
56
57 transaction.validate_signatures(&public_keys)?;
58
59 let txid = transaction.tx_hash();
60
61 for (output, out_idx) in transaction.outputs.iter().zip(0u64..) {
62 let amount = modules
63 .get_expect(output.module_instance_id())
64 .process_output(
65 &mut dbtx
66 .to_ref_with_prefix_module_id(output.module_instance_id())
67 .0,
68 output,
69 OutPoint { txid, out_idx },
70 )
71 .await
72 .map_err(TransactionError::Output)?;
73
74 funding_verifier.add_output(amount)?;
75 }
76
77 funding_verifier.verify_funding()?;
78
79 Ok(())
80}
81
82pub struct FundingVerifier {
83 input_amount: Amount,
84 output_amount: Amount,
85 fee_amount: Amount,
86}
87
88impl FundingVerifier {
89 pub fn add_input(
90 &mut self,
91 input_amount: TransactionItemAmount,
92 ) -> Result<(), TransactionError> {
93 self.input_amount = self
94 .input_amount
95 .checked_add(input_amount.amount)
96 .ok_or(TRANSACTION_OVERFLOW_ERROR)?;
97 self.fee_amount = self
98 .fee_amount
99 .checked_add(input_amount.fee)
100 .ok_or(TRANSACTION_OVERFLOW_ERROR)?;
101 Ok(())
102 }
103
104 pub fn add_output(
105 &mut self,
106 output_amount: TransactionItemAmount,
107 ) -> Result<(), TransactionError> {
108 self.output_amount = self
109 .output_amount
110 .checked_add(output_amount.amount)
111 .ok_or(TRANSACTION_OVERFLOW_ERROR)?;
112 self.fee_amount = self
113 .fee_amount
114 .checked_add(output_amount.fee)
115 .ok_or(TRANSACTION_OVERFLOW_ERROR)?;
116 Ok(())
117 }
118
119 pub fn verify_funding(self) -> Result<(), TransactionError> {
120 let outputs_and_fees = self
121 .output_amount
122 .checked_add(self.fee_amount)
123 .ok_or(TRANSACTION_OVERFLOW_ERROR)?;
124 if self.input_amount == outputs_and_fees {
125 Ok(())
126 } else {
127 Err(TransactionError::UnbalancedTransaction {
128 inputs: self.input_amount,
129 outputs: self.output_amount,
130 fee: self.fee_amount,
131 })
132 }
133 }
134}
135
136impl Default for FundingVerifier {
137 fn default() -> Self {
138 FundingVerifier {
139 input_amount: Amount::ZERO,
140 output_amount: Amount::ZERO,
141 fee_amount: Amount::ZERO,
142 }
143 }
144}