Merge pull request #174 from zcash/bench-note-decryption

Benchmark note decryption
This commit is contained in:
str4d 2021-08-06 11:17:59 +01:00 committed by GitHub
commit fe923cc391
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 121 additions and 4 deletions

View File

@ -60,6 +60,10 @@ bench = false
dev-graph = ["halo2/dev-graph", "plotters"]
test-dependencies = ["proptest"]
[[bench]]
name = "note_decryption"
harness = false
[[bench]]
name = "small"
harness = false

106
benches/note_decryption.rs Normal file
View File

@ -0,0 +1,106 @@
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
use orchard::{
builder::Builder,
bundle::Flags,
circuit::ProvingKey,
keys::{FullViewingKey, IncomingViewingKey, SpendingKey},
note_encryption::{CompactAction, OrchardDomain},
value::NoteValue,
Anchor, Bundle,
};
use rand::rngs::OsRng;
use zcash_note_encryption::{try_compact_note_decryption, try_note_decryption};
#[cfg(unix)]
use pprof::criterion::{Output, PProfProfiler};
fn bench_note_decryption(c: &mut Criterion) {
let rng = OsRng;
let pk = ProvingKey::build();
let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap());
let valid_ivk = IncomingViewingKey::from(&fvk);
let recipient = fvk.default_address();
// Compact actions don't have the full AEAD ciphertext, so ZIP 307 trial-decryption
// relies on an invalid ivk resulting in random noise for which the note commitment
// is invalid. However, in practice we still get early rejection:
// - The version byte will be invalid in 255/256 instances.
// - If the version byte is valid, one of either the note commitment check or the esk
// check will be invalid, saving us at least one scalar mul.
//
// Our fixed (action, invalid ivk) tuple will always fall into a specific rejection
// case. In order to reflect the real behaviour in the benchmarks, we trial-decrypt
// with 1000 invalid ivks (each of which will result in a different uniformly-random
// plaintext); this is equivalent to trial-decrypting 1000 different actions with the
// same ivk, but is faster to set up.
let invalid_ivks: Vec<_> = (0u32..1000)
.map(|i| {
let mut sk = [0; 32];
sk[..4].copy_from_slice(&i.to_le_bytes());
IncomingViewingKey::from(&FullViewingKey::from(&SpendingKey::from_bytes(sk).unwrap()))
})
.collect();
let bundle = {
let mut builder = Builder::new(
Flags::from_parts(true, true),
Anchor::from_bytes([0; 32]).unwrap(),
);
builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
.unwrap();
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
bundle
.create_proof(&pk)
.unwrap()
.apply_signatures(rng, [0; 32], &[])
.unwrap()
};
let action = bundle.actions().first();
let domain = OrchardDomain::for_action(action);
let compact = {
let mut group = c.benchmark_group("note-decryption");
group.bench_function("valid", |b| {
b.iter(|| try_note_decryption(&domain, &valid_ivk, action).unwrap())
});
// Non-compact actions will always early-reject at the same point: AEAD decryption.
group.bench_function("invalid", |b| {
b.iter(|| try_note_decryption(&domain, &invalid_ivks[0], action))
});
let compact = CompactAction::from(action);
group.bench_function("compact-valid", |b| {
b.iter(|| try_compact_note_decryption(&domain, &valid_ivk, &compact).unwrap())
});
compact
};
{
let mut group = c.benchmark_group("compact-note-decryption");
group.throughput(Throughput::Elements(invalid_ivks.len() as u64));
group.bench_function("invalid", |b| {
b.iter(|| {
for ivk in &invalid_ivks {
try_compact_note_decryption(&domain, ivk, &compact);
}
})
});
}
}
#[cfg(unix)]
criterion_group! {
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = bench_note_decryption
}
#[cfg(not(unix))]
criterion_group!(benches, bench_note_decryption);
criterion_main!(benches);

View File

@ -23,7 +23,7 @@ pub mod circuit;
mod constants;
pub mod keys;
pub mod note;
mod note_encryption;
pub mod note_encryption;
pub mod primitives;
mod spec;
pub mod tree;
@ -36,5 +36,4 @@ pub use address::Address;
pub use bundle::Bundle;
pub use circuit::Proof;
pub use note::Note;
pub use note_encryption::OrchardDomain;
pub use tree::Anchor;

View File

@ -1,6 +1,6 @@
//! In-band secret distribution for Orchard bundles.
use std::convert::TryInto;
use std::{convert::TryInto, fmt};
use blake2b_simd::{Hash, Params};
use halo2::arithmetic::FieldExt;
@ -242,6 +242,7 @@ impl Domain for OrchardDomain {
}
}
/// Implementation of in-band secret distribution for Orchard bundles.
pub type OrchardNoteEncryption = zcash_note_encryption::NoteEncryption<OrchardDomain>;
impl<T> ShieldedOutput<OrchardDomain> for Action<T> {
@ -258,12 +259,19 @@ impl<T> ShieldedOutput<OrchardDomain> for Action<T> {
}
}
struct CompactAction {
/// A compact Action for light clients.
pub struct CompactAction {
ephemeral_key: EphemeralKeyBytes,
cmx: ExtractedNoteCommitment,
enc_ciphertext: [u8; 52],
}
impl fmt::Debug for CompactAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CompactAction")
}
}
impl<T> From<&Action<T>> for CompactAction {
fn from(action: &Action<T>) -> Self {
CompactAction {