/ fedimint-server / src / consensus / transaction.rs
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  }