Skip to content

Commit

Permalink
refactor more
Browse files Browse the repository at this point in the history
  • Loading branch information
Sajjon committed Sep 17, 2024
1 parent 8547166 commit aaa3959
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 63 deletions.
64 changes: 64 additions & 0 deletions src/factor_instance_provider/cache/is_pre_derived_keys_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::prelude::*;

/// A cache for pre-derived keys, saved on file and which will derive more keys
/// if needed, using UI/UX via KeysCollector.
///
/// We must implement the `FactorInstanceProvider` in a way that it can handle
/// the case where the cache does not exist, which it does not for users before
/// Radix Wallet version 2.0.
///
/// The purpose of this cache is only to speed up the process of accessing
/// FactorInstances.
#[async_trait::async_trait]
pub trait IsPreDerivedKeysCache {
/// Inserts the `derived` keys into the cache, notice the asymmetry of this
/// "save" vs the `consume_next_factor_instances` ("load") - this method accepts
/// a set of factors per request, while the `consume_next_factor_instances`
/// returns a single factor per request.
///
/// The reason is that we are deriving many keys and caching them, per request,
/// whereas the `consume_next_factor_instances` ("load") only ever cares about
/// the next key to be consumed.
async fn insert(
&self,
derived: IndexMap<PreDeriveKeysCacheKey, IndexSet<HierarchicalDeterministicFactorInstance>>,
) -> Result<()>;

/// Must be async since might need to derive more keys if we are about
/// to use the last, thus will require usage of KeysCollector - which is async.
/// Also typically we cache to file - which itself is async
async fn consume_next_factor_instances(
&self,
requests: IndexSet<DerivationRequest>,
) -> Result<IndexMap<DerivationRequest, HierarchicalDeterministicFactorInstance>>;

/// Returns `NextDerivationPeekOutcome::WouldHaveAtLeastOneFactorLeftPerFulfilledRequests`
/// if there would be **at least on key left** after we have consumed
/// (deleted) keys fulfilling all `requests`. Otherwise returns
///`NextDerivationPeekOutcome::WouldConsumeLastFactorOfRequests(last)` where `indices` is a map of the last consumed indices
/// for each request. By index we mean Derivation Entity Index (`HDPathComponent`).
/// If there is any problem with the cache, returns `Err`.
///
/// We **must** have one key/factor left fulfilling the request, so that we can
/// derive the next keys based on that.
/// This prevents us from a problem:
/// 1. Account X with address `A` is created by FactorInstance `F` with
/// `{ factor_source: L, key_space: Unsecurified, index: 0 }`
/// 2. User securified account `X`, and `F = { factor_source: L, key_space: Unsecurified, index: 0 }`
/// is now "free", since it is no longer found in the Profile.
/// 3. User tries to create account `Y` with `L` and if we would have used
/// Profile "static analysis" it would say that `F = { factor_source: L, key_space: Unsecurified, index: 0 }`
/// is next/available.
/// 4. Failure! Account `Y` was never created since it would have same
/// address `A` as account `X`, since it would have used same FactorInstance.
/// 5. This problem is we cannot do this simple static analysis of Profile
/// to find next index we would actually need to form derivation paths and
/// derive the keys and check if that public key has been used to create any
/// of the addresses in profile.
///
/// Eureka! Or we just ensure to not loose track of the fact that `0` has
/// been used, by letting the cache contains (0...N) keys and **before** `N`
/// is consumed, we derive the next `(N+1, N+N)` keys and cache them. This
/// way we need only derive more keys when they are needed.
async fn peek(&self, requests: IndexSet<DerivationRequest>) -> NextDerivationPeekOutcome;
}
3 changes: 3 additions & 0 deletions src/factor_instance_provider/cache/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod is_pre_derived_keys_cache;

pub use is_pre_derived_keys_cache::*;
64 changes: 1 addition & 63 deletions src/factor_instance_provider/factor_instance_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,75 +6,13 @@ use rand::seq::index;

use crate::prelude::*;

