use super::{
spend_reason::SpendReason,
transaction::{Output, Transaction},
CashNote, DerivationIndex, DerivedSecretKey, Input, MainPubkey, NanoTokens, SignedSpend, Spend,
UniquePubkey,
};
use crate::{Result, TransferError};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
pub type InputSrcTx = Transaction;
#[derive(custom_debug::Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UnsignedTransfer {
pub tx: Transaction,
pub spends: BTreeSet<(Spend, DerivationIndex)>,
pub change_id: UniquePubkey,
pub output_details: BTreeMap<UniquePubkey, (MainPubkey, DerivationIndex)>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct TransactionBuilder {
inputs: Vec<Input>,
outputs: Vec<Output>,
input_details: BTreeMap<UniquePubkey, (Option<DerivedSecretKey>, InputSrcTx, DerivationIndex)>,
output_details: BTreeMap<UniquePubkey, (MainPubkey, DerivationIndex)>,
}
impl TransactionBuilder {
pub fn add_input(
mut self,
input: Input,
derived_key: Option<DerivedSecretKey>,
input_src_tx: InputSrcTx,
derivation_index: DerivationIndex,
) -> Self {
self.input_details.insert(
*input.unique_pubkey(),
(derived_key, input_src_tx, derivation_index),
);
self.inputs.push(input);
self
}
pub fn add_inputs(
mut self,
inputs: impl IntoIterator<Item = (Input, Option<DerivedSecretKey>, InputSrcTx, DerivationIndex)>,
) -> Self {
for (input, derived_key, input_src_tx, derivation_index) in inputs.into_iter() {
self = self.add_input(input, derived_key, input_src_tx, derivation_index);
}
self
}
pub fn add_output(
mut self,
token: NanoTokens,
main_pubkey: MainPubkey,
derivation_index: DerivationIndex,
) -> Self {
let unique_pubkey = main_pubkey.new_unique_pubkey(&derivation_index);
self.output_details
.insert(unique_pubkey, (main_pubkey, derivation_index));
let output = Output::new(unique_pubkey, token.as_nano());
self.outputs.push(output);
self
}
pub fn add_outputs(
mut self,
outputs: impl IntoIterator<Item = (NanoTokens, MainPubkey, DerivationIndex)>,
) -> Self {
for (token, main_pubkey, derivation_index) in outputs.into_iter() {
self = self.add_output(token, main_pubkey, derivation_index);
}
self
}
pub fn build(
self,
reason: SpendReason,
network_royalties: Vec<DerivationIndex>,
) -> CashNoteBuilder {
let spent_tx = Transaction {
inputs: self.inputs,
outputs: self.outputs,
};
let mut signed_spends = BTreeSet::new();
for input in &spent_tx.inputs {
if let Some((Some(derived_key), input_src_tx, _)) =
self.input_details.get(&input.unique_pubkey)
{
let spend = Spend {
unique_pubkey: *input.unique_pubkey(),
spent_tx: spent_tx.clone(),
reason: reason.clone(),
amount: input.amount,
parent_tx: input_src_tx.clone(),
network_royalties: network_royalties.clone(),
};
let derived_key_sig = derived_key.sign(&spend.to_bytes_for_signing());
signed_spends.insert(SignedSpend {
spend,
derived_key_sig,
});
}
}
CashNoteBuilder::new(spent_tx, self.output_details, signed_spends)
}
pub fn build_unsigned_transfer(
self,
reason: SpendReason,
network_royalties: Vec<DerivationIndex>,
change_id: UniquePubkey,
) -> Result<UnsignedTransfer> {
let tx = Transaction {
inputs: self.inputs,
outputs: self.outputs,
};
let mut spends = BTreeSet::new();
for input in &tx.inputs {
if let Some((_, input_src_tx, derivation_index)) =
self.input_details.get(&input.unique_pubkey)
{
let spend = Spend {
unique_pubkey: *input.unique_pubkey(),
spent_tx: tx.clone(),
reason: reason.clone(),
amount: input.amount,
parent_tx: input_src_tx.clone(),
network_royalties: network_royalties.clone(),
};
spends.insert((spend, *derivation_index));
}
}
Ok(UnsignedTransfer {
tx,
spends,
change_id,
output_details: self.output_details,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CashNoteBuilder {
pub spent_tx: Transaction,
pub output_details: BTreeMap<UniquePubkey, (MainPubkey, DerivationIndex)>,
pub signed_spends: BTreeSet<SignedSpend>,
}
impl CashNoteBuilder {
pub fn new(
spent_tx: Transaction,
output_details: BTreeMap<UniquePubkey, (MainPubkey, DerivationIndex)>,
signed_spends: BTreeSet<SignedSpend>,
) -> Self {
Self {
spent_tx,
output_details,
signed_spends,
}
}
pub fn signed_spends(&self) -> Vec<&SignedSpend> {
self.signed_spends.iter().collect()
}
pub fn build(self) -> Result<Vec<(CashNote, NanoTokens)>> {
self.spent_tx
.verify_against_inputs_spent(self.signed_spends.iter())?;
self.build_output_cashnotes()
}
pub fn build_without_verifying(self) -> Result<Vec<(CashNote, NanoTokens)>> {
self.build_output_cashnotes()
}
fn build_output_cashnotes(self) -> Result<Vec<(CashNote, NanoTokens)>> {
self.spent_tx
.outputs
.iter()
.map(|output| {
let (main_pubkey, derivation_index) = self
.output_details
.get(&output.unique_pubkey)
.ok_or(TransferError::UniquePubkeyNotFound)?;
Ok((
CashNote {
unique_pubkey: main_pubkey.new_unique_pubkey(derivation_index),
parent_tx: self.spent_tx.clone(),
parent_spends: self.signed_spends.clone(),
main_pubkey: *main_pubkey,
derivation_index: *derivation_index,
},
output.amount,
))
})
.collect()
}
}