diff --git a/CHANGELOG.md b/CHANGELOG.md index 2725ba8a9..a99b2dc56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] ### Added +- [\#349](https://github.com/Manta-Network/manta-rs/pull/349) Nullifier map optimization. - [\#345](https://github.com/Manta-Network/manta-rs/pull/345) Precompute ledger and minor bug fix. ### Changed diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index ccd9980d4..82bc74b4c 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -169,7 +169,8 @@ pub trait Configuration { Secret = Self::SpendSecret, Nullifier = Self::Nullifier, Identifier = Self::Identifier, - > + utxo::UtxoReconstruct; + > + utxo::NullifierOpen + + utxo::UtxoReconstruct; /// Authorization Context Variable Type type AuthorizationContextVar: Variable< diff --git a/manta-accounting/src/transfer/utxo/mod.rs b/manta-accounting/src/transfer/utxo/mod.rs index b1265cf32..df4352e0d 100644 --- a/manta-accounting/src/transfer/utxo/mod.rs +++ b/manta-accounting/src/transfer/utxo/mod.rs @@ -91,6 +91,26 @@ impl IndependenceContext for NullifierIndependence { const DEFAULT: bool = false; } +/// Nullifier Open +pub trait NullifierOpen: AssetType + DeriveDecryptionKey + NullifierType { + /// Opens the outgoing note in `nullifier` with `decryption_key`. + fn open( + &self, + nullifier: &Self::Nullifier, + decryption_key: &Self::DecryptionKey, + ) -> Option; + + /// Returns `true` if `nullifier` can be opened with `decryption_key`. + #[inline] + fn can_be_opened( + &self, + nullifier: &Self::Nullifier, + decryption_key: &Self::DecryptionKey, + ) -> bool { + self.open(nullifier, decryption_key).is_some() + } +} + /// Identifier pub trait IdentifierType { /// Identifier Type diff --git a/manta-accounting/src/transfer/utxo/protocol.rs b/manta-accounting/src/transfer/utxo/protocol.rs index 39603d2cd..e8a5fa1f2 100644 --- a/manta-accounting/src/transfer/utxo/protocol.rs +++ b/manta-accounting/src/transfer/utxo/protocol.rs @@ -1368,6 +1368,31 @@ where } } +impl utxo::NullifierOpen for Parameters +where + C: Configuration, + C::OutgoingBaseEncryptionScheme: + Decrypt>>, +{ + #[inline] + fn open( + &self, + nullifier: &Self::Nullifier, + decryption_key: &Self::DecryptionKey, + ) -> Option { + Hybrid::new( + StandardDiffieHellman::new(self.base.group_generator.generator().clone()), + self.base.outgoing_base_encryption_scheme.clone(), + ) + .decrypt( + decryption_key, + &C::OutgoingHeader::default(), + &nullifier.outgoing_note.ciphertext, + &mut (), + ) + } +} + impl utxo::NoteOpen for Parameters where C: Configuration, diff --git a/manta-accounting/src/wallet/signer/functions.rs b/manta-accounting/src/wallet/signer/functions.rs index 6d722475f..680194fa7 100644 --- a/manta-accounting/src/wallet/signer/functions.rs +++ b/manta-accounting/src/wallet/signer/functions.rs @@ -29,8 +29,8 @@ use crate::{ receiver::ReceiverPost, requires_authorization, utxo::{ - auth::DeriveContext, DeriveAddress as _, DeriveDecryptionKey, DeriveSpend, Spend, - UtxoReconstruct, + auth::DeriveContext, DeriveAddress as _, DeriveDecryptionKey, DeriveSpend, + NullifierOpen, Spend, UtxoReconstruct, }, Address, Asset, AssociatedData, Authorization, AuthorizationContext, FullParametersRef, IdentifiedAsset, Identifier, IdentityProof, Note, Nullifier, Parameters, PreSender, @@ -162,7 +162,7 @@ fn insert_next_item( asset.clone(), rng, ); - if nullifiers.contains_item(&nullifier) { + if nullifiers.remove(&nullifier) { utxo_accumulator.insert_nonprovable(&item_hash::(parameters, &utxo)); } else { utxo_accumulator.insert(&item_hash::(parameters, &utxo)); @@ -192,7 +192,7 @@ where { let (_, utxo, nullifier) = parameters.derive_spend(authorization_context, identifier, asset.clone(), rng); - if nullifiers.contains_item(&nullifier) { + if nullifiers.remove(&nullifier) { utxo_accumulator.remove_proof(&item_hash::(parameters, &utxo)); if !asset.is_zero() { withdraw.push(asset); @@ -224,10 +224,14 @@ where C::AssetValue: CheckedAdd + CheckedSub, { let nullifier_count = nullifier_data.len(); - nullifiers.extend(nullifier_data); let mut deposit = Vec::new(); let mut withdraw = Vec::new(); let decryption_key = parameters.derive_decryption_key(authorization_context); + nullifiers.extend( + nullifier_data + .into_iter() + .filter(|nullifier| parameters.can_be_opened(nullifier, &decryption_key)), + ); let mut nonprovable_inserts = Vec::new(); for (utxo, note) in inserts { if let Some((identifier, asset)) = parameters.open_with_check(&decryption_key, &utxo, note) diff --git a/manta-accounting/src/wallet/signer/nullifier_map.rs b/manta-accounting/src/wallet/signer/nullifier_map.rs index a20bc25b3..62443973e 100644 --- a/manta-accounting/src/wallet/signer/nullifier_map.rs +++ b/manta-accounting/src/wallet/signer/nullifier_map.rs @@ -43,6 +43,9 @@ pub trait NullifierMap: Default { where I: IntoIterator; + /// Removes `item` from `self`. + fn remove(&mut self, item: &T) -> bool; + /// Checks if `self` contains `item`. fn contains_item(&self, item: &T) -> bool; } @@ -75,6 +78,16 @@ where Extend::extend(self, items) } + #[inline] + fn remove(&mut self, item: &T) -> bool { + if let Some(index) = self.iter().position(|x| x == item) { + self.remove(index); + true + } else { + false + } + } + #[inline] fn contains_item(&self, item: &T) -> bool { self.contains(item) @@ -108,6 +121,11 @@ where Extend::extend(self, items) } + #[inline] + fn remove(&mut self, item: &T) -> bool { + self.remove(item) + } + #[inline] fn contains_item(&self, item: &T) -> bool { self.contains(item) @@ -142,6 +160,11 @@ where Extend::extend(self, items) } + #[inline] + fn remove(&mut self, item: &T) -> bool { + self.remove(item) + } + #[inline] fn contains_item(&self, item: &T) -> bool { self.contains(item) diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index d22f940ac..b06dbb318 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -33,8 +33,8 @@ pub mod config; #[cfg_attr(doc_cfg, doc(cfg(feature = "key")))] pub mod key; -#[cfg(all(feature = "parameters"))] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "parameters"))))] +#[cfg(feature = "parameters")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "parameters")))] pub mod parameters; #[cfg(feature = "groth16")]