/ fedimint-client / src / transaction / builder.rs
builder.rs
  1  use std::sync::Arc;
  2  
  3  use bitcoin::key::KeyPair;
  4  use fedimint_core::core::{DynInput, DynOutput, IntoDynInstance, ModuleInstanceId};
  5  use fedimint_core::transaction::{Transaction, TransactionSignature};
  6  use fedimint_core::Amount;
  7  use itertools::multiunzip;
  8  use rand::{CryptoRng, Rng, RngCore};
  9  use secp256k1_zkp::Secp256k1;
 10  
 11  use crate::module::StateGenerator;
 12  use crate::sm::DynState;
 13  
 14  #[derive(Clone)]
 15  pub struct ClientInput<I = DynInput, S = DynState> {
 16      pub input: I,
 17      pub keys: Vec<KeyPair>,
 18      pub amount: Amount,
 19      pub state_machines: StateGenerator<S>,
 20  }
 21  
 22  impl<I, S> IntoDynInstance for ClientInput<I, S>
 23  where
 24      I: IntoDynInstance<DynType = DynInput> + 'static,
 25      S: IntoDynInstance<DynType = DynState> + 'static,
 26  {
 27      type DynType = ClientInput;
 28  
 29      fn into_dyn(self, module_instance_id: ModuleInstanceId) -> ClientInput {
 30          ClientInput {
 31              input: self.input.into_dyn(module_instance_id),
 32              keys: self.keys,
 33              amount: self.amount,
 34              state_machines: state_gen_to_dyn(self.state_machines, module_instance_id),
 35          }
 36      }
 37  }
 38  
 39  #[derive(Clone)]
 40  pub struct ClientOutput<O = DynOutput, S = DynState> {
 41      pub output: O,
 42      pub amount: Amount,
 43      pub state_machines: StateGenerator<S>,
 44  }
 45  
 46  impl<O, S> IntoDynInstance for ClientOutput<O, S>
 47  where
 48      O: IntoDynInstance<DynType = DynOutput> + 'static,
 49      S: IntoDynInstance<DynType = DynState> + 'static,
 50  {
 51      type DynType = ClientOutput;
 52  
 53      fn into_dyn(self, module_instance_id: ModuleInstanceId) -> ClientOutput {
 54          ClientOutput {
 55              output: self.output.into_dyn(module_instance_id),
 56              amount: self.amount,
 57              state_machines: state_gen_to_dyn(self.state_machines, module_instance_id),
 58          }
 59      }
 60  }
 61  
 62  #[derive(Default, Clone)]
 63  pub struct TransactionBuilder {
 64      pub(crate) inputs: Vec<ClientInput>,
 65      pub(crate) outputs: Vec<ClientOutput>,
 66  }
 67  
 68  impl TransactionBuilder {
 69      pub fn new() -> Self {
 70          Self::default()
 71      }
 72  
 73      pub fn with_input(mut self, input: ClientInput) -> Self {
 74          self.inputs.push(input);
 75          self
 76      }
 77  
 78      pub fn with_output(mut self, output: ClientOutput) -> Self {
 79          self.outputs.push(output);
 80          self
 81      }
 82  
 83      pub fn with_inputs(mut self, inputs: Vec<ClientInput>) -> Self {
 84          for input in inputs {
 85              self.inputs.push(input);
 86          }
 87  
 88          self
 89      }
 90  
 91      pub fn with_outputs(mut self, outputs: Vec<ClientOutput>) -> Self {
 92          for output in outputs {
 93              self.outputs.push(output);
 94          }
 95  
 96          self
 97      }
 98  
 99      pub fn build<C, R: RngCore + CryptoRng>(
100          self,
101          secp_ctx: &Secp256k1<C>,
102          mut rng: R,
103      ) -> (Transaction, Vec<DynState>)
104      where
105          C: secp256k1_zkp::Signing + secp256k1_zkp::Verification,
106      {
107          let (inputs, input_keys, input_states): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(
108              self.inputs
109                  .into_iter()
110                  .map(|input| (input.input, input.keys, input.state_machines)),
111          );
112          let (outputs, output_states): (Vec<_>, Vec<_>) = self
113              .outputs
114              .into_iter()
115              .map(|output| (output.output, output.state_machines))
116              .unzip();
117  
118          let nonce: [u8; 8] = rng.gen();
119  
120          let txid = Transaction::tx_hash_from_parts(&inputs, &outputs, nonce);
121          let msg = secp256k1_zkp::Message::from_slice(&txid[..]).expect("txid has right length");
122  
123          let signatures = input_keys
124              .into_iter()
125              .flatten()
126              .map(|keypair| secp_ctx.sign_schnorr(&msg, &keypair))
127              .collect();
128  
129          let transaction = Transaction {
130              inputs,
131              outputs,
132              nonce,
133              signatures: TransactionSignature::NaiveMultisig(signatures),
134          };
135  
136          let states = input_states
137              .into_iter()
138              .enumerate()
139              .chain(output_states.into_iter().enumerate())
140              .flat_map(|(idx, state_gen)| state_gen(txid, idx as u64))
141              .collect::<Vec<_>>();
142  
143          (transaction, states)
144      }
145  }
146  
147  fn state_gen_to_dyn<S>(
148      state_gen: StateGenerator<S>,
149      module_instance: ModuleInstanceId,
150  ) -> StateGenerator<DynState>
151  where
152      S: IntoDynInstance<DynType = DynState> + 'static,
153  {
154      Arc::new(move |txid, index| {
155          let states = state_gen(txid, index);
156          states
157              .into_iter()
158              .map(|state| state.into_dyn(module_instance))
159              .collect()
160      })
161  }