use crate::{
transaction::{DbcTransaction, Output},
DbcId, DerivationIndex, DerivedKey, FeeOutput, Input, PublicAddress, Spend,
};
use crate::{Dbc, DbcSecrets, Error, Hash, Result, SignedSpend, Token};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
pub type InputSrcTx = DbcTransaction;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Default)]
pub struct TransactionBuilder {
inputs: Vec<Input>,
outputs: Vec<Output>,
fee: FeeOutput,
input_details: BTreeMap<DbcId, (DerivedKey, InputSrcTx)>,
output_details: BTreeMap<DbcId, (PublicAddress, DerivationIndex)>,
}
impl TransactionBuilder {
pub fn add_input(
mut self,
input: Input,
derived_key: DerivedKey,
input_src_tx: InputSrcTx,
) -> Self {
self.input_details
.insert(input.dbc_id(), (derived_key, input_src_tx));
self.inputs.push(input);
self
}
pub fn add_inputs(
mut self,
inputs: impl IntoIterator<Item = (Input, DerivedKey, InputSrcTx)>,
) -> Self {
for (input, derived_key, input_src_tx) in inputs.into_iter() {
self = self.add_input(input, derived_key, input_src_tx);
}
self
}
pub fn add_input_dbc(mut self, dbc: &Dbc, derived_key: &DerivedKey) -> Result<Self> {
let input_src_tx = dbc.src_tx.clone();
let input = Input {
dbc_id: dbc.id(),
token: dbc.token()?,
};
self = self.add_input(input, derived_key.clone(), input_src_tx);
Ok(self)
}
pub fn add_input_dbcs(mut self, dbcs: &[(Dbc, DerivedKey)]) -> Result<Self> {
for (dbc, derived_key) in dbcs.iter() {
self = self.add_input_dbc(dbc, derived_key)?;
}
Ok(self)
}
pub fn add_output(
mut self,
token: Token,
public_address: PublicAddress,
derivation_index: DerivationIndex,
) -> Self {
let dbc_id = public_address.new_dbc_id(&derivation_index);
self.output_details
.insert(dbc_id, (public_address, derivation_index));
let output = Output::new(dbc_id, token.as_nano());
self.outputs.push(output);
self
}
pub fn add_outputs(
mut self,
outputs: impl IntoIterator<Item = (Token, PublicAddress, DerivationIndex)>,
) -> Self {
for (token, public_address, derivation_index) in outputs.into_iter() {
self = self.add_output(token, public_address, derivation_index);
}
self
}
pub fn set_fee_output(mut self, output: FeeOutput) -> Self {
self.fee = output;
self
}
pub fn input_ids(&self) -> Vec<DbcId> {
self.inputs.iter().map(|i| i.dbc_id()).collect()
}
pub fn inputs_tokens_sum(&self) -> Token {
let amount = self.inputs.iter().map(|i| i.token.as_nano()).sum();
Token::from_nano(amount)
}
pub fn outputs_tokens_sum(&self) -> Token {
let amount = self
.outputs
.iter()
.map(|o| o.token.as_nano())
.chain(std::iter::once(self.fee.token.as_nano()))
.sum();
Token::from_nano(amount)
}
pub fn inputs(&self) -> &Vec<Input> {
&self.inputs
}
pub fn outputs(&self) -> &Vec<Output> {
&self.outputs
}
pub fn build(self, reason: Hash) -> Result<DbcBuilder> {
let spent_tx = DbcTransaction {
inputs: self.inputs.clone(),
outputs: self.outputs.clone(),
fee: self.fee.clone(),
};
let signed_spends: BTreeSet<_> = self
.inputs
.iter()
.flat_map(|input| {
let (derived_key, input_src_tx) = self.input_details.get(&input.dbc_id)?;
let spend = Spend {
dbc_id: input.dbc_id(),
spent_tx: spent_tx.clone(),
reason,
token: input.token,
dbc_creation_tx: input_src_tx.clone(),
};
let derived_key_sig = derived_key.sign(&spend.to_bytes());
Some(SignedSpend {
spend,
derived_key_sig,
})
})
.collect();
Ok(DbcBuilder::new(
spent_tx,
self.output_details,
signed_spends,
))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct DbcBuilder {
pub spent_tx: DbcTransaction,
pub output_details: BTreeMap<DbcId, (PublicAddress, DerivationIndex)>,
pub signed_spends: BTreeSet<SignedSpend>,
}
impl DbcBuilder {
pub fn new(
spent_tx: DbcTransaction,
output_details: BTreeMap<DbcId, (PublicAddress, 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<(Dbc, Token)>> {
self.spent_tx
.verify_against_inputs_spent(&self.signed_spends)?;
self.build_output_dbcs()
}
pub fn build_without_verifying(self) -> Result<Vec<(Dbc, Token)>> {
self.build_output_dbcs()
}
fn build_output_dbcs(self) -> Result<Vec<(Dbc, Token)>> {
self.spent_tx
.outputs
.iter()
.map(|output| {
let (public_address, derivation_index) = self
.output_details
.get(&output.dbc_id)
.ok_or(Error::DbcIdNotFound)?;
Ok((
Dbc {
id: public_address.new_dbc_id(derivation_index),
src_tx: self.spent_tx.clone(),
secrets: DbcSecrets::from((public_address, derivation_index)),
signed_spends: self.signed_spends.clone(),
},
output.token,
))
})
.collect()
}
}