mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 12:49:23 +00:00
145 lines
4.0 KiB
Rust
145 lines
4.0 KiB
Rust
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,
|
|
)
|
|
}
|
|
}
|