1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::sync::{Arc, RwLock};

use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};

use crate::{
  Protocol,
  wallet::{
    address::MoneroAddress, Fee, SpendableOutput, Change, Decoys, SignableTransaction,
    TransactionError, extra::MAX_ARBITRARY_DATA_SIZE,
  },
};

#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
struct SignableTransactionBuilderInternal {
  protocol: Protocol,
  fee_rate: Fee,

  r_seed: Option<Zeroizing<[u8; 32]>>,
  inputs: Vec<(SpendableOutput, Decoys)>,
  payments: Vec<(MoneroAddress, u64)>,
  change_address: Change,
  data: Vec<Vec<u8>>,
}

impl SignableTransactionBuilderInternal {
  // Takes in the change address so users don't miss that they have to manually set one
  // If they don't, all leftover funds will become part of the fee
  fn new(protocol: Protocol, fee_rate: Fee, change_address: Change) -> Self {
    Self {
      protocol,
      fee_rate,
      r_seed: None,
      inputs: vec![],
      payments: vec![],
      change_address,
      data: vec![],
    }
  }

  fn set_r_seed(&mut self, r_seed: Zeroizing<[u8; 32]>) {
    self.r_seed = Some(r_seed);
  }

  fn add_input(&mut self, input: (SpendableOutput, Decoys)) {
    self.inputs.push(input);
  }
  fn add_inputs(&mut self, inputs: &[(SpendableOutput, Decoys)]) {
    self.inputs.extend(inputs.iter().cloned());
  }

  fn add_payment(&mut self, dest: MoneroAddress, amount: u64) {
    self.payments.push((dest, amount));
  }
  fn add_payments(&mut self, payments: &[(MoneroAddress, u64)]) {
    self.payments.extend(payments);
  }

  fn add_data(&mut self, data: Vec<u8>) {
    self.data.push(data);
  }
}

/// A Transaction Builder for Monero transactions.
/// All methods provided will modify self while also returning a shallow copy, enabling efficient
/// chaining with a clean API.
/// In order to fork the builder at some point, clone will still return a deep copy.
#[derive(Debug)]
pub struct SignableTransactionBuilder(Arc<RwLock<SignableTransactionBuilderInternal>>);
impl Clone for SignableTransactionBuilder {
  fn clone(&self) -> Self {
    Self(Arc::new(RwLock::new((*self.0.read().unwrap()).clone())))
  }
}

impl PartialEq for SignableTransactionBuilder {
  fn eq(&self, other: &Self) -> bool {
    *self.0.read().unwrap() == *other.0.read().unwrap()
  }
}
impl Eq for SignableTransactionBuilder {}

impl Zeroize for SignableTransactionBuilder {
  fn zeroize(&mut self) {
    self.0.write().unwrap().zeroize()
  }
}

impl SignableTransactionBuilder {
  fn shallow_copy(&self) -> Self {
    Self(self.0.clone())
  }

  pub fn new(protocol: Protocol, fee_rate: Fee, change_address: Change) -> Self {
    Self(Arc::new(RwLock::new(SignableTransactionBuilderInternal::new(
      protocol,
      fee_rate,
      change_address,
    ))))
  }

  pub fn set_r_seed(&mut self, r_seed: Zeroizing<[u8; 32]>) -> Self {
    self.0.write().unwrap().set_r_seed(r_seed);
    self.shallow_copy()
  }

  pub fn add_input(&mut self, input: (SpendableOutput, Decoys)) -> Self {
    self.0.write().unwrap().add_input(input);
    self.shallow_copy()
  }
  pub fn add_inputs(&mut self, inputs: &[(SpendableOutput, Decoys)]) -> Self {
    self.0.write().unwrap().add_inputs(inputs);
    self.shallow_copy()
  }

  pub fn add_payment(&mut self, dest: MoneroAddress, amount: u64) -> Self {
    self.0.write().unwrap().add_payment(dest, amount);
    self.shallow_copy()
  }
  pub fn add_payments(&mut self, payments: &[(MoneroAddress, u64)]) -> Self {
    self.0.write().unwrap().add_payments(payments);
    self.shallow_copy()
  }

  pub fn add_data(&mut self, data: Vec<u8>) -> Result<Self, TransactionError> {
    if data.len() > MAX_ARBITRARY_DATA_SIZE {
      Err(TransactionError::TooMuchData)?;
    }
    self.0.write().unwrap().add_data(data);
    Ok(self.shallow_copy())
  }

  pub fn build(self) -> Result<SignableTransaction, TransactionError> {
    let read = self.0.read().unwrap();
    SignableTransaction::new(
      read.protocol,
      read.r_seed.clone(),
      read.inputs.clone(),
      read.payments.clone(),
      &read.change_address,
      read.data.clone(),
      read.fee_rate,
    )
  }
}