-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FactorInstanceProvider
("FIP") with Serializable FactorInstancesCache
+ consumers of "FIP"
#254
Conversation
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.
Amazing work!
No comments worth spending time on from my side.
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.
couldn't find anything big, which I am not yet sure if it is a good thing or not
anyway, AMAZING job 👏
crates/sargon-uniffi/src/profile/mfa/security_structures/decl_security_structure_of.rs
Outdated
Show resolved
Hide resolved
crates/sargon/src/factor_instances_provider/agnostic_paths/index_agnostic_path.rs
Outdated
Show resolved
Hide resolved
crates/sargon/src/factor_instances_provider/factor_instances_cache/factor_instances_cache.rs
Outdated
Show resolved
Hide resolved
crates/sargon/src/factor_instances_provider/factor_instances_cache/factor_instances_cache.rs
Outdated
Show resolved
Hide resolved
.assert_elements_on_same_network()? | ||
.expect("Should have handled empty accounts case already."); | ||
.expect("Should have handled empty entities case already."); |
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.
we aren't validating that entities aren't empty but that they all belong to same network
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.
Right it is a bit hard to follow, but that function returns None
if empty. Here we are unwrapping an Option not a Result.
Arc::new(self.clients.factor_instances_cache.clone()), | ||
Arc::new(profile_snapshot), | ||
factor_source.clone(), | ||
NetworkID::Mainnet, // we care not about other networks here |
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.
why?
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.
Not all users have Stokenet. Not "important" to check if they have stokenet and pre-derive for that network. We can add it in the future if we want ofc.
|
||
/// Mutable internal state of the collector which builds up the list | ||
/// of public keys from each used factor source. | ||
state: RwLock<KeysCollectorState>, |
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.
Should we also use RwLock in signatures collector to keep track of the state?
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.
Not needed until needed :D
I had to change to RwLock
for Rust async reason, Send / Sync and all that.
self.wrapped.new_wallet().await.into_result() | ||
pub async fn new_wallet( | ||
&self, | ||
should_prederive_instances: bool, |
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.
I had the impression this would be hidden for now and we would end up using
self.wrapped.new_wallet(true)
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.
I can unbreak the breaking change if we want? Maybe let new_wallet
be what it was and add new_wallet_with_pre_derivation(bool)
?
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.
I thought that uniffi::new_wallet()
will just call self.wrapped.new_wallet(true)
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.
We do not wanna use shouldPreDerive: true
for PROD just yet. We wanna try it more first. But we wanna use shouldPreDerive: true
for DEBUG
build to try it out.
UniFFI does not know about #if DEBUG
or not in Swift. So we must surface the ability to control true
/false` to consumers. Which is what I've done. So on Android host, just update to what I've done here:
https://github.com/radixdlt/babylon-wallet-ios/pull/1391/files#r1848198515
@@ -1,5 +1,6 @@ | |||
use crate::{ |
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.
I understand that we use prelude in most of the files. Why in some files we need to explicitly include those mods?
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.
this should probably have been crate::prelude
actually. But in SOME cases we must use paths, because we have marked some symbol as pub(super)
which means that all file that module can access it. So it is not publically re-exported at crate::prelude
level.
lookup_mnemonic: F, | ||
) -> Result<IndexSet<HierarchicalDeterministicFactorInstance>> | ||
where | ||
F: async Fn(FactorSourceIDFromHash) -> Result<MnemonicWithPassphrase>, |
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.
IDE complains about these async F definitions.
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.
it does? hmm strange, which IDE, show me error! :)
but if you run cargo check
in iterm
- all good? Probably your IDE does not recognize the #![feature(async_closure)]
in lib.rs: https://github.com/radixdlt/sargon/blob/integrate_keys_collector/crates/sargon/src/lib.rs#L1
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.
Thanks, it is rust rover
fn index_in_local_key_space(&self) -> U31; | ||
} | ||
pub trait IsInLocalKeySpace: HasIndexInLocalKeySpace + IsKeySpaceAware {} | ||
|
||
impl<T: HasIndexInLocalKeySpace + IsKeySpaceAware> IsInLocalKeySpace for T {} |
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.
Why do we need this impl?
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.
This is essentially a "shortcut"/""typealias"" of traits :) it allows us to constraint some generic U: IsInLocalKeySpace
instead of having to write U: HasIndexInLocalKeySpace + IsKeySpaceAware
. But since it is only a "shortcut" we do want to consider all types which are HasIndexInLocalKeySpace + IsKeySpaceAware
as in fact IsInLocalKeySpace
. So:
impl<T: HasIndexInLocalKeySpace + IsKeySpaceAware> IsInLocalKeySpace for T {}
gives exactly that! Rust magic! 🦀
/// Checks ALL FactorInstances for ALL Personas and Accounts on ALL networks, | ||
/// returns Err(CommonError::FactorInstancesDiscrepancy { .. }) if the same | ||
/// FactorInstances is used between any entity. | ||
pub fn assert_factor_instances_valid(&self) -> Result<()> { |
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.
This uncovered the bug in Android host - where FactorInstances was shared between Personas and Accounts, which was fixed in radixdlt/babylon-wallet-android#1256
Abstract
Add a cache for FactorInstances on file. We pre-derive FactorInstances for every new FactorSource being added. We always try to use FactorInstances from the cache if possible.
Add foundation for Securifying Accounts/Personas by mapping
(Set<EntityAddress>, SecurityStructureOfFactorSources) -> Map<EntityAddress, SecurityStructureOfFactorInstances>
(needs implementation of the proper rules of which FactorSources can be used for which roles according to Matts doc)
Description
Caution
Breaking change in SargonOS, the
newWallet
now takes a requiredshouldPrederiveInstances: bool
parameter, and you SHOULD passfalse
for PROD, but you can passtrue
forDEBUG
builds, seeiOS PR: https://github.com/radixdlt/babylon-wallet-ios/pull/1391/files
Note
Coverage is much higher than reported, tarpaulin fails to understand that parameters passed to a function on multiple lines are in fact tested.
This PR does quite a few things, but I will try to describe it as a delta compared to the code being migrated over from Sargon-MFA repo - which post merging this can be deleted.
KeysCollector
from Sargon-MFA, see original PR with KeysCollector (and SignaturesCollector) which got review by @matiasbzurovski and @GhenadieVP : SignaturesCollector + KeysCollector sargon-mfa#1Delta compared to Sargon-MFA
The code in Sargon-MFA, as @micbakos-rdx painfully knows, was quite simplified in order for me to solve the crux of the problem, it allowed me to iterate fast and solve the underlying hard problems. One fundamental simplification which Sargon-MFA repo did was to simplify
MatrixOfFactor<F> = (PrimaryRoleWithFactors<F>, RecoveryRoleWithFactors<F>, ConfirmationRoleWithFactors<F>)
intoRoleWithFactors<F>
(but called "Matrix"). Another major simplification was Entities and FactorInstances (did not contain real keys).The implementation in Sargon-MFA deleted the FactorInstances from the cache "eagerly". However, this is not so useful, since we have more "steps" in SargonOS which might fail. For example, when we query the FactorInstancesProvider (which tries to load from cache) for an FactorInstance for creating a new virtual account, we wanna use that FactorInstance to create a new account, then save it into Profile and finally persist the serialized Profile JSON into SecureStorage. If any of these steps would fail, we would already have consumed the FactorInstance, and thus create "gaps" in the Derivation Entities. So an important change I've made here (compared to Sargon-MFA repo impl) is a
InstancesInCacheConsumer
, which contains a single (async throwing) methodconsume
, which we can call after having successfully persisted the updated Profile. This allows us to ensure we dont create such gaps.Whats new
Introduce:
KeysCollector
FactorInstancesProvider
FactorInstancesCache
InstancesInCacheConsumer
And higher level providers:
VirtualEntityCreatingInstanceProvider
SecurifyEntityFactorInstancesProvider
CacheFiller
SargonOS APIs:
Gated behind
#[cfg(test)]
for now:make_security_structure_of_factor_instances_for_entities...
Which maps
SecurityStructureOfFactorSources -> IndexMap<EntityAddress, SecurityStructureOfFactorInstances>
for provided entity addresses.__OFFLINE_ONLY_securify_accounts
Which mutates profile and changes accounts to be Securified, using
make_security_structure_of_factor_instances_for_entities...
above, named like that since it does not try to submit any transaction on network - no "real securify".Prod available
CRUD operations for Persona(s), such as create, update, batch create (useful for tests in hosts).
Further Changes to
SargonOS
SargonOS
has received a third field:pub struct SargonOS { pub(crate) profile_state_holder: ProfileStateHolder, pub(crate) clients: Clients, + pub(crate) interactors: Interactors }
Where
Interactors
currently is:Maybe we change this to contain a
signing_interactors
of typeSignInteractors
- already inmain
branch but not used by hosts (only used by tests):pub struct Interactors { pub key_derivation: Arc<dyn KeysDerivationInteractors>, +. pub signing_interactors: Arc<dyn SignInteractors> }
But maybe we should merge them into one, a a
UseFactorSourcesInteractors
which can either be used for "purpose"Sign
orKeysDerivation
.Booting SargonOS now has a new ctor (non test, for test we have many new):
and the existing
boot
calls it and creates a defaultInteractors
usingNoUIInteractorForKeyDerivation::new(secure_storage)
:I've not updated the
sargon-uniffi
crate though, so currently SargonOS will be usingNoUIInteractorForKeyDerivation::new(secure_storage)
. But I dont think any of the hosts are using SargonOS method to create accounts or add FactorSources.Future directions
We need to decide if we wanna merge the
KeyDerivationInteractors
andSignInteractors
and we need production ready implementation in hosts.With this PR merged and with production ready interactors, all the fundamental pieces for securifying entities are there! Exciting! What remains is changing the
__OFFLINE_ONLY_securify_*
code here to also building the (batch of) manifest(s) for entity(ies) and ask host to present them to user for review, sign them and (batch) submit them (with a queue) to the network.And we also need to update the
fulfulling_matrix_of_factor_sources_with_instances
function to enforce Matts rules for which FactorInstances can be used in each role, according the rules doc.