From 852ac41ddb46726268f81ff1c58bda5a52e6b9e3 Mon Sep 17 00:00:00 2001 From: David Rusu Date: Fri, 28 May 2021 12:17:52 -0400 Subject: [PATCH] feat(bench): benchmark split and merge reissus fixup fixup --- src/dbc.rs | 2 +- src/dbc_content.rs | 6 +- src/lib.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++ src/mint.rs | 106 ++++++++++++++++----------------- 4 files changed, 201 insertions(+), 57 deletions(-) diff --git a/src/dbc.rs b/src/dbc.rs index f2c6e87..b33717b 100644 --- a/src/dbc.rs +++ b/src/dbc.rs @@ -88,7 +88,7 @@ mod tests { DbcContent::new( input_hashes.clone(), amount, - i as u8, + i as u32, output_owner.public_key(), ) })); diff --git a/src/dbc_content.rs b/src/dbc_content.rs index 57f0e9e..3ebd7c0 100644 --- a/src/dbc_content.rs +++ b/src/dbc_content.rs @@ -21,7 +21,7 @@ impl BlindedOwner { owner: &PublicKey, parents: &BTreeSet, amount: u64, - output_number: u8, + output_number: u32, ) -> Self { let mut sha3 = Sha3::v256(); @@ -43,7 +43,7 @@ impl BlindedOwner { pub struct DbcContent { pub parents: BTreeSet, // Parent DBC's, acts as a nonce pub amount: u64, - pub output_number: u8, + pub output_number: u32, pub owner: BlindedOwner, } @@ -52,7 +52,7 @@ impl DbcContent { pub fn new( parents: BTreeSet, amount: u64, - output_number: u8, + output_number: u32, owner_key: PublicKey, ) -> Self { let owner = BlindedOwner::new(&owner_key, &parents, amount, output_number); diff --git a/src/lib.rs b/src/lib.rs index bafdcc9..639b5f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +#![feature(test)] // required for #[bench] macro #![allow(clippy::from_iter_instead_of_collect)] use serde::{Deserialize, Serialize}; @@ -105,10 +106,15 @@ fn sha3_256(input: &[u8]) -> [u8; 32] { #[cfg(test)] mod tests { + extern crate test; + use super::*; + use std::collections::{BTreeSet, HashMap, HashSet}; + use std::iter::FromIterator; use core::num::NonZeroU8; use quickcheck::{Arbitrary, Gen}; + use test::Bencher; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct TinyInt(u8); @@ -207,4 +213,142 @@ mod tests { "; assert_eq!(sha3_256(data), *expected); } + + fn genesis(amount: u64) -> (Mint, bls_dkg::outcome::Outcome, Dbc) { + let genesis_owner = bls_dkg_id(); + let (genesis, genesis_dbc) = Mint::genesis(genesis_owner.public_key_set.clone(), amount); + + (genesis, genesis_owner, genesis_dbc) + } + + #[bench] + fn bench_reissue_1_to_100(b: &mut Bencher) { + let n_outputs: u32 = 100; + let (mut genesis, genesis_owner, genesis_dbc) = genesis(n_outputs as u64); + + let inputs = HashSet::from_iter(vec![genesis_dbc.clone()]); + let input_hashes = BTreeSet::from_iter(inputs.iter().map(|in_dbc| in_dbc.name())); + + let output_owner = bls_dkg_id(); + let owner_pub_key = output_owner.public_key_set.public_key(); + let outputs = (0..n_outputs) + .into_iter() + .map(|i| DbcContent::new(input_hashes.clone(), 1, i, owner_pub_key)) + .collect(); + + let transaction = MintTransaction { inputs, outputs }; + + let sig_share = genesis_owner + .secret_key_share + .sign(&transaction.blinded().hash()); + + let sig = genesis_owner + .public_key_set + .combine_signatures(vec![(0, &sig_share)]) + .unwrap(); + + let mint_request = MintRequest { + transaction, + input_ownership_proofs: HashMap::from_iter(vec![( + genesis_dbc.name(), + (genesis_owner.public_key_set.public_key(), sig), + )]), + }; + + let spendbook = genesis.snapshot_spendbook(); + b.iter(|| { + genesis.reset_spendbook(spendbook.clone()); + genesis + .reissue(mint_request.clone(), input_hashes.clone()) + .unwrap(); + }); + } + + #[bench] + fn bench_reissue_100_to_1(b: &mut Bencher) { + let n_outputs: u32 = 100; + let (mut genesis, genesis_owner, genesis_dbc) = genesis(n_outputs as u64); + + let inputs = HashSet::from_iter(vec![genesis_dbc.clone()]); + let input_hashes = BTreeSet::from_iter(inputs.iter().map(|in_dbc| in_dbc.name())); + + let owners: Vec<_> = (0..n_outputs).into_iter().map(|_| bls_dkg_id()).collect(); + let outputs = Vec::from_iter((0..n_outputs).into_iter().map(|i| { + DbcContent::new( + input_hashes.clone(), + 1, + i, + owners[i as usize].public_key_set.public_key(), + ) + })); + + let transaction = MintTransaction { + inputs, + outputs: HashSet::from_iter(outputs.clone()), + }; + + let sig_share = genesis_owner + .secret_key_share + .sign(&transaction.blinded().hash()); + + let sig = genesis_owner + .public_key_set + .combine_signatures(vec![(0, &sig_share)]) + .unwrap(); + + let mint_request = MintRequest { + transaction, + input_ownership_proofs: HashMap::from_iter(vec![( + genesis_dbc.name(), + (genesis_owner.public_key_set.public_key(), sig), + )]), + }; + + let (transaction, transaction_sigs) = genesis + .reissue(mint_request.clone(), input_hashes.clone()) + .unwrap(); + + let dbcs = Vec::from_iter(outputs.into_iter().map(|content| Dbc { + content, + transaction: transaction.clone(), + transaction_sigs: transaction_sigs.clone(), + })); + + let merged_output = DbcContent::new( + BTreeSet::from_iter(dbcs.iter().map(Dbc::name)), + n_outputs as u64, + 0, + bls_dkg_id().public_key_set.public_key(), + ); + + let merge_transaction = MintTransaction { + inputs: HashSet::from_iter(dbcs.clone()), + outputs: HashSet::from_iter([merged_output]), + }; + + let input_ownership_proofs = HashMap::from_iter(dbcs.iter().enumerate().map(|(i, dbc)| { + let sig_share = owners[i] + .secret_key_share + .sign(merge_transaction.blinded().hash()); + let sig = owners[i] + .public_key_set + .combine_signatures(vec![(0, &sig_share)]) + .unwrap(); + (dbc.name(), (owners[i].public_key_set.public_key(), sig)) + })); + + let merge_mint_request = MintRequest { + transaction: merge_transaction, + input_ownership_proofs, + }; + let inputs = merge_mint_request.transaction.blinded().inputs; + + let spendbook = genesis.snapshot_spendbook(); + b.iter(|| { + genesis.reset_spendbook(spendbook.clone()); + genesis + .reissue(merge_mint_request.clone(), inputs.clone()) + .unwrap(); + }); + } } diff --git a/src/mint.rs b/src/mint.rs index d388828..cb369a2 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::iter::FromIterator; use crate::{ Dbc, DbcContent, DbcContentHash, DbcTransaction, Error, Hash, KeyCache, KeyManager, PublicKey, @@ -23,7 +24,7 @@ use crate::{ pub type InputSignatures = BTreeMap; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct SpendBook { pub transactions: BTreeMap, } @@ -47,8 +48,8 @@ pub struct MintTransaction { impl MintTransaction { pub fn blinded(&self) -> DbcTransaction { DbcTransaction { - inputs: self.inputs.iter().map(|i| i.name()).collect(), - outputs: self.outputs.iter().map(|i| i.hash()).collect(), + inputs: BTreeSet::from_iter(self.inputs.iter().map(|i| i.name())), + outputs: BTreeSet::from_iter(self.outputs.iter().map(|i| i.hash())), } } @@ -84,13 +85,13 @@ impl MintTransaction { fn validate_outputs(&self) -> Result<()> { // Validate outputs are numbered 0..N_OUTPUTS - let number_set = self - .outputs - .iter() - .map(|dbc_content| dbc_content.output_number.into()) - .collect::>(); + let number_set = BTreeSet::from_iter( + self.outputs + .iter() + .map(|dbc_content| dbc_content.output_number), + ); - let expected_number_set = (0..self.outputs.len()).into_iter().collect::>(); + let expected_number_set = BTreeSet::from_iter(0..self.outputs.len() as u32); if number_set != expected_number_set { println!( @@ -130,12 +131,12 @@ impl Mint { let genesis_input = Hash([0u8; 32]); - let parents = vec![genesis_input].into_iter().collect(); + let parents = BTreeSet::from_iter(vec![genesis_input]); let content = DbcContent::new(parents, amount, 0, genesis_key.public_key()); let transaction = DbcTransaction { - inputs: vec![genesis_input].into_iter().collect(), - outputs: vec![content.hash()].into_iter().collect(), + inputs: BTreeSet::from_iter(vec![genesis_input]), + outputs: BTreeSet::from_iter(vec![content.hash()]), }; let mut spendbook = SpendBook::default(); @@ -146,9 +147,10 @@ impl Mint { let dbc = Dbc { content, transaction, - transaction_sigs: vec![(genesis_input, (key_mgr.public_key(), transaction_sig))] - .into_iter() - .collect(), + transaction_sigs: BTreeMap::from_iter(vec![( + genesis_input, + (key_mgr.public_key(), transaction_sig), + )]), }; (Self { key_mgr, spendbook }, dbc) @@ -224,14 +226,21 @@ impl Mint { .zip(std::iter::repeat((self.public_key(), sig))) .collect() } + + // Used in testing / benchmarking + pub(crate) fn snapshot_spendbook(&self) -> SpendBook { + self.spendbook.clone() + } + + // Used in testing / benchmarking + pub(crate) fn reset_spendbook(&mut self, spendbook: SpendBook) { + self.spendbook = spendbook + } } #[cfg(test)] mod tests { use super::*; - use std::collections::BTreeSet; - use std::iter::FromIterator; - use quickcheck_macros::quickcheck; use crate::tests::{TinyInt, TinyVec}; @@ -260,18 +269,14 @@ mod tests { let input_hashes = BTreeSet::from_iter(inputs.iter().map(|in_dbc| in_dbc.name())); let output_owner = crate::bls_dkg_id(); - let outputs = output_amounts - .iter() - .enumerate() - .map(|(i, amount)| { - DbcContent::new( - input_hashes.clone(), - *amount, - i as u8, - output_owner.public_key_set.public_key(), - ) - }) - .collect(); + let outputs = HashSet::from_iter(output_amounts.iter().enumerate().map(|(i, amount)| { + DbcContent::new( + input_hashes.clone(), + *amount, + i as u32, + output_owner.public_key_set.public_key(), + ) + })); let transaction = MintTransaction { inputs, outputs }; @@ -418,19 +423,19 @@ mod tests { let output_amounts = Vec::from_iter( output_amounts .into_iter() - .map(|(number, amount)| (number.coerce::(), amount.coerce::())), + .map(|(number, amount)| (number.coerce::(), amount.coerce::())), ); let extra_output_parents = - Vec::from_iter(extra_output_parents.into_iter().map(TinyInt::coerce::)); + Vec::from_iter(extra_output_parents.into_iter().map(TinyInt::coerce::)); let inputs_to_create_owner_proofs = - BTreeSet::from_iter(input_owner_proofs.into_iter().map(TinyInt::coerce::)); + BTreeSet::from_iter(input_owner_proofs.into_iter().map(TinyInt::coerce::)); let inputs_to_create_invalid_owner_proofs = BTreeSet::from_iter( invalid_input_owner_proofs .into_iter() - .map(TinyInt::coerce::), + .map(TinyInt::coerce::), ); let genesis_owner = crate::bls_dkg_id(); @@ -438,7 +443,7 @@ mod tests { let (mut genesis, genesis_dbc) = Mint::genesis(genesis_owner.public_key_set.clone(), genesis_amount); - let mut owners: BTreeMap = Default::default(); + let mut owners: BTreeMap = Default::default(); let gen_inputs = HashSet::from_iter(vec![genesis_dbc.clone()]); let gen_input_hashes = BTreeSet::from_iter(gen_inputs.iter().map(Dbc::name)); @@ -446,8 +451,13 @@ mod tests { HashSet::from_iter(input_amounts.iter().enumerate().map(|(i, amount)| { let owner = crate::bls_dkg_id(); let owner_public_key = owner.public_key_set.public_key(); - owners.insert(i as u8, owner); - DbcContent::new(gen_input_hashes.clone(), *amount, i as u8, owner_public_key) + owners.insert(i as u32, owner); + DbcContent::new( + gen_input_hashes.clone(), + *amount, + i as u32, + owner_public_key, + ) })); let mut mint_request = MintRequest { @@ -668,16 +678,6 @@ mod tests { ); let input_content_hashes = BTreeSet::from_iter(vec![input_content.hash()]); - let output_owner = crate::bls_dkg_id(); - let amount = 100; - let output_number = 0; - let owner = crate::BlindedOwner::new( - &output_owner.public_key_set.public_key(), - &input_content_hashes, - amount, - output_number, - ); - let fraudulant_reissue_result = genesis.reissue( MintRequest { transaction: MintTransaction { @@ -689,12 +689,12 @@ mod tests { }, transaction_sigs: Default::default(), }]), - outputs: HashSet::from_iter(vec![DbcContent { - parents: input_content_hashes.clone(), - amount, - output_number, - owner, - }]), + outputs: HashSet::from_iter(vec![DbcContent::new( + input_content_hashes.clone(), + 100, + 0, + crate::bls_dkg_id().public_key_set.public_key(), + )]), }, input_ownership_proofs: HashMap::default(), },