Skip to content

Commit

Permalink
feat(tx_builder): introduce transaction builder pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
davidrusu committed Aug 17, 2021
1 parent 2799e58 commit d0539a7
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 34 deletions.
66 changes: 66 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::collections::{BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;

use curve25519_dalek_ng::scalar::Scalar;

use crate::{AmountSecrets, Dbc, DbcContent, ReissueTransaction, Result};

///! Unblinded data for creating to sn_dbc::DbcContent
pub struct Output {
pub amount: u64,
pub owner: blsttc::PublicKey,
}

#[derive(Default)]
pub struct TransactionBuilder {
pub inputs: HashMap<Dbc, AmountSecrets>,
pub outputs: Vec<Output>,
}

impl TransactionBuilder {
pub fn add_input(mut self, dbc: Dbc, amount_secrets: AmountSecrets) -> Self {
self.inputs.insert(dbc, amount_secrets);
self
}

pub fn add_output(mut self, output_mat: Output) -> Self {
self.outputs.push(output_mat);
self
}

pub fn build(self) -> Result<ReissueTransaction> {
let parents = BTreeSet::from_iter(self.inputs.keys().map(Dbc::name));
let inputs_bf_sum = self
.inputs
.values()
.map(|amount_secrets| amount_secrets.blinding_factor)
.sum();

let mut outputs_bf_sum: Scalar = Default::default();
let outputs = self
.outputs
.iter()
.enumerate()
.map(|(out_idx, output)| {
let blinding_factor = DbcContent::calc_blinding_factor(
out_idx == self.outputs.len() - 1,
inputs_bf_sum,
outputs_bf_sum,
);
outputs_bf_sum += blinding_factor;

DbcContent::new(
parents.clone(),
output.amount,
output.owner,
blinding_factor,
)
})
.collect::<Result<HashSet<_>>>()?;

Ok(ReissueTransaction {
inputs: HashSet::from_iter(self.inputs.into_keys()),
outputs,
})
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use tiny_keccak::{Hasher, Sha3};
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Hash([u8; 32]);
pub(crate) type DbcContentHash = Hash;
mod builder;
mod dbc;
mod dbc_content;
mod dbc_transaction;
Expand All @@ -24,6 +25,7 @@ mod key_manager;
mod mint;

pub use crate::{
builder::{Output, TransactionBuilder},
dbc::Dbc,
dbc_content::{AmountSecrets, BlindedOwner, DbcContent},
dbc_transaction::DbcTransaction,
Expand Down
50 changes: 16 additions & 34 deletions src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,57 +429,40 @@ mod tests {
(genesis_key, genesis_sig),
)]),
};

let inputs = HashSet::from_iter([genesis_dbc.clone()]);
let input_hashes = BTreeSet::from_iter(inputs.iter().map(|in_dbc| in_dbc.name()));
let gen_dbc_name = genesis_dbc.name();

let genesis_amount_secrets =
DbcHelper::decrypt_amount_secrets(&genesis_owner, &genesis_dbc.content)?;

let mut dbc_output_amounts = HashMap::new();
let mut tx_builder = crate::builder::TransactionBuilder::default()
.add_input(genesis_dbc, genesis_amount_secrets);

let output_owner = crate::bls_dkg_id();
let mut outputs_bf_sum = Scalar::default();
let outputs = output_amounts
.into_iter()
.enumerate()
.map(|(i, amount)| {
let blinding_factor = DbcContent::calc_blinding_factor(
i == n_outputs - 1,
genesis_amount_secrets.blinding_factor,
outputs_bf_sum,
);
outputs_bf_sum += blinding_factor;

let dbc_content = DbcContent::new(
input_hashes.clone(),
amount,
output_owner.public_key_set.public_key(),
blinding_factor,
)?;

dbc_output_amounts.insert(dbc_content.hash(), amount);
Ok(dbc_content)
})
.collect::<Result<HashSet<_>, Error>>()?;
let output_owner_pk = output_owner.public_key_set.public_key();
for amount in output_amounts.iter().copied() {
tx_builder = tx_builder.add_output(crate::builder::Output {
amount,
owner: output_owner_pk,
});
}

let transaction = ReissueTransaction { inputs, outputs };
let reissue_tx = tx_builder.build()?;

let sig_share = genesis_owner
.secret_key_share
.sign(&transaction.blinded().hash());
.sign(&reissue_tx.blinded().hash());

let sig = genesis_owner
.public_key_set
.combine_signatures(vec![(0, &sig_share)])?;

let reissue_req = ReissueRequest {
transaction,
input_ownership_proofs: HashMap::from_iter([(genesis_dbc.name(), (genesis_key, sig))]),
transaction: reissue_tx,
input_ownership_proofs: HashMap::from_iter([(gen_dbc_name, (genesis_key, sig))]),
};

let (transaction, transaction_sigs) =
match genesis_node.reissue(reissue_req.clone(), input_hashes) {
match genesis_node.reissue(reissue_req.clone(), BTreeSet::from_iter([gen_dbc_name])) {
Ok((tx, sigs)) => {
// Verify that at least one output was present.
assert_ne!(n_outputs, 0);
Expand Down Expand Up @@ -524,9 +507,8 @@ mod tests {
}));

for dbc in output_dbcs.iter() {
let expected_amount: u64 = dbc_output_amounts[&dbc.name()];
let dbc_amount = DbcHelper::decrypt_amount(&output_owner, &dbc.content)?;
assert_eq!(dbc_amount, expected_amount);
assert!(output_amounts.iter().find(|a| **a == dbc_amount).is_some());
assert!(dbc.confirm_valid(&key_manager).is_ok());
}

Expand Down

0 comments on commit d0539a7

Please sign in to comment.