mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-30 20:11:23 +00:00
Test nullifiers in constant time
Checking for spent notes in a block is still not completely constant time, due to filtering out negative results of the constant-time comparison. Part of #84.
This commit is contained in:
@@ -17,6 +17,7 @@ ff = { version = "0.5.0", path = "../ff" }
|
||||
hex = "0.3"
|
||||
pairing = { version = "0.15.0", path = "../pairing" }
|
||||
protobuf = "2"
|
||||
subtle = "2"
|
||||
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||
|
||||
[build-dependencies]
|
||||
|
@@ -3,6 +3,7 @@
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::{Bls12, Fr, FrRepr};
|
||||
use std::collections::HashSet;
|
||||
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
use zcash_primitives::{
|
||||
jubjub::{edwards, fs::Fs},
|
||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||
@@ -116,28 +117,29 @@ pub fn scan_block(
|
||||
let num_outputs = tx.outputs.len();
|
||||
|
||||
// Check for spent notes
|
||||
let shielded_spends: Vec<_> =
|
||||
tx.spends
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, spend)| {
|
||||
if let Some(account) = nullifiers.iter().find_map(|&(nf, acc)| {
|
||||
if nf == &spend.nf[..] {
|
||||
Some(acc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(WalletShieldedSpend {
|
||||
index,
|
||||
nf: spend.nf,
|
||||
account,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// The only step that is not constant-time is the filter() at the end.
|
||||
let shielded_spends: Vec<_> = tx
|
||||
.spends
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, spend)| {
|
||||
// Find the first tracked nullifier that matches this spend, and produce
|
||||
// a WalletShieldedSpend if there is a match, in constant time.
|
||||
nullifiers
|
||||
.iter()
|
||||
.map(|&(nf, account)| CtOption::new(account as u64, nf.ct_eq(&spend.nf[..])))
|
||||
.fold(CtOption::new(0, 0.into()), |first, next| {
|
||||
CtOption::conditional_select(&next, &first, first.is_some())
|
||||
})
|
||||
.map(|account| WalletShieldedSpend {
|
||||
index,
|
||||
nf: spend.nf,
|
||||
account: account as usize,
|
||||
})
|
||||
})
|
||||
.filter(|spend| spend.is_some().into())
|
||||
.map(|spend| spend.unwrap())
|
||||
.collect();
|
||||
|
||||
// Collect the set of accounts that were spent from in this transaction
|
||||
let spent_from_accounts: HashSet<_> =
|
||||
|
Reference in New Issue
Block a user