diff --git a/Cargo.toml b/Cargo.toml index c7292ff..93d9b54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,31 @@ edition = "2018" [features] dkg = [ "bls_dkg" ] +#-- Notes: +#-- 1. We want to be able to say cargo build --features serde +#-- 2. Feature name must be different from dependency name (or cargo gives error). +#-- 3. We must define serde feature explicitly in order to pass --features serde to crates we depend on. +#-- 4. Given 1-3, a solution *should* be to alias serde dep. (serde --> serdes) and change code refs. +#-- 5. However that results in Serialize macro "can't find crate" error. +#-- 6. So for now we give up and name this feature "serdes" instead. +# 7. But there is hope, because... +#-- 8. [2] is fixed with Namespaced Features in latest nightly. Once in stable, +#-- everything will work as desired. +#-- See: https://doc.rust-lang.org/cargo/reference/unstable.html#namespaced-features +#-- So this can change to: +#-- serde = ["dep:serde", ... ] +serdes = [ + "serde", + "ringct-serde", +# "blsttc-serde", +] + +ringct-serde = ["blst_ringct/serde"] +#blsttc-serde = ["blsttc/serde"] + [dependencies] -serde_json = "1.0.64" thiserror = "1.0.24" -quickcheck = "1" +quickcheck = {git="https://github.com/davidrusu/quickcheck.git", branch="only-build-debug-reprs-on-failure"} quickcheck_macros = "1" rand = "0.7.1" blst_ringct = {git="https://github.com/maidsafe/blst-ringct"} @@ -40,30 +61,30 @@ xor_name = "3.1.0" version = "2.0.0" features = [ "sha3" ] - [dependencies.serde] - version = "1.0.111" +[dependencies.serde] + version = "1.0.133" features = [ "derive", "rc" ] + optional = true [dev-dependencies] criterion = "0.3" anyhow = "1.0.40" -serde = "1.0.126" rand = "0.7.1" rustyline = "8.0.0" bincode = "1.3.3" [dev-dependencies.sn_dbc] path = "." - features = [ "dkg" ] + features = [ "dkg", "serdes" ] [target."cfg(unix)".dev-dependencies] termios = "0.3.3" -[[bench]] -name = "reissue" -harness = false -required-features = [ "dkg" ] +# [[bench]] +# name = "reissue" +# harness = false +# required-features = [ "dkg" ] -#[[example]] -#name = "mint-repl" -#path = "examples/mint-repl/mint-repl.rs" +# [[example]] +# name = "mint-repl" +# path = "examples/mint-repl/mint-repl.rs" diff --git a/src/amount_secrets.rs b/src/amount_secrets.rs index 0de41a3..44fd4e2 100644 --- a/src/amount_secrets.rs +++ b/src/amount_secrets.rs @@ -7,7 +7,6 @@ // permissions and limitations relating to use of the SAFE Network Software. use blst_ringct::RevealedCommitment; -use blstrs::Scalar; use blsttc::{ Ciphertext, DecryptionShare, IntoFr, PublicKey, PublicKeySet, SecretKey, SecretKeySet, SecretKeyShare, @@ -17,20 +16,28 @@ use std::collections::BTreeMap; use std::convert::Into; use std::convert::TryFrom; -use crate::{Amount, Error}; +use crate::{Amount, BlindingFactor, Error, SecretKeyBlst}; -// note: Amount should move into blst_ringct crate. +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +// todo: Amount should probably move into blst_ringct crate. // (or else blst_ringct::RevealedCommitment should be made generic over Amount type) // note: AmountSecrets wraps RevealedCommitment to provide some methods // for decrypting from Ciphertext using various blsttc components, // eg SecretKey, SecretKeyShare, SecretKeySet, DecryptionShare // -// Once blst_ringct uses blsttc, perhaps AmountSecrets can go away. +// todo: perhaps AmountSecrets should be renamed to be more consistent with +// RevealedCommitment, since it is just a NewType wrapper. +// +// Once blst_ringct uses blsttc, perhaps AmountSecrets functionality could +// move into RevealedCommitment, and AmountSecrets goes away entirely. -const AMT_SIZE: usize = 8; // Amount size: 8 bytes (u64) -const BF_SIZE: usize = 32; // Blinding factor size: 32 bytes (Scalar) +const AMT_SIZE: usize = std::mem::size_of::(); // Amount size: 8 bytes (u64) +const BF_SIZE: usize = std::mem::size_of::(); // Blinding factor size: 32 bytes (BlindingFactor) +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct AmountSecrets(RevealedCommitment); @@ -39,7 +46,7 @@ impl AmountSecrets { self.0.value } - pub fn blinding_factor(&self) -> Scalar { + pub fn blinding_factor(&self) -> SecretKeyBlst { self.0.blinding } @@ -56,7 +63,7 @@ impl AmountSecrets { b }); let mut b = [0u8; BF_SIZE]; - let blinding_factor = Scalar::from_bytes_le({ + let blinding_factor = BlindingFactor::from_bytes_le({ b.copy_from_slice(&bytes[AMT_SIZE..]); &b }) @@ -79,7 +86,7 @@ impl AmountSecrets { b }); let mut b = [0u8; BF_SIZE]; - let blinding_factor = Scalar::from_bytes_le({ + let blinding_factor = BlindingFactor::from_bytes_le({ b.copy_from_slice(&bytes[AMT_SIZE..]); &b }) @@ -107,9 +114,9 @@ impl From for AmountSecrets { } } -impl From<(Amount, Scalar)> for AmountSecrets { +impl From<(Amount, BlindingFactor)> for AmountSecrets { /// create AmountSecrets from an amount and a randomly generated blinding factor - fn from(params: (Amount, Scalar)) -> Self { + fn from(params: (Amount, BlindingFactor)) -> Self { let (value, blinding) = params; Self(RevealedCommitment { value, blinding }) diff --git a/src/blst.rs b/src/blst.rs new file mode 100644 index 0000000..1f165f4 --- /dev/null +++ b/src/blst.rs @@ -0,0 +1,111 @@ +// Copyright 2021 MaidSafe.net limited. +// +// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. +// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed +// 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. + +// This module defines blstrs aliases, wrappers, and helpers. +// +// blstrs types Scalar and G1Affine are used to represent distinct concepts +// in ringct such as: +// Scalar: SecretKey, BlindingFactor +// G1Affine: Commitment, PublicKey, KeyImage +// +// We provide type aliases to make the usage in each context clearer and to make the +// the sn_dbc public API simpler so that the caller should not need to depend on blstrs +// and use its types directly. +// +// Even sn_dbc uses the type aliases rather than directly using the blstrs types. +// +// We could consider moving some or all of this lower into blst_ringct to make these +// crates consistent. + +use blstrs::{G1Affine, Scalar}; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; + +use blsttc::{PublicKey, SecretKey}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +pub type SecretKeyBlst = Scalar; +pub type PublicKeyBlst = G1Affine; +pub type Commitment = G1Affine; +pub type BlindingFactor = Scalar; + +// We use a NewType wrapper for KeyImage because it needs to be used +// as a key in a BTreeMap. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Default)] +pub struct KeyImage(G1Affine); + +impl PartialEq for KeyImage { + fn eq(&self, other: &Self) -> bool { + self.0.to_compressed() == other.0.to_compressed() + } +} + +impl AsRef for KeyImage { + fn as_ref(&self) -> &G1Affine { + &self.0 + } +} + +impl Eq for KeyImage {} + +impl PartialOrd for KeyImage { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for KeyImage { + fn cmp(&self, other: &Self) -> Ordering { + self.0.to_compressed().cmp(&other.0.to_compressed()) + } +} + +impl Hash for KeyImage { + fn hash(&self, state: &mut H) { + let bytes = self.0.to_compressed(); + bytes.hash(state); + } +} + +impl From for KeyImage { + fn from(k: G1Affine) -> Self { + Self(k) + } +} + +// temporary: should go away once blsttc is integrated with with blstrs +pub struct BlsHelper {} + +impl BlsHelper { + #[allow(dead_code)] + pub fn blsttc_to_blstrs_sk(sk: SecretKey) -> SecretKeyBlst { + let bytes = sk.to_bytes(); + SecretKeyBlst::from_bytes_be(&bytes).unwrap() + } + + pub fn blsttc_to_blstrs_pubkey(pk: &PublicKey) -> PublicKeyBlst { + let bytes = pk.to_bytes(); + // fixme: unwrap + PublicKeyBlst::from_compressed(&bytes).unwrap() + } + + pub fn blstrs_to_blsttc_pubkey(pk: &PublicKeyBlst) -> PublicKey { + let bytes = pk.to_compressed(); + // fixme: unwrap + PublicKey::from_bytes(bytes).unwrap() + } + + pub fn blstrs_to_blsttc_sk(sk: SecretKeyBlst) -> SecretKey { + let bytes = sk.to_bytes_be(); + // fixme: unwrap + SecretKey::from_bytes(bytes).unwrap() + } +} diff --git a/src/builder.rs b/src/builder.rs index b9f6a7c..658ca0f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,20 +1,24 @@ use blst_ringct::ringct::{RingCtMaterial, RingCtTransaction}; pub use blst_ringct::{DecoyInput, MlsagMaterial, Output, RevealedCommitment, TrueInput}; use blstrs::group::Curve; -pub use blstrs::{G1Affine, Scalar}; use blsttc::{PublicKeySet, SignatureShare}; use bulletproofs::PedersenGens; use rand_core::RngCore; use std::collections::{BTreeMap, BTreeSet, HashSet}; use crate::{ - Amount, AmountSecrets, Dbc, DbcContent, DerivedOwner, Error, KeyImage, NodeSignature, - ReissueRequest, ReissueShare, Result, SpentProof, SpentProofShare, + Amount, AmountSecrets, Commitment, Dbc, DbcContent, DerivedOwner, Error, KeyImage, + NodeSignature, PublicKeyBlst, ReissueRequest, ReissueShare, Result, SecretKeyBlst, SpentProof, + SpentProofShare, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub type OutputOwnerMap = BTreeMap; -#[derive(Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default)] pub struct TransactionBuilder { material: RingCtMaterial, output_owners: OutputOwnerMap, @@ -56,7 +60,7 @@ impl TransactionBuilder { pub fn add_input_by_secrets( mut self, - secret_key: Scalar, + secret_key: SecretKeyBlst, amount_secrets: AmountSecrets, decoy_inputs: Vec, mut rng: impl RngCore, @@ -74,7 +78,7 @@ impl TransactionBuilder { pub fn add_inputs_by_secrets( mut self, - secrets: Vec<(Scalar, AmountSecrets, Vec)>, + secrets: Vec<(SecretKeyBlst, AmountSecrets, Vec)>, mut rng: impl RngCore, ) -> Self { for (secret_key, amount_secrets, decoy_inputs) in secrets.into_iter() { @@ -84,8 +88,7 @@ impl TransactionBuilder { } pub fn add_output(mut self, output: Output, owner: DerivedOwner) -> Self { - self.output_owners - .insert(output.public_key().to_compressed(), owner); + self.output_owners.insert(output.public_key().into(), owner); self.material.outputs.push(output); self } @@ -100,7 +103,7 @@ impl TransactionBuilder { self } - pub fn input_owners(&self) -> Vec { + pub fn input_owners(&self) -> Vec { self.material.public_keys() } @@ -139,6 +142,7 @@ impl TransactionBuilder { /// Builds a ReissueRequest from a RingCtTransaction and /// any number of (input) DBC spent proof shares. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug)] pub struct ReissueRequestBuilder { pub transaction: RingCtTransaction, @@ -196,10 +200,10 @@ impl ReissueRequestBuilder { .map(NodeSignature::threshold_crypto), )?; - let public_commitments: Vec = any_share.public_commitments.clone(); + let public_commitments: Vec = any_share.public_commitments.clone(); let spent_proof = SpentProof { - key_image: any_share.key_image, + key_image: any_share.key_image.clone(), spentbook_pub_key, spentbook_sig, public_commitments, @@ -222,6 +226,7 @@ impl ReissueRequestBuilder { /// A Builder for aggregating ReissueShare (Mint::reissue() results) /// from multiple mint nodes and combining signatures to /// generate the final Dbc outputs. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug)] pub struct DbcBuilder { pub revealed_commitments: Vec, @@ -292,7 +297,7 @@ impl DbcBuilder { if !rs .mint_node_signatures .keys() - .any(|k| *k == mlsag.key_image.to_compressed()) + .any(|k| *k == mlsag.key_image.into()) { return Err(Error::ReissueShareMintNodeSignatureNotFoundForInput); } @@ -323,7 +328,7 @@ impl DbcBuilder { let mint_sig = mint_public_key_set.combine_signatures(mint_sig_shares_ref)?; let pc_gens = PedersenGens::default(); - let output_commitments: Vec<(G1Affine, RevealedCommitment)> = self + let output_commitments: Vec<(Commitment, RevealedCommitment)> = self .revealed_commitments .iter() .map(|r| (r.commit(&pc_gens).to_affine(), *r)) @@ -334,7 +339,7 @@ impl DbcBuilder { .iter() .map(|output| { self.output_owners - .get(&output.public_key().to_compressed()) + .get(&(*output.public_key()).into()) .ok_or(Error::PublicKeyNotFound) }) .collect::>()?; @@ -364,7 +369,7 @@ impl DbcBuilder { .iter() .map(|mlsag| { ( - mlsag.key_image.to_compressed(), + mlsag.key_image.into(), (mint_public_key_set.public_key(), mint_sig.clone()), ) }) diff --git a/src/dbc.rs b/src/dbc.rs index e79f420..6b5f4a5 100644 --- a/src/dbc.rs +++ b/src/dbc.rs @@ -6,9 +6,8 @@ // 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. -use crate::{dbc_content::OwnerPublicKey, DbcContent, Error, KeyManager, Result}; - -use crate::{AmountSecrets, BlsHelper, SpentProof, TransactionValidator}; +use crate::{AmountSecrets, BlsHelper, KeyImage, PublicKeyBlst, SpentProof, TransactionValidator}; +use crate::{DbcContent, Error, KeyManager, Result}; use blst_ringct::ringct::{OutputProof, RingCtTransaction}; use blst_ringct::RevealedCommitment; use blstrs::group::Curve; @@ -16,12 +15,10 @@ use blsttc::{PublicKey, SecretKey, Signature}; use std::collections::{BTreeMap, BTreeSet}; use tiny_keccak::{Hasher, Sha3}; -// todo: move this someplace better, maybe lib.rs. -// Alternatively, we could perhaps wrap G1Affine to add Ord so it can -// be used in a BTreeMap directly -pub type KeyImage = [u8; 48]; // G1 compressed +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; -// #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct Dbc { pub content: DbcContent, @@ -32,9 +29,13 @@ pub struct Dbc { impl Dbc { /// Read the DBC owner - pub fn owner(&self, base_sk: &SecretKey) -> Result { - let pubkey = self.content.derive_owner(base_sk)?; - Ok(BlsHelper::blsttc_to_blstrs_pubkey(&pubkey)) + pub fn owner(&self, base_sk: &SecretKey) -> Result { + self.content.derive_owner(base_sk) + } + + // convenience fn, can go away once blsttc integrated with blst_ringct. + pub(crate) fn owner_blst(&self, base_sk: &SecretKey) -> Result { + Ok(BlsHelper::blsttc_to_blstrs_pubkey(&self.owner(base_sk)?)) } pub fn has_secret_key(&self) -> bool { @@ -49,7 +50,7 @@ impl Dbc { sha3.update(&self.transaction.hash()); for (in_key, (mint_key, mint_sig)) in self.transaction_sigs.iter() { - sha3.update(in_key); + sha3.update(&in_key.as_ref().to_compressed()); sha3.update(&mint_key.to_bytes()); sha3.update(&mint_sig.to_bytes()); } @@ -79,7 +80,7 @@ impl Dbc { &self.spent_proofs, )?; - let owner = self.owner(base_sk)?; + let owner = self.owner_blst(base_sk)?; if !self .transaction @@ -134,7 +135,7 @@ impl Dbc { } fn my_output_proof(&self, base_sk: &SecretKey) -> Result<&OutputProof> { - let owner = self.owner(base_sk)?; + let owner = self.owner_blst(base_sk)?; self.transaction .outputs .iter() @@ -154,7 +155,7 @@ mod tests { use crate::tests::{init_genesis, NonZeroTinyInt, SpentBookMock, TinyInt}; use crate::{ Amount, AmountSecrets, BlsHelper, DbcBuilder, DerivedOwner, Hash, KeyManager, Owner, - ReissueRequest, ReissueRequestBuilder, SimpleKeyManager, SimpleSigner, + ReissueRequest, ReissueRequestBuilder, SecretKeyBlst, SimpleKeyManager, SimpleSigner, }; use blst_ringct::ringct::RingCtMaterial; use blst_ringct::{Output, RevealedCommitment}; @@ -212,7 +213,7 @@ mod tests { )) .build(&mut rng8)?; - let key_image = reissue_tx.mlsags[0].key_image.to_compressed(); + let key_image = reissue_tx.mlsags[0].key_image.into(); let spent_proof_share = spentbook.log_spent(key_image, reissue_tx.clone())?; let rr = ReissueRequestBuilder::new(reissue_tx) @@ -315,18 +316,17 @@ mod tests { let output_dbcs = dbc_builder.build()?; // The outputs become inputs for next reissue. - let inputs: Vec<(blstrs::Scalar, AmountSecrets, Vec)> = - output_dbcs - .into_iter() - .map(|(_dbc, derived_owner, amount_secrets)| { - let decoy_inputs = vec![]; // todo - ( - BlsHelper::blsttc_to_blstrs_sk(derived_owner.derive_secret_key().unwrap()), - amount_secrets, - decoy_inputs, - ) - }) - .collect(); + let inputs: Vec<(SecretKeyBlst, AmountSecrets, Vec)> = output_dbcs + .into_iter() + .map(|(_dbc, derived_owner, amount_secrets)| { + let decoy_inputs = vec![]; // todo + ( + BlsHelper::blsttc_to_blstrs_sk(derived_owner.derive_secret_key().unwrap()), + amount_secrets, + decoy_inputs, + ) + }) + .collect(); let derived_owner = DerivedOwner::from_owner_base(Owner::from_random_secret_key(&mut rng), &mut rng8); @@ -353,7 +353,7 @@ mod tests { .enumerate() { let spent_proof_share = - spentbook.log_spent(mlsag.key_image.to_compressed(), reissue_tx.clone())?; + spentbook.log_spent(mlsag.key_image.into(), reissue_tx.clone())?; rr_builder = rr_builder.add_spent_proof_share(i, spent_proof_share); } @@ -385,9 +385,9 @@ mod tests { fuzzed_transaction_sigs.extend( reissue_share .mint_node_signatures - .iter() + .into_iter() .take(n_valid_sigs.coerce()) - .map(|(in_owner, _)| (*in_owner, (mint_pk, mint_sig.clone()))), + .map(|(in_owner, _)| (in_owner, (mint_pk, mint_sig.clone()))), ); let mut repeating_inputs = reissue_tx @@ -410,7 +410,7 @@ mod tests { .combine_signatures(vec![trans_sig_share.threshold_crypto()]) .unwrap(); fuzzed_transaction_sigs.insert( - input.key_image.to_compressed(), + input.key_image.into(), (key_manager.public_key_set()?.public_key(), trans_sig), ); } @@ -427,7 +427,7 @@ mod tests { .unwrap(); fuzzed_transaction_sigs.insert( - input.key_image.to_compressed(), + input.key_image.into(), ( mint_node.key_manager.public_key_set()?.public_key(), wrong_msg_mint_sig, @@ -439,7 +439,7 @@ mod tests { // Valid mint signatures for inputs not present in the transaction for _ in 0..n_extra_input_sigs.coerce() { fuzzed_transaction_sigs.insert( - [0u8; 48], + Default::default(), ( mint_node.key_manager().public_key_set()?.public_key(), mint_sig.clone(), @@ -457,7 +457,7 @@ mod tests { let key_manager = mint_node.key_manager(); let validation_res = dbc.confirm_valid(&derived_owner.base_secret_key()?, key_manager); - let dbc_owner = dbc.owner(&derived_owner.base_secret_key()?)?; + let dbc_owner = dbc.owner_blst(&derived_owner.base_secret_key()?)?; // Check if commitment in AmountSecrets matches the commitment in tx OutputProof let commitments_match = dbc @@ -495,11 +495,11 @@ mod tests { Err(Error::UnknownInput) => { assert!(n_extra_input_sigs.coerce::() > 0); assert_ne!( - Vec::from_iter(dbc.transaction_sigs.keys().copied()), + Vec::from_iter(dbc.transaction_sigs.keys().cloned()), dbc.transaction .mlsags .iter() - .map(|m| m.key_image.to_compressed()) + .map(|m| m.key_image.into()) .collect::>() ); } diff --git a/src/dbc_content.rs b/src/dbc_content.rs index 217d1ab..e8fbd3a 100644 --- a/src/dbc_content.rs +++ b/src/dbc_content.rs @@ -7,20 +7,20 @@ // permissions and limitations relating to use of the SAFE Network Software. use crate::{AmountSecrets, DerivationIndex, Owner}; -use blstrs::G1Affine; use blsttc::{Ciphertext, PublicKey, SecretKey}; -// use serde::{Deserialize, Serialize}; use tiny_keccak::{Hasher, Sha3}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + use crate::{Error, Hash, Result}; // note: Amount should move into blst_ringct crate. // (or else blst_ringct::RevealedCommitment should be made generic over Amount type) pub type Amount = u64; -pub type OwnerPublicKey = G1Affine; -// #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, PartialEq, Eq, Clone)] pub struct DbcContent { /// This is the owner's well-known key. owner_base.public_key() may be published diff --git a/src/derived_owner.rs b/src/derived_owner.rs index 2c1b0d1..f5af9ee 100644 --- a/src/derived_owner.rs +++ b/src/derived_owner.rs @@ -7,19 +7,21 @@ // permissions and limitations relating to use of the SAFE Network Software. use crate::{Error, PublicKey, Result}; -use blsttc::SecretKey; -// use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use blsttc::{serde_impl::SerdeSecret, SecretKey}; use std::fmt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + use rand::distributions::Standard; use rand::Rng; pub type DerivationIndex = [u8; 32]; -// #[derive(Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone)] pub enum Owner { - SecretKey(SecretKey), + SecretKey(SerdeSecret), PublicKey(PublicKey), } @@ -38,7 +40,7 @@ impl fmt::Debug for Owner { impl From for Owner { fn from(s: SecretKey) -> Self { - Self::SecretKey(s) + Self::SecretKey(SerdeSecret(s)) } } @@ -70,15 +72,15 @@ impl Owner { pub fn secret_key(&self) -> Result { match self { - Self::SecretKey(sk) => Ok(sk.clone()), + Self::SecretKey(sk) => Ok(sk.inner().clone()), Self::PublicKey(_pk) => Err(Error::SecretKeyUnavailable), } } pub fn derive(&self, i: &DerivationIndex) -> Self { match self { - Self::SecretKey(sk) => Self::SecretKey(sk.derive_child(i)), - Self::PublicKey(pk) => Self::PublicKey(pk.derive_child(i)), + Self::SecretKey(sk) => Self::from(sk.inner().derive_child(i)), + Self::PublicKey(pk) => Self::from(pk.derive_child(i)), } } @@ -98,11 +100,11 @@ impl Owner { pub fn from_random_secret_key(mut rng: impl rand::RngCore) -> Self { let sk: SecretKey = rng.sample(Standard); - Self::SecretKey(sk) + Self::from(sk) } } -// #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] pub struct DerivedOwner { pub owner_base: Owner, diff --git a/src/key_manager.rs b/src/key_manager.rs index c98fc78..71d48d9 100644 --- a/src/key_manager.rs +++ b/src/key_manager.rs @@ -9,10 +9,13 @@ use crate::{Error, Hash, Result}; use blsttc::{serde_impl::SerdeSecret, SecretKeyShare, SignatureShare}; pub use blsttc::{PublicKey, PublicKeySet, Signature}; -use serde::{Deserialize, Serialize}; use std::collections::HashSet; -#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct NodeSignature { index: u64, sig: SignatureShare, @@ -44,7 +47,8 @@ pub trait KeyManager { fn verify_known_key(&self, key: &PublicKey) -> Result<(), Self::Error>; } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone)] pub struct SimpleSigner { public_key_set: PublicKeySet, secret_key_share: (u64, SerdeSecret), @@ -89,7 +93,8 @@ impl SimpleSigner { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone)] pub struct SimpleKeyManager { signer: SimpleSigner, cache: Keys, @@ -140,7 +145,8 @@ impl KeyManager for SimpleKeyManager { } } -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, Clone)] struct Keys(HashSet); impl From> for Keys { diff --git a/src/lib.rs b/src/lib.rs index 1f236b6..8026b7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,10 +7,10 @@ // permissions and limitations relating to use of the SAFE Network Software. #![allow(clippy::from_iter_instead_of_collect)] -use serde::{Deserialize, Serialize}; use std::fmt; mod amount_secrets; +mod blst; mod builder; mod dbc; mod dbc_content; @@ -23,8 +23,9 @@ mod validation; pub use crate::{ amount_secrets::AmountSecrets, + blst::{BlindingFactor, BlsHelper, Commitment, KeyImage, PublicKeyBlst, SecretKeyBlst}, builder::{DbcBuilder, Output, OutputOwnerMap, ReissueRequestBuilder, TransactionBuilder}, - dbc::{Dbc, KeyImage}, + dbc::Dbc, dbc_content::{Amount, DbcContent}, derived_owner::{DerivationIndex, DerivedOwner, Owner}, error::{Error, Result}, @@ -37,7 +38,11 @@ pub use crate::{ validation::TransactionValidator, }; -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Hash([u8; 32]); impl Hash { @@ -101,65 +106,6 @@ pub fn bls_dkg_id(mut rng: impl RngCore) -> bls_dkg::outcome::Outcome { outcome } -#[cfg(feature = "dkg")] -use blsttc::{Ciphertext, SecretKey}; - -#[cfg(feature = "dkg")] -use blstrs::{G1Affine, Scalar}; - -#[cfg(feature = "dkg")] -use std::convert::TryFrom; - -#[cfg(feature = "dkg")] -pub struct DbcHelper {} - -#[cfg(feature = "dkg")] -impl DbcHelper { - #[allow(dead_code)] - pub fn decrypt_amount_secrets( - owner: &bls_dkg::outcome::Outcome, - ciphertext: &Ciphertext, - ) -> Result { - let mut shares: std::collections::BTreeMap = - Default::default(); - shares.insert(owner.index, owner.secret_key_share.clone()); - AmountSecrets::try_from((&owner.public_key_set, &shares, ciphertext)) - } - - pub fn decrypt_amount( - owner: &bls_dkg::outcome::Outcome, - ciphertext: &Ciphertext, - ) -> Result { - Ok(Self::decrypt_amount_secrets(owner, ciphertext)?.amount()) - } -} - -// temporary: should go away once blsttc is integrated with with blstrs -pub struct BlsHelper {} - -impl BlsHelper { - #[allow(dead_code)] - pub fn blsttc_to_blstrs_sk(sk: SecretKey) -> Scalar { - let bytes = sk.to_bytes(); - Scalar::from_bytes_be(&bytes).unwrap() - } - - pub fn blsttc_to_blstrs_pubkey(pk: &PublicKey) -> G1Affine { - let bytes = pk.to_bytes(); - G1Affine::from_compressed(&bytes).unwrap() - } - - pub fn blstrs_to_blsttc_pubkey(pk: &G1Affine) -> PublicKey { - let bytes = pk.to_compressed(); - PublicKey::from_bytes(bytes).unwrap() - } - - pub fn blstrs_to_blsttc_sk(sk: Scalar) -> SecretKey { - let bytes = sk.to_bytes_be(); - SecretKey::from_bytes(bytes).unwrap() - } -} - pub(crate) fn sha3_256(input: &[u8]) -> [u8; 32] { use tiny_keccak::{Hasher, Sha3}; @@ -292,7 +238,7 @@ mod tests { pub struct SpentBookMock { pub key_manager: SimpleKeyManager, pub transactions: BTreeMap, - pub genesis: Option<(KeyImage, G1Affine)>, // genesis input (keyimage, public_commitment) + pub genesis: Option<(KeyImage, Commitment)>, // genesis input (keyimage, public_commitment) } impl From for SpentBookMock { @@ -305,8 +251,8 @@ mod tests { } } - impl From<(SimpleKeyManager, KeyImage, G1Affine)> for SpentBookMock { - fn from(params: (SimpleKeyManager, KeyImage, G1Affine)) -> Self { + impl From<(SimpleKeyManager, KeyImage, Commitment)> for SpentBookMock { + fn from(params: (SimpleKeyManager, KeyImage, Commitment)) -> Self { let (key_manager, key_image, public_commitment) = params; Self { @@ -359,15 +305,15 @@ mod tests { // If this is the very first tx logged and genesis key_image was not // provided, then it becomes the genesis tx. - let (genesis_key_image, genesis_public_commitment) = match self.genesis { + let (genesis_key_image, genesis_public_commitment) = match &self.genesis { Some((k, pc)) => (k, pc), None => panic!("Genesis key_image and public commitments unavailable"), }; // public_commitments are not available in spentbook for genesis transaction. - let public_commitments_info: Vec<(KeyImage, Vec)> = - if key_image == genesis_key_image { - vec![(key_image, vec![genesis_public_commitment])] + let public_commitments_info: Vec<(KeyImage, Vec)> = + if key_image == *genesis_key_image { + vec![(key_image.clone(), vec![*genesis_public_commitment])] } else { // Todo: make this cleaner and more efficient. // spentbook needs to also be indexed by OutputProof PublicKey. @@ -375,7 +321,7 @@ mod tests { tx.mlsags .iter() .map(|mlsag| { - let commitments: Vec = mlsag + let commitments: Vec = mlsag .public_keys() .iter() .map(|pk| { @@ -395,19 +341,19 @@ mod tests { .collect(); assert_eq!(commitments.len(), mlsag.public_keys().len()); assert!(commitments.len() == mlsag.ring.len()); - (mlsag.key_image.to_compressed(), commitments) + (mlsag.key_image.into(), commitments) }) .collect() }; // Grab the commitments specific to the spent KeyImage - let tx_public_commitments: Vec> = public_commitments_info + let tx_public_commitments: Vec> = public_commitments_info .clone() .into_iter() .map(|(_, v)| v) .collect(); - let public_commitments: Vec = public_commitments_info + let public_commitments: Vec = public_commitments_info .into_iter() .flat_map(|(k, v)| if k == key_image { v } else { vec![] }) .collect(); @@ -419,7 +365,7 @@ mod tests { let existing_tx = self .transactions - .entry(key_image) + .entry(key_image.clone()) .or_insert_with(|| tx.clone()); if existing_tx.hash() == tx.hash() { Ok(SpentProofShare { @@ -434,7 +380,7 @@ mod tests { } pub fn set_genesis(&mut self, material: &RingCtMaterial) { - let key_image = material.inputs[0].true_input.key_image().to_compressed(); + let key_image = KeyImage::from(material.inputs[0].true_input.key_image().to_affine()); let public_commitment = material.inputs[0] .true_input .revealed_commitment @@ -476,7 +422,7 @@ mod tests { .combine_signatures(vec![genesis.transaction_sig.threshold_crypto()])?; let spent_proof_share = - spentbook.log_spent(genesis.input_key_image, genesis.transaction.clone())?; + spentbook.log_spent(genesis.input_key_image.clone(), genesis.transaction.clone())?; let spentbook_sig = spent_proof_share @@ -502,7 +448,7 @@ mod tests { content: genesis.dbc_content.clone(), transaction: genesis.transaction.clone(), transaction_sigs: BTreeMap::from_iter([( - genesis.input_key_image, + genesis.input_key_image.clone(), ( mint_node.key_manager().public_key_set()?.public_key(), mint_sig, diff --git a/src/mint.rs b/src/mint.rs index 4e24ba4..8e625db 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -15,23 +15,25 @@ use crate::{ Amount, AmountSecrets, DbcContent, DerivedOwner, Error, Hash, KeyImage, KeyManager, - NodeSignature, Owner, PublicKey, PublicKeySet, Result, SpentProof, SpentProofShare, - TransactionValidator, + NodeSignature, Owner, PublicKey, PublicKeyBlst, PublicKeySet, Result, SecretKeyBlst, + SpentProof, SpentProofShare, TransactionValidator, }; use blst_ringct::mlsag::{MlsagMaterial, TrueInput}; use blst_ringct::ringct::{RingCtMaterial, RingCtTransaction}; use blst_ringct::{Output, RevealedCommitment}; use blstrs::group::prime::PrimeCurveAffine; use blstrs::group::Curve; -use blstrs::{G1Affine, Scalar}; use blsttc::{poly::Poly, SecretKeySet}; use rand_core::RngCore; -use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub type MintNodeSignature = (PublicKeySet, NodeSignature); pub type MintNodeSignatures = BTreeMap; +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone)] pub struct GenesisDbcShare { pub ringct_material: RingCtMaterial, @@ -40,18 +42,18 @@ pub struct GenesisDbcShare { pub derived_owner: DerivedOwner, pub transaction: RingCtTransaction, pub transaction_sig: NodeSignature, - pub secret_key: Scalar, // todo: redundant with derived_owner. get rid of this once blsttc uses blstrs. + pub secret_key: SecretKeyBlst, // todo: redundant with derived_owner. get rid of this once blsttc uses blstrs. pub input_key_image: KeyImage, } -// #[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct ReissueRequest { pub transaction: RingCtTransaction, pub spent_proofs: BTreeSet, } -// #[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct ReissueShare { pub transaction: RingCtTransaction, @@ -59,7 +61,8 @@ pub struct ReissueShare { pub mint_node_signatures: MintNodeSignatures, } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone)] pub struct MintNode where K: KeyManager, @@ -88,7 +91,7 @@ impl MintNode { let input_poly = Poly::zero(); let input_secret_key_set = SecretKeySet::from(input_poly); let input_secret_key = - Scalar::from_bytes_be(&input_secret_key_set.secret_key().to_bytes()).unwrap(); + SecretKeyBlst::from_bytes_be(&input_secret_key_set.secret_key().to_bytes()).unwrap(); // Make a secret key for the output of Genesis Tx. (The Genesis Dbc) // temporary: we bypass KeyManager and create a deterministic @@ -101,8 +104,8 @@ impl MintNode { // create sk and derive pk. let secret_key = - Scalar::from_bytes_be(&secret_key_set_derived.secret_key().to_bytes()).unwrap(); - let public_key = (G1Affine::generator() * secret_key).to_affine(); + SecretKeyBlst::from_bytes_be(&secret_key_set_derived.secret_key().to_bytes()).unwrap(); + let public_key = (PublicKeyBlst::generator() * secret_key).to_affine(); let true_input = TrueInput { secret_key: input_secret_key, @@ -112,7 +115,7 @@ impl MintNode { }, }; - let input_key_image = true_input.key_image().to_compressed(); + let input_key_image = true_input.key_image().to_affine().into(); // note: no decoy inputs because no other DBCs exist prior to genesis DBC. let decoy_inputs = vec![]; @@ -225,7 +228,7 @@ impl MintNode { Ok(transaction .mlsags .iter() - .map(|m| (m.key_image.to_compressed(), (pks.clone(), sig.clone()))) + .map(|m| (m.key_image.into(), (pks.clone(), sig.clone()))) .collect()) } } @@ -324,7 +327,7 @@ mod tests { } }; - let genesis_key_image = reissue_tx.mlsags[0].key_image.to_compressed(); + let genesis_key_image: KeyImage = reissue_tx.mlsags[0].key_image.into(); let spent_proof_share = match spentbook.log_spent(genesis_key_image, reissue_tx.clone()) { Ok(s) => s, Err(e) => return check_error(e), @@ -438,7 +441,7 @@ mod tests { })) .build(&mut rng8)?; - let genesis_key_image = reissue_tx.mlsags[0].key_image.to_compressed(); + let genesis_key_image = reissue_tx.mlsags[0].key_image.into(); // note: this closure is used for checking errors returned from both // MintNode::reissue and SpentBookMock::log_spent(). @@ -495,7 +498,7 @@ mod tests { gen_decoy_inputs(&spentbook, &public_key_blstrs, num_decoy_inputs); Ok((secret_key_blstrs, amount_secrets.clone(), decoy_inputs)) }) - .collect::)>>>()?; + .collect::)>>>()?; let owners: Vec = (0..=output_amounts.len()) .map(|_| { @@ -566,7 +569,7 @@ mod tests { let idx = reissue_tx2 .mlsags .iter() - .position(|i| i.key_image.to_compressed() == key) + .position(|i| i.key_image == *key.as_ref()) .unwrap(); assert!(invalid_spent_proofs.contains(&idx)); } @@ -590,12 +593,11 @@ mod tests { } 1 if is_invalid_spent_proof => { // spentbook verifies the tx. If an error, we need to check it same as with reissue result - let spent_proof_share = match spentbook - .log_spent(in_mlsag.key_image.to_compressed(), reissue_tx2.clone()) - { - Ok(s) => s, - Err(e) => return check_error(e), - }; + let spent_proof_share = + match spentbook.log_spent(in_mlsag.key_image.into(), reissue_tx2.clone()) { + Ok(s) => s, + Err(e) => return check_error(e), + }; SpentProofShare { key_image: spent_proof_share.key_image, public_commitments: spent_proof_share.public_commitments, @@ -610,9 +612,7 @@ mod tests { } _ => { // spentbook verifies the tx. If an error, we need to check it same as with reissue result - match spentbook - .log_spent(in_mlsag.key_image.to_compressed(), reissue_tx2.clone()) - { + match spentbook.log_spent(in_mlsag.key_image.into(), reissue_tx2.clone()) { Ok(s) => s, Err(e) => return check_error(e), } @@ -665,7 +665,7 @@ mod tests { fn gen_decoy_inputs( spentbook: &SpentBookMock, - pubkey: &G1Affine, + pubkey: &PublicKeyBlst, num: usize, ) -> Vec { let mut decoys: Vec = Default::default(); @@ -830,8 +830,7 @@ mod tests { ) .build(&mut rng8)?; - let spent_proof_share = - spentbook.log_spent(tx.mlsags[0].key_image.to_compressed(), tx.clone())?; + let spent_proof_share = spentbook.log_spent(tx.mlsags[0].key_image.into(), tx.clone())?; let rr = ReissueRequestBuilder::new(tx.clone()) .add_spent_proof_share(0, spent_proof_share) @@ -939,10 +938,7 @@ mod tests { // 6. Attempt to write this tx to the spentbook. // This will fail because the input and output commitments do not match. // ---------- - match spentbook.log_spent( - tx_fudged.mlsags[0].key_image.to_compressed(), - tx_fudged.clone(), - ) { + match spentbook.log_spent(tx_fudged.mlsags[0].key_image.into(), tx_fudged.clone()) { Err(Error::RingCt(blst_ringct::Error::InvalidHiddenCommitmentInRing)) => {} _ => panic!("Expecting RingCt Error::InvalidHiddenCommitmentInRing"), } @@ -955,7 +951,7 @@ mod tests { // normally spentbook verifies the tx, but here we skip it in order to obtain // a spentproof with an invalid tx. let spent_proof_share_fudged = spentbook.log_spent_and_skip_tx_verification( - tx_fudged.mlsags[0].key_image.to_compressed(), + tx_fudged.mlsags[0].key_image.into(), tx_fudged.clone(), )?; @@ -1040,9 +1036,9 @@ mod tests { let _genesis_spent_proof_share = new_spentbook.log_spent(genesis.input_key_image, genesis.transaction.clone())?; let _spent_proof_share = - new_spentbook.log_spent(tx.mlsags[0].key_image.to_compressed(), tx.clone())?; - let spent_proof_share_true = new_spentbook - .log_spent(tx_true.mlsags[0].key_image.to_compressed(), tx_true.clone())?; + new_spentbook.log_spent(tx.mlsags[0].key_image.into(), tx.clone())?; + let spent_proof_share_true = + new_spentbook.log_spent(tx_true.mlsags[0].key_image.into(), tx_true.clone())?; // Now that the SpentBook is correct, we have a valid spent_proof_share // and can make a valid ReissueRequest diff --git a/src/spent_proof.rs b/src/spent_proof.rs index 44d9d39..71fadc2 100644 --- a/src/spent_proof.rs +++ b/src/spent_proof.rs @@ -1,15 +1,17 @@ use crate::{ - Error, Hash, KeyImage, KeyManager, NodeSignature, PublicKey, PublicKeySet, Result, Signature, + Commitment, Error, Hash, KeyImage, KeyManager, NodeSignature, PublicKey, PublicKeySet, Result, + Signature, }; -use blstrs::G1Affine; -// use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::hash; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// A share of a SpentProof, combine enough of these to form a /// SpentProof. -// #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct SpentProofShare { pub key_image: KeyImage, @@ -19,7 +21,7 @@ pub struct SpentProofShare { pub spentbook_sig_share: NodeSignature, - pub public_commitments: Vec, + pub public_commitments: Vec, } impl PartialEq for SpentProofShare { @@ -61,6 +63,8 @@ impl SpentProofShare { /// SpentProof's are constructed when a DBC is logged to the spentbook. // #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] // #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct SpentProof { pub key_image: KeyImage, @@ -72,14 +76,14 @@ pub struct SpentProof { /// signing over RingCtTransaction, spent_sig, and public_commitments. pub spentbook_sig: Signature, - pub public_commitments: Vec, + pub public_commitments: Vec, } impl SpentProof { pub fn to_bytes(&self) -> Vec { let mut bytes: Vec = Default::default(); - bytes.extend(&self.key_image); + bytes.extend(&self.key_image.as_ref().to_compressed()); bytes.extend(&self.spentbook_sig.to_bytes()); for pc in self.public_commitments.iter() { @@ -91,7 +95,7 @@ impl SpentProof { pub fn validate(&self, tx: Hash, verifier: &K) -> Result<()> { verifier .verify(&tx, &self.spentbook_pub_key, &self.spentbook_sig) - .map_err(|_| Error::InvalidSpentProofSignature(self.key_image))?; + .map_err(|_| Error::InvalidSpentProofSignature(self.key_image.clone()))?; Ok(()) } } diff --git a/src/validation.rs b/src/validation.rs index d931f67..247897d 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -1,6 +1,5 @@ -use crate::{Error, Hash, KeyImage, KeyManager, PublicKey, Result, SpentProof}; +use crate::{Commitment, Error, Hash, KeyImage, KeyManager, PublicKey, Result, SpentProof}; use blst_ringct::ringct::RingCtTransaction; -use blstrs::G1Affine; use blsttc::Signature; use std::collections::{BTreeMap, BTreeSet}; @@ -40,7 +39,7 @@ impl TransactionValidator { if !transaction .mlsags .iter() - .any(|m| m.key_image.to_compressed() == *key_image) + .any(|m| m.key_image == *key_image.as_ref()) { return Err(Error::UnknownInput); } @@ -78,7 +77,7 @@ impl TransactionValidator { let keyimage_unique: BTreeSet = transaction .mlsags .iter() - .map(|m| m.key_image.to_compressed()) + .map(|m| m.key_image.into()) .collect(); if keyimage_unique.len() != transaction.mlsags.len() { return Err(Error::KeyImageNotUniqueAcrossInputs); @@ -88,7 +87,7 @@ impl TransactionValidator { let pubkey_unique: BTreeSet = transaction .outputs .iter() - .map(|o| o.public_key().to_compressed()) + .map(|o| (*o.public_key()).into()) .collect(); if pubkey_unique.len() != transaction.outputs.len() { return Err(Error::PublicKeyNotUniqueAcrossOutputs); @@ -103,7 +102,7 @@ impl TransactionValidator { if !transaction .mlsags .iter() - .any(|m| m.key_image.to_compressed() == spent_proof.key_image) + .any(|m| m.key_image == *spent_proof.key_image.as_ref()) { return Err(Error::SpentProofInputMismatch); } @@ -119,7 +118,7 @@ impl TransactionValidator { transaction .mlsags .iter() - .position(|m| m.key_image.to_compressed() == s.key_image) + .position(|m| m.key_image == *s.key_image.as_ref()) .map(|idx| (idx, s)) }) .collect(); @@ -128,7 +127,7 @@ impl TransactionValidator { let spent_proofs_sorted: Vec<&SpentProof> = spent_proofs_found.into_iter().map(|s| s.1).collect(); - let public_commitments: Vec> = spent_proofs_sorted + let public_commitments: Vec> = spent_proofs_sorted .iter() .map(|s| s.public_commitments.clone()) .collect();