Skip to content

Commit

Permalink
feat: add DbcBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
dan-da committed Sep 7, 2021
1 parent 967450a commit 66fda04
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 214 deletions.
11 changes: 8 additions & 3 deletions benches/reissue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,16 @@ fn bench_reissue_100_to_1(c: &mut Criterion) {
input_ownership_proofs,
};

let (transaction, transaction_sigs) = genesis
let reissue_share = genesis
.reissue(reissue.clone(), BTreeSet::from_iter([genesis_dbc.name()]))
.unwrap();

let (mint_key_set, mint_sig_share) = transaction_sigs.values().cloned().next().unwrap();
let (mint_key_set, mint_sig_share) = reissue_share
.mint_node_signatures
.values()
.cloned()
.next()
.unwrap();

let mint_sig = genesis_owner
.public_key_set
Expand All @@ -166,7 +171,7 @@ fn bench_reissue_100_to_1(c: &mut Criterion) {

let dbcs = Vec::from_iter(reissue.transaction.outputs.into_iter().map(|content| Dbc {
content,
transaction: transaction.clone(),
transaction: reissue_share.dbc_transaction.clone(),
transaction_sigs: BTreeMap::from_iter([(
genesis_dbc.name(),
(mint_key_set.public_key(), mint_sig.clone()),
Expand Down
75 changes: 6 additions & 69 deletions examples/mint-repl/mint-repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use rustyline::error::ReadlineError;
use rustyline::Editor;
use serde::{Deserialize, Serialize};
use sn_dbc::{
Amount, Dbc, DbcContent, DbcTransaction, Hash, Mint, MintSignatures, NodeSignature, Output,
Amount, Dbc, DbcBuilder, DbcContent, DbcTransaction, Hash, Mint, NodeSignature, Output,
ReissueRequest, ReissueTransaction, SimpleKeyManager as KeyManager, SimpleSigner as Signer,
SimpleSpendBook as SpendBook, TransactionBuilder,
};
Expand Down Expand Up @@ -1020,85 +1020,22 @@ fn reissue_exec(
input_hashes: &BTreeSet<Hash>,
outputs_pks: &HashMap<Hash, PublicKeySet>,
) -> Result<()> {
let mut results: Vec<(DbcTransaction, MintSignatures)> = Default::default();
let mut mint_sig_shares: Vec<NodeSignature> = Default::default();
let mut dbc_builder: DbcBuilder = Default::default();
dbc_builder = dbc_builder.set_reissue_transaction(reissue_request.transaction.clone());

// Mint is multi-node. So each mint node must execute Mint::reissue() and
// provide its SignatureShare, which the client must then combine together
// to form the mint's Signature. This loop would exec on the client.
for mint in mintinfo.mintnodes.iter_mut() {
// here we pretend the client has made a network request to a single mint node
// so this mint.reissue() execs on the Mint node and returns data to client.
let (transaction, transaction_sigs) =
mint.reissue(reissue_request.clone(), input_hashes.clone())?;
let reissue_share = mint.reissue(reissue_request.clone(), input_hashes.clone())?;

// and now we are back to client code.

// Verify transaction returned to us by the Mint matches our request
assert_eq!(reissue_request.transaction.blinded(), transaction);

// Make a list of NodeSignature (sigshare from each Mint Node)
let mut node_shares: Vec<NodeSignature> =
transaction_sigs.iter().map(|e| e.1 .1.clone()).collect();
mint_sig_shares.append(&mut node_shares);

// Verify signatures corespond to each input
let (pubkey, sig) = transaction_sigs
.values()
.cloned()
.next()
.ok_or_else(|| anyhow!("Signature not found"))?;
for input in reissue_request.transaction.inputs.iter() {
assert_eq!(
transaction_sigs.get(&input.name()),
Some(&(pubkey.clone(), sig.clone()))
);
}
assert_eq!(transaction_sigs.len(), transaction.inputs.len());

results.push((transaction, transaction_sigs));
dbc_builder = dbc_builder.add_reissue_share(reissue_share);
}

// Transform Vec<NodeSignature> to Vec<u64, &SignatureShare>
let mint_sig_shares_ref: Vec<(u64, &SignatureShare)> = mint_sig_shares
.iter()
.map(|e| e.threshold_crypto())
.collect();

// Combine signatures from all the mint nodes to obtain Mint's Signature.
let mint_sig = mintinfo
.secret_key_set
.public_keys()
.combine_signatures(mint_sig_shares_ref)
.map_err(|e| anyhow!(e))?;

// Obtain a copy of the tx and sigs from the first MintNode results.
let (transaction, transaction_sigs) = results
.get(0)
.ok_or_else(|| anyhow!("Signature not found"))?;

// Form the final output DBCs, with Mint's Signature for each.
let mut output_dbcs: Vec<Dbc> = reissue_request
.transaction
.outputs
.iter()
.map(|content| Dbc {
content: content.clone(),
transaction: transaction.clone(),
transaction_sigs: transaction_sigs
.iter()
.map(|(input, _)| {
(
*input,
(mintinfo.genesis.owner.public_key(), mint_sig.clone()),
)
})
.collect(),
})
.collect();

// sort outputs by name
output_dbcs.sort_by_key(|d| d.name());
let output_dbcs = dbc_builder.build()?;

// for each output, construct DbcUnblinded and display
for dbc in output_dbcs.iter() {
Expand Down
138 changes: 137 additions & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use blsttc::{PublicKeySet, SignatureShare};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;

use curve25519_dalek_ng::scalar::Scalar;

use crate::{Amount, AmountSecrets, Dbc, DbcContent, Hash, ReissueTransaction, Result};
use crate::{
Amount, AmountSecrets, Dbc, DbcContent, Error, Hash, NodeSignature, ReissueShare,
ReissueTransaction, Result,
};

///! Unblinded data for creating sn_dbc::DbcContent
pub struct Output {
Expand Down Expand Up @@ -94,3 +98,135 @@ impl TransactionBuilder {
Ok((ReissueTransaction { inputs, outputs }, output_owners))
}
}

/// A Builder for aggregating ReissueShare (Mint::reissue() results)
/// from multiple mint nodes and combining signatures to
/// generate the final Dbc outputs.
#[derive(Default)]
pub struct DbcBuilder {
pub reissue_transaction: Option<ReissueTransaction>,
pub reissue_shares: Vec<ReissueShare>,
}

impl DbcBuilder {
/// Create a new DbcBuilder from a ReissueTransaction
pub fn new(reissue_transaction: ReissueTransaction) -> Self {
Self {
reissue_transaction: Some(reissue_transaction),
reissue_shares: Default::default(),
}
}

/// Add a ReissueShare from Mint::reissue()
pub fn add_reissue_share(mut self, reissue_share: ReissueShare) -> Self {
self.reissue_shares.push(reissue_share);
self
}

/// Set the ReissueTransaction
pub fn set_reissue_transaction(mut self, reissue_transaction: ReissueTransaction) -> Self {
self.reissue_transaction = Some(reissue_transaction);
self
}

/// Build the output DBCs
///
/// Note that the result Vec may be empty if the ReissueTransaction
/// has not been set or no ReissueShare has been added.
pub fn build(self) -> Result<Vec<Dbc>> {
if self.reissue_shares.is_empty() {
return Ok(vec![]);
}

let reissue_transaction = match self.reissue_transaction {
Some(rt) => rt,
None => return Ok(vec![]),
};

let mut mint_sig_shares: Vec<NodeSignature> = Default::default();
let mut pk_set: HashSet<PublicKeySet> = Default::default();

for rs in self.reissue_shares.iter() {
// Make a list of NodeSignature (sigshare from each Mint Node)
let mut node_shares: Vec<NodeSignature> = rs
.mint_node_signatures
.iter()
.map(|e| e.1 .1.clone())
.collect();
mint_sig_shares.append(&mut node_shares);

let pub_key_sets: HashSet<PublicKeySet> = rs
.mint_node_signatures
.iter()
.map(|e| e.1 .0.clone())
.collect();

// add pubkeyset to HashSet, so we can verify there is only one distinct PubKeySet
pk_set = &pk_set | &pub_key_sets; // union the sets together.

// Verify transaction returned to us by the Mint matches our request
if reissue_transaction.blinded() != rs.dbc_transaction {
return Err(Error::ReissueShareDbcTransactionMismatch);
}

// Verify that mint sig count matches input count.
if rs.mint_node_signatures.len() != reissue_transaction.inputs.len() {
return Err(Error::ReissueShareMintNodeSignaturesLenMismatch);
}

// Verify that each input has a NodeSignature
for input in reissue_transaction.inputs.iter() {
if rs.mint_node_signatures.get(&input.name()).is_none() {
return Err(Error::ReissueShareMintNodeSignatureNotFoundForInput);
}
}
}

// verify that PublicKeySet for all Dbc in all ReissueShare match.
if pk_set.len() != 1 {
return Err(Error::ReissueSharePublicKeySetMismatch);
}
let mint_public_key_set = match pk_set.iter().next() {
Some(pks) => pks,
None => return Err(Error::ReissueSharePublicKeySetMismatch),
};

// Transform Vec<NodeSignature> to Vec<u64, &SignatureShare>
let mint_sig_shares_ref: Vec<(u64, &SignatureShare)> = mint_sig_shares
.iter()
.map(|e| e.threshold_crypto())
.collect();

// Note: we can just use the first item because we already verified that
// all the ReissueShare match for dbc_transaction
let dbc_transaction = &self.reissue_shares[0].dbc_transaction;

// Combine signatures from all the mint nodes to obtain Mint's Signature.
let mint_sig = mint_public_key_set.combine_signatures(mint_sig_shares_ref)?;

// Form the final output DBCs, with Mint's Signature for each.
let mut output_dbcs: Vec<Dbc> = reissue_transaction
.outputs
.iter()
.map(|content| Dbc {
content: content.clone(),
transaction: dbc_transaction.clone(),
transaction_sigs: reissue_transaction
.inputs
.iter()
.map(|input| {
(
input.name(),
(mint_public_key_set.public_key(), mint_sig.clone()),
)
})
.collect(),
})
.collect();

// sort outputs by name
output_dbcs.sort_by_key(|d| d.name());

Ok(output_dbcs)
}
}
61 changes: 27 additions & 34 deletions src/dbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ mod tests {

use crate::tests::{NonZeroTinyInt, TinyInt};
use crate::{
Amount, DbcHelper, KeyManager, Mint, ReissueRequest, SimpleKeyManager, SimpleSigner,
SimpleSpendBook,
Amount, DbcBuilder, DbcHelper, KeyManager, Mint, ReissueRequest, SimpleKeyManager,
SimpleSigner, SimpleSpendBook,
};

fn divide(amount: Amount, n_ways: u8) -> impl Iterator<Item = Amount> {
Expand Down Expand Up @@ -193,38 +193,23 @@ mod tests {
&input_owner.public_key_set,
)?;

let (split_transaction, split_transaction_sigs) = genesis_node
let split_reissue_share = genesis_node
.reissue(
reissue_request.clone(),
reissue_request.transaction.blinded().inputs,
)
.unwrap();

assert_eq!(split_transaction, reissue_request.transaction.blinded());
let mut dbc_builder = DbcBuilder::new(reissue_request.transaction);
dbc_builder = dbc_builder.add_reissue_share(split_reissue_share);
let output_dbcs = dbc_builder.build()?;

let (mint_key_set, mint_sig_share) = split_transaction_sigs.values().next().unwrap();
let mint_sig = mint_key_set
.combine_signatures(vec![mint_sig_share.threshold_crypto()])
.unwrap();

let inputs = reissue_request
.transaction
.outputs
.into_iter()
.map(|content| Dbc {
content,
transaction: split_transaction.clone(),
transaction_sigs: BTreeMap::from_iter(
split_transaction_sigs
.iter()
.map(|(input, _)| (*input, (genesis_key, mint_sig.clone()))),
),
})
.map(|dbc| {
let amount_secrets =
DbcHelper::decrypt_amount_secrets(&input_owner, &dbc.content).unwrap();
(dbc, amount_secrets)
});
// The outputs become inputs for next reissue.
let inputs = output_dbcs.into_iter().map(|dbc| {
let amount_secrets =
DbcHelper::decrypt_amount_secrets(&input_owner, &dbc.content).unwrap();
(dbc, amount_secrets)
});

let (reissue_tx, _) = crate::TransactionBuilder::default()
.add_inputs(inputs)
Expand Down Expand Up @@ -255,21 +240,26 @@ mod tests {
input_ownership_proofs,
};

let (transaction, transaction_sigs) = genesis_node
let reissue_share = genesis_node
.reissue(
reissue_request.clone(),
reissue_request.transaction.blinded().inputs,
)
.unwrap();
assert_eq!(reissue_request.transaction.blinded(), transaction);
assert_eq!(
reissue_request.transaction.blinded(),
reissue_share.dbc_transaction
);

let (mint_key_set, mint_sig_share) = transaction_sigs.values().next().unwrap();
let (mint_key_set, mint_sig_share) =
reissue_share.mint_node_signatures.values().next().unwrap();
let mint_sig = mint_key_set
.combine_signatures(vec![mint_sig_share.threshold_crypto()])
.unwrap();

let fuzzed_parents = BTreeSet::from_iter(
transaction
reissue_share
.dbc_transaction
.inputs
.iter()
.copied()
Expand All @@ -293,7 +283,8 @@ mod tests {

// Add valid sigs
fuzzed_transaction_sigs.extend(
transaction_sigs
reissue_share
.mint_node_signatures
.iter()
.take(n_valid_sigs.coerce())
.map(|(in_hash, _)| (*in_hash, (genesis_key, mint_sig.clone()))),
Expand All @@ -312,7 +303,9 @@ mod tests {
let id = crate::bls_dkg_id();
let key_manager =
SimpleKeyManager::new(SimpleSigner::from(id.clone()), genesis_key);
let trans_sig_share = key_manager.sign(&transaction.hash()).unwrap();
let trans_sig_share = key_manager
.sign(&reissue_share.dbc_transaction.hash())
.unwrap();
let trans_sig = id
.public_key_set
.combine_signatures(vec![trans_sig_share.threshold_crypto()])
Expand Down Expand Up @@ -344,7 +337,7 @@ mod tests {

let dbc = Dbc {
content: fuzzed_content,
transaction,
transaction: reissue_share.dbc_transaction,
transaction_sigs: fuzzed_transaction_sigs,
};

Expand Down
Loading

0 comments on commit 66fda04

Please sign in to comment.