/// A cache for pre-derived keys, saved on file and which will derive more keys
/// if needed, using UI/UX via KeysCollector.
///
/// We must implement the `FactorInstanceProvider` in a way that it can handle
/// the case where the cache does not exist, which it does not for users before
/// Radix Wallet version 2.0.
///
/// The purpose of this cache is only to speed up the process of accessing
/// FactorInstances.
#[async_trait::async_trait]
pub trait IsPreDerivedKeysCache {
/// Inserts the `derived` keys into the cache, notice the asymmetry of this
/// "save" vs the `consume_next_factor_instances` ("load") - this method accepts
/// a set of factors per request, while the `consume_next_factor_instances`
/// returns a single factor per request.
///
/// The reason is that we are deriving many keys and caching them, per request,
/// whereas the `consume_next_factor_instances` ("load") only ever cares about
/// the next key to be consumed.
async fn insert(
&self,
derived: IndexMap<PreDeriveKeysCacheKey, IndexSet<HierarchicalDeterministicFactorInstance>>,
) -> Result<()>;

/// Must be async since might need to derive more keys if we are about
/// to use the last, thus will require usage of KeysCollector - which is async.
/// Also typically we cache to file - which itself is async
async fn consume_next_factor_instances(
&self,
requests: IndexSet<DerivationRequest>,
) -> Result<IndexMap<DerivationRequest, HierarchicalDeterministicFactorInstance>>;

/// Returns `NextDerivationPeekOutcome::WouldHaveAtLeastOneFactorLeftPerFulfilledRequests`
/// if there would be **at least on key left** after we have consumed
/// (deleted) keys fulfilling all `requests`. Otherwise returns
///`NextDerivationPeekOutcome::WouldConsumeLastFactorOfRequests(last)` where `indices` is a map of the last consumed indices
/// for each request. By index we mean Derivation Entity Index (`HDPathComponent`).
/// If there is any problem with the cache, returns `Err`.
///
/// We **must** have one key/factor left fulfilling the request, so that we can
/// derive the next keys based on that.
/// This prevents us from a problem:
/// 1. Account X with address `A` is created by FactorInstance `F` with
/// `{ factor_source: L, key_space: Unsecurified, index: 0 }`
/// 2. User securified account `X`, and `F = { factor_source: L, key_space: Unsecurified, index: 0 }`
/// is now "free", since it is no longer found in the Profile.
/// 3. User tries to create account `Y` with `L` and if we would have used
/// Profile "static analysis" it would say that `F = { factor_source: L, key_space: Unsecurified, index: 0 }`
/// is next/available.
/// 4. Failure! Account `Y` was never created since it would have same
/// address `A` as account `X`, since it would have used same FactorInstance.
/// 5. This problem is we cannot do this simple static analysis of Profile
/// to find next index we would actually need to form derivation paths and
/// derive the keys and check if that public key has been used to create any
/// of the addresses in profile.
///
/// Eureka! Or we just ensure to not loose track of the fact that `0` has
/// been used, by letting the cache contains (0...N) keys and **before** `N`
/// is consumed, we derive the next `(N+1, N+N)` keys and cache them. This
/// way we need only derive more keys when they are needed.
async fn peek(&self, requests: IndexSet<DerivationRequest>) -> NextDerivationPeekOutcome;
}

/// Used as a map key in `InMemoryPreDerivedKeysCache`.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct PreDeriveKeysCacheKey {
factor_source_id: FactorSourceIDFromHash,
path_without_index: DerivationPathWithoutIndex,
}

impl From<HierarchicalDeterministicFactorInstance> for PreDeriveKeysCacheKey {
fn from(value: HierarchicalDeterministicFactorInstance) -> Self {
Self::new(
Expand Down
2 changes: 2 additions & 0 deletions src/factor_instance_provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod derivation_request;
#[allow(clippy::module_inception)]
mod factor_instance_provider;

mod cache;
mod gateway;
mod next_derivation_peek_outcome;
mod profile_extensions;
Expand All @@ -10,6 +11,7 @@ mod unfulfillable_request;
mod unfulfillable_request_reason;
mod unfulfillable_requests;

pub use cache::*;
pub use derivation_request::*;
pub use factor_instance_provider::*;
pub use gateway::*;
Expand Down

0 comments on commit aaa3959

Please sign in to comment.