-
Notifications
You must be signed in to change notification settings - Fork 0
FactorInstancesProvider #18
Changes from 72 commits
551f555
b6a1a40
92dd942
113045b
ceaee8b
9f24311
3e1e6b6
863db76
e7c4cec
289d76c
b9f0637
15c423c
20b0810
68e1a66
4bdeabb
1116345
e193399
2aa8091
080951c
674f272
b809f3e
0e96ce3
416c2ee
8136341
eceddfc
f74fcc3
8297d2c
60e9cae
002d475
0e73b27
ac1e50c
9358de1
827b05d
f6845ed
693b39f
70956bc
516a44f
5df0baf
c560bd1
7eb40ad
a9f36f1
79aab62
cec9c94
f439723
8983e31
0df13f3
a1db98e
fd1f391
e8736e4
4ac0467
6ec6af5
f989bce
b2a2f8d
2674ee2
38cd499
1fec655
47ce0bd
c7c3951
c20c647
1419d98
c6ef757
fd21bfa
40d9967
f87e3e9
9da8c85
250a460
8feb867
72bfe24
420f4f3
d772506
371d5bb
2bbdb80
e824680
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
{ | ||
"cSpell.words": [ | ||
"analyser", | ||
"Appendable", | ||
"Banksy", | ||
"bdfs", | ||
"Fulfillable", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use crate::prelude::*; | ||
|
||
/// Derivation Presets are Network agnostic and Index agnostic | ||
/// "templates" for DerivationPaths. | ||
#[derive(Clone, Copy, Hash, PartialEq, Eq, enum_iterator::Sequence, derive_more::Debug)] | ||
pub enum DerivationPreset { | ||
/// Used to form DerivationPaths used to derive FactorInstances | ||
/// for "veci": Virtual Entity Creating (Factor)Instance for accounts. | ||
/// `(EntityKind::Account, KeySpace::Unsecurified, KeyKind::TransactionSigning)` | ||
#[debug("A-VECI")] | ||
AccountVeci, | ||
|
||
/// Used to form DerivationPaths used to derive FactorInstances | ||
/// for "mfa" to securify accounts. | ||
/// `(EntityKind::Account, KeySpace::Securified, KeyKind::TransactionSigning)` | ||
#[debug("A-MFA")] | ||
AccountMfa, | ||
|
||
/// Used to form DerivationPaths used to derive FactorInstances | ||
/// for "veci": Virtual Entity Creating (Factor)Instance for personas. | ||
/// `(EntityKind::Identity, KeySpace::Unsecurified, KeyKind::TransactionSigning)` | ||
#[debug("I-VECI")] | ||
IdentityVeci, | ||
|
||
/// Used to form DerivationPaths used to derive FactorInstances | ||
/// for "mfa" to securify personas. | ||
/// `(EntityKind::Identity, KeySpace::Securified, KeyKind::TransactionSigning)` | ||
#[debug("I-MFA")] | ||
IdentityMfa, | ||
} | ||
|
||
// ============= | ||
// Construction | ||
// ============= | ||
impl DerivationPreset { | ||
/// All DerivationPreset's, used to fill cache. | ||
pub fn all() -> IndexSet<Self> { | ||
enum_iterator::all::<Self>().collect() | ||
} | ||
|
||
/// Selects a `DerivationPreset` for veci based on `CAP26EntityKind`, | ||
/// i.e. either `DerivationPreset::AccountVeci` or `DerivationPreset::IdentityVeci`. | ||
pub fn veci_entity_kind(entity_kind: CAP26EntityKind) -> Self { | ||
match entity_kind { | ||
CAP26EntityKind::Account => Self::AccountVeci, | ||
CAP26EntityKind::Identity => Self::IdentityVeci, | ||
} | ||
} | ||
|
||
/// Selects a `DerivationPreset` for MFA based on `CAP26EntityKind`, | ||
/// i.e. either `DerivationPreset::AccountMfa` or `DerivationPreset::IdentityMfa`. | ||
pub fn mfa_entity_kind(entity_kind: CAP26EntityKind) -> Self { | ||
match entity_kind { | ||
CAP26EntityKind::Account => Self::AccountMfa, | ||
CAP26EntityKind::Identity => Self::IdentityMfa, | ||
} | ||
} | ||
} | ||
|
||
// ============= | ||
// Instance Methods | ||
// ============= | ||
impl DerivationPreset { | ||
/// Returns the `CAP26EntityKind` of the `DerivationPreset`. | ||
pub fn entity_kind(&self) -> CAP26EntityKind { | ||
match self { | ||
Self::AccountVeci | Self::AccountMfa => CAP26EntityKind::Account, | ||
Self::IdentityVeci | Self::IdentityMfa => CAP26EntityKind::Identity, | ||
} | ||
} | ||
|
||
/// Returns the `CAP26KeyKind` of the `DerivationPreset`. | ||
pub fn key_kind(&self) -> CAP26KeyKind { | ||
match self { | ||
Self::AccountVeci | Self::IdentityVeci | Self::AccountMfa | Self::IdentityMfa => { | ||
CAP26KeyKind::TransactionSigning | ||
} | ||
} | ||
} | ||
|
||
/// Returns the `KeySpace` of the `DerivationPreset`. | ||
pub fn key_space(&self) -> KeySpace { | ||
match self { | ||
Self::AccountVeci | Self::IdentityVeci => KeySpace::Unsecurified, | ||
Self::AccountMfa | Self::IdentityMfa => KeySpace::Securified, | ||
} | ||
} | ||
|
||
/// Maps a DerivationPreset to a `IndexAgnosticPath` which is network aware. | ||
pub fn index_agnostic_path_on_network(&self, network_id: NetworkID) -> IndexAgnosticPath { | ||
IndexAgnosticPath::from((network_id, *self)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use crate::prelude::*; | ||
|
||
use super::quantities; | ||
|
||
/// A DerivationPath which is not indexed. On a specific network. | ||
#[derive(Clone, Copy, Hash, PartialEq, Eq, derive_more::Debug, derive_more::Display)] | ||
#[display("{}/{}/{}/?{}", network_id, entity_kind, key_kind, key_space.indicator())] | ||
#[debug("{:?}/{:?}/{:?}/?{}", network_id, entity_kind, key_kind, key_space.indicator())] | ||
pub struct IndexAgnosticPath { | ||
pub network_id: NetworkID, | ||
pub entity_kind: CAP26EntityKind, | ||
pub key_kind: CAP26KeyKind, | ||
pub key_space: KeySpace, | ||
} | ||
|
||
impl IndexAgnosticPath { | ||
pub fn new( | ||
network_id: NetworkID, | ||
entity_kind: CAP26EntityKind, | ||
key_kind: CAP26KeyKind, | ||
key_space: KeySpace, | ||
) -> Self { | ||
Self { | ||
network_id, | ||
entity_kind, | ||
key_kind, | ||
key_space, | ||
} | ||
} | ||
} | ||
|
||
impl From<(NetworkID, DerivationPreset)> for IndexAgnosticPath { | ||
fn from((network_id, agnostic_path): (NetworkID, DerivationPreset)) -> Self { | ||
Self::new( | ||
network_id, | ||
agnostic_path.entity_kind(), | ||
agnostic_path.key_kind(), | ||
agnostic_path.key_space(), | ||
) | ||
} | ||
} | ||
|
||
impl TryFrom<IndexAgnosticPath> for DerivationPreset { | ||
type Error = CommonError; | ||
/// Tries to convert an IndexAgnosticPath to a DerivationPreset, | ||
/// is failing if the path is not a standard DerivationPreset | ||
fn try_from(value: IndexAgnosticPath) -> Result<DerivationPreset> { | ||
match (value.entity_kind, value.key_kind, value.key_space) { | ||
CyonAlexRDX marked this conversation as resolved.
Show resolved
Hide resolved
|
||
( | ||
CAP26EntityKind::Account, | ||
CAP26KeyKind::TransactionSigning, | ||
KeySpace::Unsecurified, | ||
) => Ok(DerivationPreset::AccountVeci), | ||
( | ||
CAP26EntityKind::Identity, | ||
CAP26KeyKind::TransactionSigning, | ||
KeySpace::Unsecurified, | ||
) => Ok(DerivationPreset::IdentityVeci), | ||
(CAP26EntityKind::Account, CAP26KeyKind::TransactionSigning, KeySpace::Securified) => { | ||
Ok(DerivationPreset::AccountMfa) | ||
} | ||
(CAP26EntityKind::Identity, CAP26KeyKind::TransactionSigning, KeySpace::Securified) => { | ||
Ok(DerivationPreset::IdentityMfa) | ||
} | ||
_ => Err(CommonError::NonStandardDerivationPath), | ||
CyonAlexRDX marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} | ||
|
||
impl From<(IndexAgnosticPath, HDPathComponent)> for DerivationPath { | ||
fn from((path, index): (IndexAgnosticPath, HDPathComponent)) -> Self { | ||
assert_eq!(index.key_space(), path.key_space); | ||
Self::new(path.network_id, path.entity_kind, path.key_kind, index) | ||
} | ||
} | ||
|
||
impl DerivationPath { | ||
pub fn agnostic(&self) -> IndexAgnosticPath { | ||
IndexAgnosticPath { | ||
network_id: self.network_id, | ||
entity_kind: self.entity_kind, | ||
key_kind: self.key_kind, | ||
key_space: self.key_space(), | ||
} | ||
} | ||
} | ||
impl HierarchicalDeterministicFactorInstance { | ||
pub fn agnostic_path(&self) -> IndexAgnosticPath { | ||
self.derivation_path().agnostic() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
type Sut = IndexAgnosticPath; | ||
|
||
#[test] | ||
fn try_from_success() { | ||
NetworkID::all().into_iter().for_each(|n| { | ||
let f = |preset: DerivationPreset| { | ||
let sut = preset.index_agnostic_path_on_network(n); | ||
let back_again = DerivationPreset::try_from(sut).unwrap(); | ||
assert_eq!(back_again, preset); | ||
}; | ||
|
||
DerivationPreset::all().into_iter().for_each(|p| f(p)); | ||
}); | ||
} | ||
|
||
#[test] | ||
fn try_from_fail() { | ||
let path = Sut::new( | ||
NetworkID::Stokenet, | ||
CAP26EntityKind::Account, | ||
CAP26KeyKind::AuthenticationSigning, | ||
KeySpace::Unsecurified, | ||
); | ||
assert!(DerivationPreset::try_from(path).is_err()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
mod derivation_preset; | ||
mod index_agnostic_path; | ||
mod quantified_derivation_preset; | ||
mod quantities; | ||
|
||
pub use derivation_preset::*; | ||
pub use index_agnostic_path::*; | ||
pub use quantified_derivation_preset::*; | ||
pub use quantities::*; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use crate::prelude::*; | ||
|
||
#[derive(Clone, Copy, Hash, PartialEq, Eq, derive_more::Debug)] | ||
#[debug("🎯: {:?} #{}", self.derivation_preset, self.quantity)] | ||
pub struct QuantifiedDerivationPreset { | ||
pub derivation_preset: DerivationPreset, | ||
pub quantity: usize, | ||
} | ||
|
||
impl QuantifiedDerivationPreset { | ||
pub fn new(derivation_preset: DerivationPreset, quantity: usize) -> Self { | ||
Self { | ||
derivation_preset, | ||
quantity, | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
use crate::prelude::*; | ||
|
||
/// The quantity of DerivationPreset's to fill cache with. | ||
pub const CACHE_FILLING_QUANTITY: usize = 30; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
mod agnostic_paths; | ||
mod next_index_assigner; | ||
mod provider; | ||
|
||
pub use agnostic_paths::*; | ||
pub use next_index_assigner::*; | ||
pub use provider::*; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
mod next_derivation_entity_index_assigner; | ||
mod next_derivation_entity_index_cache_analyzing_assigner; | ||
mod next_derivation_entity_index_profile_analyzing_assigner; | ||
mod next_derivation_entity_index_with_ephemeral_offsets; | ||
mod next_derivation_entity_index_with_ephemeral_offsets_for_factor_source; | ||
|
||
pub use next_derivation_entity_index_assigner::*; | ||
pub use next_derivation_entity_index_cache_analyzing_assigner::*; | ||
pub use next_derivation_entity_index_profile_analyzing_assigner::*; | ||
pub use next_derivation_entity_index_with_ephemeral_offsets::*; | ||
pub use next_derivation_entity_index_with_ephemeral_offsets_for_factor_source::*; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use crate::prelude::*; | ||
|
||
/// An assigner of derivation entity indices, used by the FactorInstancesProvider | ||
/// to map `IndexAgnosticPath` -> `DerivationPath` for some FactorSource on | ||
/// some NetworkID. | ||
/// | ||
/// This assigner works with the: | ||
/// * cache | ||
/// * profile | ||
/// * local offsets | ||
/// | ||
/// More specifically the assigner's `next` method performs approximately this | ||
/// operation: | ||
/// | ||
/// ```ignore | ||
/// pub fn next( | ||
/// &mut self, | ||
/// fs_id: FactorSourceIDFromHash, | ||
/// path: IndexAgnosticPath, | ||
/// ) -> Result<HDPathComponent> { | ||
/// let next_from_cache = self.cache_analyzing.next(fs_id, path).unwrap_or(0); | ||
/// let next_from_profile = self.profile_analyzing.next(fs_id, path).unwrap_or(0); | ||
/// | ||
/// let max_index = std::cmp::max(next_from_profile, next_from_cache); | ||
/// let ephemeral_offset = self.ephemeral_offsets.reserve() | ||
/// | ||
/// max_index + ephemeral_offset | ||
/// ``` | ||
pub struct NextDerivationEntityIndexAssigner { | ||
profile_analyzing: NextDerivationEntityIndexProfileAnalyzingAssigner, | ||
cache_analyzing: NextDerivationEntityIndexCacheAnalyzingAssigner, | ||
ephemeral_offsets: NextDerivationEntityIndexWithEphemeralOffsets, | ||
} | ||
|
||
impl NextDerivationEntityIndexAssigner { | ||
pub fn new( | ||
network_id: NetworkID, | ||
profile: impl Into<Option<Profile>>, | ||
cache: FactorInstancesCache, | ||
) -> Self { | ||
let profile_analyzing = | ||
NextDerivationEntityIndexProfileAnalyzingAssigner::new(network_id, profile); | ||
Check warning on line 42 in src/factor_instances_provider/next_index_assigner/next_derivation_entity_index_assigner.rs
|
||
let cache_analyzing = NextDerivationEntityIndexCacheAnalyzingAssigner::new(cache); | ||
let ephemeral_offsets = NextDerivationEntityIndexWithEphemeralOffsets::default(); | ||
Self { | ||
profile_analyzing, | ||
cache_analyzing, | ||
ephemeral_offsets, | ||
} | ||
} | ||
|
||
/// Returns the next index for the given `FactorSourceIDFromHash` and | ||
/// `IndexAgnosticPath`, by analyzing the cache, the profile and adding | ||
/// local ephemeral offsets. | ||
pub fn next( | ||
&self, | ||
factor_source_id: FactorSourceIDFromHash, | ||
index_agnostic_path: IndexAgnosticPath, | ||
) -> Result<HDPathComponent> { | ||
let default_index = HDPathComponent::new_with_key_space_and_base_index( | ||
index_agnostic_path.key_space, | ||
U30::new(0).unwrap(), | ||
); | ||
|
||
let maybe_next_from_cache = self | ||
.cache_analyzing | ||
.next(factor_source_id, index_agnostic_path)?; | ||
Check warning on line 67 in src/factor_instances_provider/next_index_assigner/next_derivation_entity_index_assigner.rs
|
||
|
||
let next_from_cache = maybe_next_from_cache.unwrap_or(default_index); | ||
let ephemeral = self | ||
.ephemeral_offsets | ||
.reserve(factor_source_id, index_agnostic_path); | ||
|
||
let maybe_next_from_profile = self | ||
.profile_analyzing | ||
.next(factor_source_id, index_agnostic_path)?; | ||
Check warning on line 76 in src/factor_instances_provider/next_index_assigner/next_derivation_entity_index_assigner.rs
|
||
|
||
let next_from_profile = maybe_next_from_profile.unwrap_or(default_index); | ||
|
||
let max_index = std::cmp::max(next_from_profile, next_from_cache); | ||
|
||
max_index.add_n(ephemeral) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this possibly be expanded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes if we would add ROLA keys. And then I want this match to stop compiling :)