transaction.rs
1 use fedimint_core::db::DatabaseTransaction; 2 use fedimint_core::module::registry::ServerModuleRegistry; 3 use fedimint_core::module::TransactionItemAmount; 4 use fedimint_core::transaction::{Transaction, TransactionError}; 5 use fedimint_core::{Amount, OutPoint}; 6 7 use crate::metrics::{CONSENSUS_TX_PROCESSED_INPUTS, CONSENSUS_TX_PROCESSED_OUTPUTS}; 8 9 pub async fn process_transaction_with_dbtx( 10 modules: ServerModuleRegistry, 11 dbtx: &mut DatabaseTransaction<'_>, 12 transaction: Transaction, 13 ) -> Result<(), TransactionError> { 14 let in_count = transaction.inputs.len(); 15 let out_count = transaction.outputs.len(); 16 17 dbtx.on_commit(move || { 18 CONSENSUS_TX_PROCESSED_INPUTS.observe(in_count as f64); 19 CONSENSUS_TX_PROCESSED_OUTPUTS.observe(out_count as f64); 20 }); 21 22 let mut funding_verifier = FundingVerifier::default(); 23 let mut public_keys = Vec::new(); 24 25 for input in transaction.inputs.iter() { 26 let meta = modules 27 .get_expect(input.module_instance_id()) 28 .process_input( 29 &mut dbtx.to_ref_with_prefix_module_id(input.module_instance_id()), 30 input, 31 input.module_instance_id(), 32 ) 33 .await 34 .map_err(TransactionError::Input)?; 35 36 funding_verifier.add_input(meta.amount); 37 public_keys.push(meta.pub_key); 38 } 39 40 transaction.validate_signatures(public_keys)?; 41 42 let txid = transaction.tx_hash(); 43 44 for (output, out_idx) in transaction.outputs.iter().zip(0u64..) { 45 let amount = modules 46 .get_expect(output.module_instance_id()) 47 .process_output( 48 &mut dbtx.to_ref_with_prefix_module_id(output.module_instance_id()), 49 output, 50 OutPoint { txid, out_idx }, 51 output.module_instance_id(), 52 ) 53 .await 54 .map_err(TransactionError::Output)?; 55 56 funding_verifier.add_output(amount); 57 } 58 59 funding_verifier.verify_funding()?; 60 61 Ok(()) 62 } 63 64 pub struct FundingVerifier { 65 input_amount: Amount, 66 output_amount: Amount, 67 fee_amount: Amount, 68 } 69 70 impl FundingVerifier { 71 pub fn add_input(&mut self, input_amount: TransactionItemAmount) { 72 self.input_amount += input_amount.amount; 73 self.fee_amount += input_amount.fee; 74 } 75 76 pub fn add_output(&mut self, output_amount: TransactionItemAmount) { 77 self.output_amount += output_amount.amount; 78 self.fee_amount += output_amount.fee; 79 } 80 81 pub fn verify_funding(self) -> Result<(), TransactionError> { 82 if self.input_amount == (self.output_amount + self.fee_amount) { 83 Ok(()) 84 } else { 85 Err(TransactionError::UnbalancedTransaction { 86 inputs: self.input_amount, 87 outputs: self.output_amount, 88 fee: self.fee_amount, 89 }) 90 } 91 } 92 } 93 94 impl Default for FundingVerifier { 95 fn default() -> Self { 96 FundingVerifier { 97 input_amount: Amount::ZERO, 98 output_amount: Amount::ZERO, 99 fee_amount: Amount::ZERO, 100 } 101 } 102 }