Skip to content

Commit

Permalink
fix: More reliably cache NameAccumulator modexps (#326)
Browse files Browse the repository at this point in the history
* fix: More reliably cache `NameAccumulator` modexps

* Remove `PrivateNodeHeader::revision_name_cache`

* Fix test

* Make `PrivateNodeHeader` equality mountpoint-independent

* Fix typo
  • Loading branch information
matheus23 committed Aug 17, 2023
1 parent 5682783 commit 380ee8c
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 178 deletions.
59 changes: 16 additions & 43 deletions wnfs-nameaccumulator/src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ const L_HASH_DSI: &str = "wnfs/PoKE*/l 128-bit hash derivation";
/// However, these names are based on RSA accumulators to make it possible
/// to prove a relationship between two names, e.g a file being contained in
/// a sub-directory of a directory while leaking as little information as possible.
#[derive(Clone, Debug, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Name {
relative_to: NameAccumulator,
segments: Vec<NameSegment>,
accumulated: OnceCell<(NameAccumulator, ElementsProof)>,
}

/// Represents a setup needed for RSA accumulator operation.
Expand All @@ -52,7 +51,7 @@ pub struct NameAccumulator {

/// A name accumluator segment. A name accumulator commits to a set of these.
/// They are represented as 256-bit prime numbers.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NameSegment(
/// Invariant: Must be a 256-bit prime
BigUint,
Expand Down Expand Up @@ -116,7 +115,6 @@ impl Name {
Self {
relative_to,
segments: segments.into_iter().collect(),
accumulated: OnceCell::new(),
}
}

Expand All @@ -129,7 +127,6 @@ impl Name {
/// Remove the last name segment, if possible.
pub fn up(&mut self) {
self.segments.pop();
self.accumulated = OnceCell::new();
}

/// Return the parent name, if possible.
Expand All @@ -149,7 +146,6 @@ impl Name {

pub fn add_segments(&mut self, segments: impl IntoIterator<Item = NameSegment>) {
self.segments.extend(segments);
self.accumulated = OnceCell::new();
}

pub fn with_segments_added(&self, segments: impl IntoIterator<Item = NameSegment>) -> Self {
Expand All @@ -163,20 +159,18 @@ impl Name {
/// this name is relative to.
///
/// This proof process is memoized. Running it twice won't duplicate work.
pub fn as_proven_accumulator(
pub fn into_proven_accumulator(
&self,
setup: &AccumulatorSetup,
) -> &(NameAccumulator, ElementsProof) {
self.accumulated.get_or_init(|| {
let mut name = self.relative_to.clone();
let proof = name.add(self.segments.iter(), setup);
(name, proof)
})
) -> (NameAccumulator, ElementsProof) {
let mut name = self.relative_to.clone();
let proof = name.add(self.segments.iter(), setup);
(name, proof)
}

/// Return what name accumulator this name commits to.
pub fn as_accumulator(&self, setup: &AccumulatorSetup) -> &NameAccumulator {
&self.as_proven_accumulator(setup).0
pub fn into_accumulator(&self, setup: &AccumulatorSetup) -> NameAccumulator {
self.into_proven_accumulator(setup).0
}
}

Expand Down Expand Up @@ -498,27 +492,6 @@ impl Serialize for UnbatchableProofPart {
}
}

impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
let left = self
.accumulated
.get()
.map(|x| &x.0)
.or(self.segments.is_empty().then_some(&self.relative_to));
let right = other
.accumulated
.get()
.map(|x| &x.0)
.or(other.segments.is_empty().then_some(&other.relative_to));

if let (Some(left), Some(right)) = (left, right) {
return left == right;
}

self.relative_to == other.relative_to && self.segments == other.segments
}
}

impl PartialEq for NameAccumulator {
fn eq(&self, other: &Self) -> bool {
self.state == other.state
Expand Down Expand Up @@ -690,17 +663,17 @@ mod tests {
]);
name_image.add_segments([root_dir_segment, pics_dir_segment, image_file_segment]);

let (accum_note, proof_note) = name_note.as_proven_accumulator(setup);
let (accum_image, proof_image) = name_image.as_proven_accumulator(setup);
let (accum_note, proof_note) = name_note.into_proven_accumulator(setup);
let (accum_image, proof_image) = name_image.into_proven_accumulator(setup);

let mut batched_proof = BatchedProofPart::new();
batched_proof.add(proof_note, setup);
batched_proof.add(proof_image, setup);
batched_proof.add(&proof_note, setup);
batched_proof.add(&proof_image, setup);

let name_base = Name::empty(setup).as_accumulator(setup).clone();
let name_base = Name::empty(setup).into_accumulator(setup);
let mut verification = BatchedProofVerification::new(setup);
verification.add(&name_base, accum_note, &proof_note.part)?;
verification.add(&name_base, accum_image, &proof_image.part)?;
verification.add(&name_base, &accum_note, &proof_note.part)?;
verification.add(&name_base, &accum_image, &proof_image.part)?;
verification.verify(&batched_proof)?;

Ok(())
Expand Down
1 change: 1 addition & 0 deletions wnfs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ libipld-core = { version = "0.16" }
multihash = "0.19"
once_cell = "1.16"
proptest = { version = "1.1", optional = true }
quick_cache = "0.3.0"
rand_core = "0.6"
semver = { version = "1.0", features = ["serde"] }
serde = { version = "1.0", features = ["rc"] }
Expand Down
3 changes: 1 addition & 2 deletions wnfs/examples/write_proofs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ async fn alice_actions(store: &impl BlockStore) -> Result<(Cid, AccessKey, NameA

let access_key = root_dir.as_node().store(forest, store, rng).await?;
let cid = forest.store(store).await?;
let setup = forest.get_accumulator_setup();
let allowed_name = root_dir.header.get_name().as_accumulator(setup).clone();
let allowed_name = forest.get_accumulated_name(&root_dir.header.get_name());

Ok((cid, access_key, allowed_name))
}
Expand Down
22 changes: 6 additions & 16 deletions wnfs/src/private/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::{
rc::Rc,
};
use wnfs_common::{utils::error, BlockStore, Metadata, PathNodes, PathNodesResult, CODEC_RAW};
use wnfs_nameaccumulator::{AccumulatorSetup, Name, NameSegment};
use wnfs_nameaccumulator::{Name, NameSegment};

//--------------------------------------------------------------------------------------------------
// Type Definitions
Expand Down Expand Up @@ -394,15 +394,6 @@ impl PrivateDirectory {
Ok(cloned)
}

/// Returns the private ref, if this directory has been `.store()`ed before.
pub(crate) fn derive_private_ref(&self, setup: &AccumulatorSetup) -> Option<PrivateRef> {
self.content.persisted_as.get().map(|content_cid| {
self.header
.derive_revision_ref(setup)
.into_private_ref(*content_cid)
})
}

/// This prepares this directory for key rotation, usually for moving or
/// copying the directory to some other place.
///
Expand Down Expand Up @@ -1259,8 +1250,7 @@ impl PrivateDirectory {
store: &impl BlockStore,
rng: &mut impl CryptoRngCore,
) -> Result<PrivateRef> {
let setup = &forest.get_accumulator_setup().clone();
let header_cid = self.header.store(store, setup).await?;
let header_cid = self.header.store(store, forest).await?;
let temporal_key = self.header.derive_temporal_key();
let name_with_revision = self.header.get_revision_name();

Expand All @@ -1270,12 +1260,12 @@ impl PrivateDirectory {
.await?;

forest
.put_encrypted(name_with_revision, [header_cid, content_cid], store)
.put_encrypted(&name_with_revision, [header_cid, content_cid], store)
.await?;

Ok(self
.header
.derive_revision_ref(setup)
.derive_revision_ref(forest)
.into_private_ref(content_cid))
}

Expand All @@ -1284,9 +1274,9 @@ impl PrivateDirectory {
serializable: PrivateDirectoryContentSerializable,
temporal_key: &TemporalKey,
cid: Cid,
forest: &impl PrivateForest,
store: &impl BlockStore,
parent_name: Option<Name>,
setup: &AccumulatorSetup,
) -> Result<Self> {
if serializable.version.major != 0 || serializable.version.minor != 2 {
bail!(FsError::UnexpectedVersion(serializable.version));
Expand All @@ -1309,9 +1299,9 @@ impl PrivateDirectory {
let header = PrivateNodeHeader::load(
&serializable.header_cid,
temporal_key,
forest,
store,
parent_name,
setup,
)
.await?;
Ok(Self { header, content })
Expand Down
30 changes: 8 additions & 22 deletions wnfs/src/private/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeSet, iter, rc::Rc};
use wnfs_common::{utils, BlockStore, Metadata, CODEC_RAW, MAX_BLOCK_SIZE};
use wnfs_nameaccumulator::{AccumulatorSetup, Name, NameAccumulator, NameSegment};
use wnfs_nameaccumulator::{Name, NameAccumulator, NameSegment};

//--------------------------------------------------------------------------------------------------
// Constants
Expand Down Expand Up @@ -531,9 +531,7 @@ impl PrivateFile {

Ok(FileContent::External {
key,
base_name: base_name
.as_accumulator(forest.get_accumulator_setup())
.clone(),
base_name: forest.get_accumulated_name(&base_name),
block_count,
block_content_size: MAX_BLOCK_CONTENT_SIZE,
})
Expand Down Expand Up @@ -588,9 +586,7 @@ impl PrivateFile {

Ok(FileContent::External {
key,
base_name: base_name
.as_accumulator(forest.get_accumulator_setup())
.clone(),
base_name: forest.get_accumulated_name(&base_name),
block_count: block_index,
block_content_size: MAX_BLOCK_CONTENT_SIZE,
})
Expand Down Expand Up @@ -696,15 +692,6 @@ impl PrivateFile {
Ok(cloned)
}

/// Returns the private ref, if this file has been `.store()`ed before.
pub(crate) fn derive_private_ref(&self, setup: &AccumulatorSetup) -> Option<PrivateRef> {
self.content.persisted_as.get().map(|content_cid| {
self.header
.derive_revision_ref(setup)
.into_private_ref(*content_cid)
})
}

/// This prepares this file for key rotation, usually for moving or
/// copying the file to some other place.
///
Expand Down Expand Up @@ -733,8 +720,7 @@ impl PrivateFile {
store: &impl BlockStore,
rng: &mut impl CryptoRngCore,
) -> Result<PrivateRef> {
let setup = &forest.get_accumulator_setup().clone();
let header_cid = self.header.store(store, setup).await?;
let header_cid = self.header.store(store, forest).await?;
let temporal_key = self.header.derive_temporal_key();
let snapshot_key = temporal_key.derive_snapshot_key();
let name_with_revision = self.header.get_revision_name();
Expand All @@ -745,12 +731,12 @@ impl PrivateFile {
.await?;

forest
.put_encrypted(name_with_revision, [header_cid, content_cid], store)
.put_encrypted(&name_with_revision, [header_cid, content_cid], store)
.await?;

Ok(self
.header
.derive_revision_ref(setup)
.derive_revision_ref(forest)
.into_private_ref(content_cid))
}

Expand All @@ -759,9 +745,9 @@ impl PrivateFile {
serializable: PrivateFileContentSerializable,
temporal_key: &TemporalKey,
cid: Cid,
forest: &impl PrivateForest,
store: &impl BlockStore,
parent_name: Option<Name>,
setup: &AccumulatorSetup,
) -> Result<Self> {
if serializable.version.major != 0 || serializable.version.minor != 2 {
bail!(FsError::UnexpectedVersion(serializable.version));
Expand All @@ -777,9 +763,9 @@ impl PrivateFile {
let header = PrivateNodeHeader::load(
&serializable.header_cid,
temporal_key,
forest,
store,
parent_name,
setup,
)
.await?;
Ok(Self { header, content })
Expand Down
Loading

0 comments on commit 380ee8c

Please sign in to comment.