Skip to content

Commit

Permalink
feat: add ReissueRequestBuilder to simplify aggregating dbc ownership…
Browse files Browse the repository at this point in the history
… proofs
  • Loading branch information
dan-da authored and davidrusu committed Sep 16, 2021
1 parent 8ead156 commit b0c3d5a
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 47 deletions.
114 changes: 110 additions & 4 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use blsttc::{PublicKeySet, SignatureShare};
use std::collections::{BTreeSet, HashMap, HashSet};
use blsttc::{Fr, IntoFr, PublicKeySet, SecretKeyShare, Signature, SignatureShare};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;

use curve25519_dalek_ng::scalar::Scalar;

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

///! Unblinded data for creating sn_dbc::DbcContent
Expand Down Expand Up @@ -95,6 +95,112 @@ impl TransactionBuilder {
}
}

/// Builds a ReissueRequest from a ReissueTransaction and
/// any number of (input) DBC hashes with associated ownership share(s).
#[derive(Default)]
pub struct ReissueRequestBuilder {
pub reissue_transaction: Option<ReissueTransaction>,
#[allow(clippy::type_complexity)]
pub signers_by_dbc: HashMap<SpendKey, Vec<(PublicKeySet, (Fr, SecretKeyShare))>>,
}

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

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

/// Add a single signer share for a DBC hash
pub fn add_dbc_signer<FR: IntoFr>(
mut self,
dbc_key: SpendKey,
public_key_set: PublicKeySet,
share_index: FR,
secret_key_share: SecretKeyShare,
) -> Self {
let entry = self.signers_by_dbc.entry(dbc_key).or_insert_with(Vec::new);
entry.push((public_key_set, (share_index.into_fr(), secret_key_share)));
self
}

/// Add a list of signer shares for a DBC hash
pub fn add_dbc_signers<FR: IntoFr>(
mut self,
dbc_key: SpendKey,
public_key_set: PublicKeySet,
secret_key_shares: Vec<(FR, SecretKeyShare)>,
) -> Self {
let entry = self.signers_by_dbc.entry(dbc_key).or_insert_with(Vec::new);
for (idx, secret_key_share) in secret_key_shares.into_iter() {
entry.push((public_key_set.clone(), (idx.into_fr(), secret_key_share)));
}
self
}

/// Aggregates SecretKeyShares for all DBC owners in a ReissueTransaction
/// in order to combine signature shares into Signatures, thereby
/// creating the ownership proofs necessary to construct
/// a ReissueRequest.
pub fn build(self) -> Result<ReissueRequest> {
let transaction = match self.reissue_transaction {
Some(rt) => rt,
None => return Err(Error::NoReissueTransaction),
};

let mut input_ownership_proofs: HashMap<SpendKey, Signature> = Default::default();

for (dbc_key, signers) in self.signers_by_dbc.iter() {
let dbc = transaction
.inputs
.iter()
.find(|dbc| &dbc.spend_key() == dbc_key)
.expect("No DBC with this spend key");

let pks_set: HashSet<PublicKeySet> = signers.iter().map(|s| s.0.clone()).collect();
if pks_set.len() != 1 {
return Err(Error::ReissueRequestPublicKeySetMismatch);
}
let owner_public_key_set = match pks_set.iter().next() {
Some(pks) => pks,
None => return Err(Error::ReissueRequestPublicKeySetMismatch),
};

let sig_shares: BTreeMap<Fr, SignatureShare> = signers
.iter()
.map(|s| {
let idx = s.1 .0;
let sks = &s.1 .1.derive_child(&dbc.spend_key_index());
let sig_share = sks.sign(transaction.blinded().hash());
(idx, sig_share)
})
.collect();

let sig_shares_ref: BTreeMap<Fr, &SignatureShare> = sig_shares
.iter()
.map(|(idx, share)| (*idx, share))
.collect();

let signature = owner_public_key_set.combine_signatures(sig_shares_ref)?;
input_ownership_proofs.insert(*dbc_key, signature);
}

let rr = ReissueRequest {
transaction,
input_ownership_proofs,
};
Ok(rr)
}
}

