-
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
Adding new device factor source #380
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #380 +/- ##
==========================================
- Coverage 92.56% 92.56% -0.01%
==========================================
Files 1227 1230 +3
Lines 28348 28495 +147
Branches 85 85
==========================================
+ Hits 26239 26375 +136
- Misses 2098 2109 +11
Partials 11 11
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
let mnemonic_with_passphrase = | ||
MnemonicWithPassphrase::new(mnemonic.clone()); | ||
let factor_source = DeviceFactorSource::babylon( | ||
false, |
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.
Can you decl_bool_type
for this bool? Is it if it is Main?
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 cascades into many changes and I'm not sure it's worth it, WDYT?
let mut csprng = OsRng; | ||
let mut confirmation_indices: HashSet<u8> = HashSet::new(); | ||
|
||
while (confirmation_indices.len() as u8) |
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.
Instead of this you can use:
generate_bytes::<3>().into_iter().map(|b| b as usize).collect::<IndexSet<_>>()
And done.
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 think this will uncover the case when the same value is generated, with the while
we're sure we'll end up with 3 distinct values.
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, so lets do generate_bytes::<100>().into_iter().map(|b| b as usize).take(3).collect::<IndexSet<_>>()
that will statistically never fail.
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.
and when #380 (comment)
it becomes:
generate_bytes::<100>()
.into_iter()
.map(|b| b as usize)
.take(Self::NUMBER_OF_WORDS_OF_MNEMONIC_USER_NEED_TO_CONFIRM_EXCL_CHECKSUM)
.collect::<IndexSet<_>>()
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'm not sure this is a good idea anymore, because I got the test failing a few times, then I increased the value from 100 to 1000 and it seemed fine for a while, but just now it failed again. @CyonAlexRDX Do you agree to revert to the previous code to avoid the tests occasionally failing? Or should we increase the value again? Not sure what's better.
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.
Improved this by taking only the first N unique values from the randomly generated ones, so now statistically it shouldn't ever fail.
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/uniffi/uniffi_SPLIT_ME/src/system/sargon_os/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/uniffi/uniffi_SPLIT_ME/src/system/sargon_os/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/uniffi/uniffi_SPLIT_ME/src/system/sargon_os/add_factor/device_factor_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor_source/device_factor_source_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor_source/device_factor_source_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor_source/device_factor_source_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor_source/device_factor_source_adding_manager.rs
Outdated
Show resolved
Hide resolved
crates/system/os/factors/src/add_factor_source/device_factor_source_adding_manager.rs
Outdated
Show resolved
Hide resolved
jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/factors/AddNewFactorManagerTest.kt
Outdated
Show resolved
Hide resolved
.await?; | ||
|
||
if let Err(e) = self | ||
.pre_derive_and_fill_cache_with_instances_for_factor_source( |
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.
hmm would be nice with .inspect_err
on the Result from pre_derive_and_fill_cache_with_instances_for_factor_source
to avoid the if let Err
but I think maybe tricky due to async...?
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 would have been nice, but I didn't manage to make it work. Besides await is only allowed inside async functions and blocks
error there is also the fact that the operation we perform in case of error returns a Result which we want to propagate.
#[uniffi::export] | ||
impl DeviceMnemonicBuilder { | ||
/// Generates a new mnemonic | ||
pub fn create_new_mnemonic_with_passphrase(self: Arc<Self>) -> Arc<Self> { |
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 think this should be named:
generate_new
not create_new
I think.
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.
What about create_mnemonic_with_passphrase_from_words
? Should it be generate_mnemonic_with_passphrase_from_words
for consistency?
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.
Generate if generating random entropy. Create If using mnemonic words
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.
So your last commit looks good
|
||
/// The result of the `build` function from `DeviceMnemonicBuilder`. | ||
#[derive(Debug, PartialEq, uniffi::Enum)] | ||
pub enum DeviceMnemonicBuildResult { |
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 would call this DeviceMnemonicBuildOutcome
- we try to always let **Result
be a Result
type.
#[uniffi::export] | ||
impl DeviceMnemonicBuilder { | ||
/// Generates a new mnemonic | ||
pub fn generate_new_mnemonic_with_passphrase(self: Arc<Self>) -> Arc<Self> { |
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.
maybe we should drop the _with_passphrase
here and in create_mnemonic_with_passphrase_from_words
since we do not in fact support BIP39Passphrases to be used.
|
||
let indices = sut.clone().get_indices_in_mnemonic_of_words_to_confirm(); | ||
|
||
let r0 = sut.clone().build( |
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.
r
stands for? ah result
? lets call it let outcome
. And Rust supports shadowing! So no need for outcome1
you can just let outcome = // second
and again and again. since it looks like you dont need r0
and r1
?
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.
LGTM, left some last minor remarks. Good job!
account-for-display@1.2.2 addresses@1.2.2 assert-json@1.2.2 build-info@1.2.2 bytes@1.2.2 cap26-models@1.2.2 clients@1.2.2 core-collections@1.2.2 core-misc@1.2.2 core-utils@1.2.2 drivers@1.2.2 ecc@1.2.2 encryption@1.2.2 entity-by-address@1.2.2 entity-foundation@1.2.2 error@1.2.2 factor-instances-provider@1.2.2 factors@1.2.2 factors-supporting-types@1.2.2 gateway-client-and-api@1.2.2 gateway-models@1.2.2 has-sample-values@1.2.2 hash@1.2.2 hierarchical-deterministic@1.2.2 home-cards@1.2.2 host-info@1.2.2 http-client@1.2.2 identified-vec-of@1.2.2 interactors@1.2.2 key-derivation-traits@1.2.2 keys-collector@1.2.2 manifests@1.2.2 metadata@1.2.2 network@1.2.2 next-derivation-index-ephemeral@1.2.2 numeric@1.2.2 prelude@1.2.2 profile@1.2.2 profile-account@1.2.2 profile-account-or-persona@1.2.2 profile-app-preferences@1.2.2 profile-base-entity@1.2.2 profile-gateway@1.2.2 profile-logic@1.2.2 profile-persona@1.2.2 profile-persona-data@1.2.2 profile-security-structures@1.2.2 profile-state-holder@1.2.2 profile-supporting-types@1.2.2 radix-connect@1.2.2 radix-connect-models@1.2.2 sargon@1.2.2 sargon-os@1.2.2 sargon-os-accounts@1.2.2 sargon-os-derive-public-keys@1.2.2 sargon-os-factors@1.2.2 sargon-os-security-center@1.2.2 sargon-os-signing@1.2.2 sargon-os-transaction@1.2.2 sargon-uniffi@1.2.2 sargon-uniffi-conversion-macros@1.2.2 security-center@1.2.2 short-string@1.2.2 signatures-collector@1.2.2 signing-traits@1.2.2 sub-systems@1.2.2 time-utils@1.2.2 transaction-foundation@1.2.2 transaction-models@1.2.2 Generated by cargo-workspaces
@@ -155,6 +156,63 @@ impl FactorSource { | |||
} | |||
} | |||
|
|||
impl FactorSource { | |||
pub fn with_details( |
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 function is mostly constrained by mnemonic_with_passphrase, so I would add an its name the mention of MWP, to know this applies only to the kinds of factor sources which have their id derived from MWP.
/// Exposes functions to be called by hosts to be able to use the resulting `MnemonicWithPassphrase`. | ||
#[derive(Debug)] | ||
pub struct DeviceMnemonicBuilder { | ||
mnemonic_with_passphrase: RwLock<Option<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.
Do we actually need the RwLock here? Is it expected to have many concurrent readers/writers
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 don't think so, at least not according to the current requirements, but I think this is a good thing to have in place for the values accessed/modified by methods called from hosts directly. Do you think we should remove the lock?
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.
Well it is no big impact having it here, it just adds additional complexity for no apparent need. It creates a sort of wrong expectation, that the state really needs to be protected. I would remove it if possible, unless it is really needed.
/// | ||
/// And also emits `Event::ProfileSaved` after having successfully written the JSON | ||
/// of the active profile to secure storage. | ||
async fn add_new_factor_source( |
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.
The name should probably me more specific to mnemonic_with_passphrase
if factor_source_kind == FactorSourceKind::Device { | ||
self.secure_storage | ||
.save_mnemonic_with_passphrase( | ||
&mnemonic_with_passphrase, | ||
&factor_source.id_from_hash(), | ||
) | ||
.await?; | ||
} |
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.
If this fails, the profile saved factor source should be reverted
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.
Good point. Or we could update the profile after saving the mnemonic to storage.
self.secure_storage | ||
.delete_mnemonic(&factor_source.id_from_hash()) | ||
.await?; |
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.
What if the kind is not device?
let result = os | ||
.with_timeout(|x| { | ||
x.add_new_factor_source( | ||
FactorSourceKind::Device, |
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.
Do add a test with another kind than Device.
/// Exposes functions to be called by hosts to be able to use the resulting `MnemonicWithPassphrase`. | ||
#[derive(Debug)] | ||
pub struct DeviceMnemonicBuilder { | ||
mnemonic_with_passphrase: RwLock<Option<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.
Well it is no big impact having it here, it just adds additional complexity for no apparent need. It creates a sort of wrong expectation, that the state really needs to be protected. I would remove it if possible, unless it is really needed.
self.update_profile_with(|p| { | ||
p.factor_sources.append(factor_source.clone()); | ||
Ok(()) | ||
}) | ||
.await?; |
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.
Same thing applies here - if profile update failed, the previously added device factor source should be removed from the secure storage.
Consider using a block to group together this and self.pre_derive_and_fill_cache_with_instances_for_factor_source
, something like:
let setup_result = {
self.update_profile_with(|p| {
p.factor_sources.append(factor_source.clone());
Ok(())
})
.await?;
self
.pre_derive_and_fill_cache_with_instances_for_factor_source(
factor_source.clone(),
)
.await
};
if let Err(e) = setup_result {
// revert logic on error
}
uniffi::Object
calledDeviceMnemonicBuilder
exposing functionality required for the new UI flow for adding a device factor source. Similarly,PasswordMnemonicBuilder
andOffDeviceMnemonicBuilder
will follow;sargonOS.is_factor_source_already_in_use
checking if a factor source is already is use;sargonOS.add_new_factor_source
, which performs the required validations, pre-derives and fills the cache with instances, save factor source into profile, and emits corresponding events.