From b0c3d5ae1b6e49534ddef75763141530cbace9dd Mon Sep 17 00:00:00 2001 From: danda Date: Tue, 7 Sep 2021 17:21:15 -0700 Subject: [PATCH] feat: add ReissueRequestBuilder to simplify aggregating dbc ownership proofs --- src/builder.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++-- src/error.rs | 3 ++ src/lib.rs | 2 +- src/mint.rs | 74 ++++++++++++++------------------ 4 files changed, 146 insertions(+), 47 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 635c216..b8e4e01 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -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 @@ -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, + #[allow(clippy::type_complexity)] + pub signers_by_dbc: HashMap>, +} + +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( + 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( + 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 { + let transaction = match self.reissue_transaction { + Some(rt) => rt, + None => return Err(Error::NoReissueTransaction), + }; + + let mut input_ownership_proofs: HashMap = 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 = 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 = 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 = 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. diff --git a/src/error.rs b/src/error.rs index 741dbee..bbd2e7d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index 1751250..1d86760 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, diff --git a/src/mint.rs b/src/mint.rs index efe707c..4bf432f 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -309,7 +309,8 @@ mod tests { use crate::{ tests::{TinyInt, TinyVec}, - DbcBuilder, DbcHelper, SimpleKeyManager, SimpleSigner, SimpleSpendBook, + DbcBuilder, DbcHelper, ReissueRequestBuilder, SimpleKeyManager, SimpleSigner, + SimpleSpendBook, }; #[quickcheck] @@ -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); @@ -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;