/// A Builder for aggregating ReissueShare (Mint::reissue() results)
/// from multiple mint nodes and combining signatures to
/// generate the final Dbc outputs.
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ pub enum Error {
#[error("Dbc Content parents is not the same transaction inputs")]
DbcContentParentsDifferentFromTransactionInputs,

#[error("The PublicKeySet differs between ReissueRequest entries")]
ReissueRequestPublicKeySetMismatch,

#[error("The PublicKeySet differs between ReissueShare entries")]
ReissueSharePublicKeySetMismatch,

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod mint;
mod spend_book;

pub use crate::{
builder::{DbcBuilder, Output, TransactionBuilder},
builder::{DbcBuilder, Output, ReissueRequestBuilder, TransactionBuilder},
dbc::Dbc,
dbc_content::{Amount, AmountSecrets, DbcContent},
dbc_transaction::DbcTransaction,
Expand Down
74 changes: 32 additions & 42 deletions src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ mod tests {

use crate::{
tests::{TinyInt, TinyVec},
DbcBuilder, DbcHelper, SimpleKeyManager, SimpleSigner, SimpleSpendBook,
DbcBuilder, DbcHelper, ReissueRequestBuilder, SimpleKeyManager, SimpleSigner,
SimpleSpendBook,
};

#[quickcheck]
Expand Down Expand Up @@ -389,35 +390,30 @@ mod tests {
}))
.build()?;

let sig_share = genesis_owner
.secret_key_share
.derive_child(&genesis_dbc.spend_key_index())
.sign(&reissue_tx.blinded().hash());

let sig = genesis_owner
.public_key_set
.combine_signatures(vec![(genesis_owner.index, &sig_share)])?;
let rr = ReissueRequestBuilder::new(reissue_tx.clone())
.add_dbc_signer(
gen_dbc_spend_key,
genesis_owner.public_key_set,
genesis_owner.index,
genesis_owner.secret_key_share,
)
.build()?;

let reissue_req = ReissueRequest {
transaction: reissue_tx.clone(),
input_ownership_proofs: HashMap::from_iter([(gen_dbc_spend_key, sig)]),
let reissue_share = match genesis_node.reissue(rr, BTreeSet::from_iter([gen_dbc_spend_key]))
{
Ok(rs) => {
// Verify that at least one output was present.
assert_ne!(n_outputs, 0);
rs
}
Err(Error::DbcReissueRequestDoesNotBalance) => {
// Verify that no outputs were present and we got correct validation error.
assert_eq!(n_outputs, 0);
return Ok(());
}
Err(e) => return Err(e),
};

let reissue_share =
match genesis_node.reissue(reissue_req, BTreeSet::from_iter([gen_dbc_spend_key])) {
Ok(rs) => {
// Verify that at least one output was present.
assert_ne!(n_outputs, 0);
rs
}
Err(Error::DbcReissueRequestDoesNotBalance) => {
// Verify that no outputs were present and we got correct validation error.
assert_eq!(n_outputs, 0);
return Ok(());
}
Err(e) => return Err(e),
};

// Aggregate ReissueShare to build output DBCs
let mut dbc_builder = DbcBuilder::new(reissue_tx);
dbc_builder = dbc_builder.add_reissue_share(reissue_share);
Expand Down Expand Up @@ -474,22 +470,16 @@ mod tests {
})
.build()?;

let sig_share = genesis_node
.key_manager
.sign_with_child_key(&genesis_dbc.spend_key_index(), &reissue_tx.blinded().hash())?;

let sig = genesis_node
.key_manager
.public_key_set()?
.combine_signatures(vec![sig_share.threshold_crypto()])?;

let reissue_req = ReissueRequest {
transaction: reissue_tx,
input_ownership_proofs: HashMap::from_iter([(gen_dbc_spend_key, sig)]),
};
let rr = ReissueRequestBuilder::new(reissue_tx)
.add_dbc_signer(
gen_dbc_spend_key,
genesis_owner.public_key_set,
genesis_owner.index,
genesis_owner.secret_key_share,
)
.build()?;

let reissue_share =
genesis_node.reissue(reissue_req, BTreeSet::from_iter([gen_dbc_spend_key]))?;
let reissue_share = genesis_node.reissue(rr, BTreeSet::from_iter([gen_dbc_spend_key]))?;
let t = reissue_share.dbc_transaction;
let s = reissue_share.mint_node_signatures;

Expand Down

0 comments on commit b0c3d5a

Please sign in to comment.