Skip to content
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

[WIP] Apply Shield Wallet Interaction Part 2 #355

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

CyonAlexRDX
Copy link
Contributor

@CyonAlexRDX CyonAlexRDX commented Jan 23, 2025

Jira ABW-4056

Caution

Sargon breaking change in Profile format - only Wallet team devs affected since models are MFA models end users have not been able to produce yet.
renamed "auto_confirm" to "time_based_confirmation" instead since "auto" was indeed very misleading! Since we MUST call confirm in a manifest and submit to the network. Is does not happen "automatically"!

Tip

Not ready to be merged but I need early input from protocol team on logic

Note

Part 2 of N
Part 1 merged into main

Update Factors of Securified Entities

For securified entities we need six different manifests to sign depending on which roles the user is able to exercise at the moment of applying the shield.

Host will display the variant which is exercising all roles with confirmation rule using quick confirm - but if user slides to sign, Sargon will under the hood try to sign all six (maybe less if we can finish early). If she does not have access to some factor belonging to Primary role she can skip signing with that factor and all manifest variants out of those six which requires Primary will be invalid. We can still withdraw XRD from AccessController XRD vault so we can continue trying sign manifests requiring Recovery role. Just that we cannot top up the XRD vault of the access controller. If she manages to sign with the required factors of Recovery role we proceed trying to sign with factors of ConfirmationRole. Which can fail too, then we use time based fallback, if user was unable to exercise Primary Role and Reocery role we fail.

This PR adds logic for creating all six flavours:

pub enum RolesExercisableInTransactionManifestCombination {

InitiateWithPrimaryCompleteWithRecovery,
    InitiateWithPrimaryCompleteWithConfirmation,
    InitiateWithRecoveryCompleteWithConfirmation,

    InitiateWithRecoveryDelayedCompletion,

   
    /// ‼️ REQUIRES "Dugong" ‼️
    InitiateWithPrimaryDelayedCompletion,
}

};

#[derive(Debug, Clone)]
pub struct AccessControllerFactorsAndTimeInput {
Copy link
Contributor

@Sajjon Sajjon Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shared type to create:

  • AccessControllerInitiateRecoveryAsPrimaryInput
  • AccessControllerInitiateRecoveryAsRecoveryInput
  • AccessControllerQuickConfirmPrimaryRoleRecoveryProposalInput
  • AccessControllerQuickConfirmRecoveryRoleRecoveryProposalInput
  • AccessControllerTimedConfirmRecoveryInput

Input to call_method instruction, using SecurityStructureOfFactorInstances - which is Into<RuleSet> (and time u32).

let entity_address = securified_entity.entity.address();

// ACCESS_CONTROLLER_CREATE_PROOF_IDENT
let mut builder = TransactionManifest::produce_owner_badge(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does:

builder.call_method(
    access_controller_address.scrypto(),
    ACCESS_CONTROLLER_CREATE_PROOF_IDENT,
    (),
);


// INITIATE RECOVERY
let (init_method, init_input) =
kind.input_for_initialization(factors_and_time_input);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on selector of which of the six TransactionManifest flavours as selected by kind: TransactionManifestApplySecurityShieldKind we use different roles for initialization of recovery (and the correct concrete scrypto "Input"-type (Box dyn-ed))

For details see transaction_manifest_apply_security_shield_kind.rs which was added by this PR

Copy link

codecov bot commented Jan 24, 2025

Codecov Report

Attention: Patch coverage is 88.94009% with 24 lines in your changes missing coverage. Please review.

Project coverage is 92.40%. Comparing base (22a4d57) to head (15e0558).

Files with missing lines Patch % Lines
...eld/sargon_os_apply_security_shield_interaction.rs 0.00% 6 Missing ⚠️
...exercisable_in_transaction_manifest_combination.rs 80.00% 5 Missing ⚠️
...eld/manifests_securify_shield_securified_entity.rs 89.18% 4 Missing ⚠️
...shield/access_controller_factors_and_time_input.rs 85.71% 3 Missing ⚠️
.../models/supporting-types/src/securified_persona.rs 66.66% 2 Missing ⚠️
...urity_shield/top_up_access_controller_xrd_vault.rs 91.66% 2 Missing ⚠️
...ces_structures/matrices/builder/matrix_template.rs 75.00% 1 Missing ⚠️
...structures/matrices/matrix_of_factor_source_ids.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #355      +/-   ##
==========================================
- Coverage   92.59%   92.40%   -0.20%     
==========================================
  Files        1169      890     -279     
  Lines       26153    22859    -3294     
  Branches       81       81              
==========================================
- Hits        24217    21122    -3095     
+ Misses       1925     1726     -199     
  Partials       11       11              
Flag Coverage Δ
kotlin 97.73% <ø> (ø)
rust 92.07% <88.94%> (-0.14%) ⬇️
swift ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

// Set Rola Key
let should_set_rola_key = security_structure_of_factor_instances
.authentication_signing_factor_instance
!= securified_entity
Copy link
Contributor

@Sajjon Sajjon Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont set ROLA key if it is unchanged

owner_badge_bucket_name,
);
};
let (mut builder, owner_badge_bucket) = Self::put_owner_badge_in_bucket(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still calls "securify" - just refactored a bit.

PrimaryAndExplicitConfirmation,

/// (Primary Time) ‼️ REQUIRES "Dugong" ‼️
PrimaryWithTimedAutoConfirm,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GhenadieVP I think Matt said we would have Dugong available. So we will remove this comment once Dugong is live. Maybe I should comment out PrimaryWithTimedAutoConfirm for now...

pub fn input_for_initialization(
&self,
factors_and_time: &AccessControllerFactorsAndTimeInput,
) -> (&'static str, Box<dyn CallMethodInput>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhedey naivly I tried returning (&'static str, impl ResolvableArguments) which did not compile. Then I tried Box<dyn ResolvableArguments> which compiled here, but did not compile when I tried passing it to call_method directly, so I tried .deref() on the box to get the "inner" dyn ResolvableArguments which did not compile... bah. But when I introduced this "dummy trait" (see above):

// Trickery to allow `Box<dyn ResolvableArguments>` - which is not allowed it seems,
// but this solves it, since in Scrypto they impl ResolvableArguments for ManifestEncode + ManifestSborTuple
pub trait CallMethodInput: ManifestEncode + ManifestSborTuple {}
impl<T: ManifestEncode + ManifestSborTuple> CallMethodInput for T {}

then everything works. It is just 2 lines of code so fine for now. But maybe you have ideas on how it can be removed? :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it, the reason why that trick works is subtle (basically T: CallMethodInput => T: ManifestEncode + ManifestSborTuple => Box<T>: ManifestEncode + ManifestSborTuple(via blanket impls on Box<T> of ManifestEncode and ManifestSborTuple) => Box<T>: ResolvableArguments).

The trick seems fine to me.

Other fixes in the repo would be to re-work the ResolvableArguments trait in the scrypto repo, either:

  • To itself have the : ManifestEncode + ManifestSborTuple bounds
  • To be dyn-friendly, e.g. changing the self to &self. Currently Box<dyn ResolvableArguments> has no methods because the resolve method isn't dyn-friendly.
pub trait ResolvableArguments {
    fn resolve(self) -> ManifestValue;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

(SCRYPTO_ACCESS_CONTROLLER_QUICK_CONFIRM_PRIMARY_ROLE_RECOVERY_PROPOSAL_IDENT, Box::new(ScryptoAccessControllerQuickConfirmPrimaryRoleRecoveryProposalInput::from(factors_and_time)))
}
} else {
// Time based
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See https://github.com/radixdlt/sargon/pull/355/files#r1928373228 I think this is wrong, should probably update this method to return Option<(&'static str, Box<dyn CallMethodInput>)> and then None if we cannot quick confirm.

EntitySecurityState::Securified { value } => {
builder = builder.call_method(
value.access_controller_address.scrypto(),
ACCESS_CONTROLLER_CREATE_PROOF_IDENT,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ACCESS_CONTROLLER_CREATE_PROOF_IDENT correct for when we are performing recovery?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this question does not make any sense... but I thought we always needed to invoke an instruction which triggers requirement to "auth". but maybe the INITIATE recovery methods will do that...?

@CyonAlexRDX CyonAlexRDX marked this pull request as ready for review January 24, 2025 09:25
let access_controller_address = securified_entity
.securified_entity_control
.access_controller_address;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have forgotten to add the withdrawal of XRD from AccessControllera XRD vault.

I’m adding it now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right we need to add the locking of fee against XRD vault of AccessController later (like the top up) - since we dont know how big a fee to lock. Ive added modify_manifest_add_lock_fee_against_xrd_vault_of_access_controller. Take a look at it 4d8fc6d

.securified_entity_control
.access_controller_address;
// Lock fee against XRD vault of the access controller
builder = builder.call_method(
Copy link
Contributor

@Sajjon Sajjon Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xOmarA here I'm appending this instruction last. Is that OK? Or must this be prepended, index 0 of the manifest? if so I need to do a lot of trickery to do that. Since I cannot create the instruction in rust code "manually" and since calling buider.call_method will put it last...

@GhenadieVP alternatively we just lock a "large" amount. Which ""should work"" for most shields... and if we do that we can lock fee in apply_security_shield_for_securified_entity at index 0 https://github.com/radixdlt/sargon/blob/ac/wallet_interaction_applying_shield_abw-4056_part2/crates/transaction/manifests/src/manifests_security_shield/manifests_securify_shield_securified_entity.rs#L37

/// be quick confirmed. We need to figure out how we best represent this
/// in Profile. Perhaps a new variant on ProvisionalSecurityConfig? Something
/// like:
/// `WaitingForTimedRecovery(SecurityStructureOfFactorInstances)`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GhenadieVP good thing we kept ProvisionalSecurifiedConfig an enum! I think we need a second variant - WaitingForTimedRecovery(SecurityStructureOfFactorInstances)

@@ -247,7 +247,7 @@ mod tests {
}
]
},
"numberOfDaysUntilAutoConfirm": 14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking profile change! But ok since only Radix Wallet team devs are affected.

numberOfDaysUntilAutoConfirm made it sound like confirmation would happen automagically. Which perhaps even I believed they would when I named this variable...

@@ -39,13 +39,14 @@ pub struct AbstractMatrixBuilderOrBuilt<
pub(crate) confirmation_role:
AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, MODE_OF_ROLE, FACTOR>,

pub number_of_days_until_auto_confirm: u16,
pub number_of_days_until_timed_confirmation_is_callable: u16,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking profile change! But ok since only Radix Wallet team devs are affected.

numberOfDaysUntilAutoConfirm made it sound like confirmation would happen automagically. Which perhaps even I believed they would when I named this variable...

#[error("The number of days until auto confirm must be greater than zero")]
NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero,
#[error("The number of days until timed confirm is callable must be greater than zero")]
NumberOfDaysUntilTimeBasedConfirmationMustBeGreaterThanZero,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking Profile format change number 2 - @sergiupuhalschi-rdx / @danvleju-rdx / @matiasbzurovski might get affected in host

@@ -379,17 +379,20 @@ impl MatrixBuilder {
self.primary_role.get_threshold()
}

pub fn set_number_of_days_until_auto_confirm(
pub fn set_number_of_days_until_timed_confirmation_is_callable(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking API change - @sergiupuhalschi-rdx / @danvleju-rdx / @matiasbzurovski will get affected in host

);

// QUICK CONFIRM RECOVERY - Only if we can exercise the confirmation role explicitly.
if let Some((confirm_method, confirm_input)) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only call "quick confirm" if we can exercise confirmation role explicitly. Else we skip quick confirm and we will LATER (number_of_days_until_timed_confirmation_is_callable later) make another transaction.

@@ -25,7 +25,7 @@ macro_rules! matrix_conversion {
pub recovery_role: #recovery_role_type,
pub confirmation_role: #confirmation_role_type,

pub number_of_days_until_auto_confirm: u16,
pub number_of_days_until_timed_confirmation_is_callable: u16,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GhenadieVP we should probably change this to number_of_minutes though! Which easily support days. But if we ever wanted to allow less than a day in the future we cannot. i.e. the host and builders will continue using Day as smallest granularity, but Profile probably SHOULD use Minute as time unit - since that is what rest of Radix Stack uses. Right? If you agree, I will make that change now - so we dont have to have two consequetive breaking profile changes on top of each other.

#[derive(Clone, Debug, PartialEq, Eq, Hash, enum_iterator::Sequence)]
pub enum TransactionManifestApplySecurityShieldKind {
/// (Primary Recovery Confirmation)
PrimaryAndRecoveryWithExplicitConfirmation,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under the two of three model, lots of these don't make sense to me.

I'd expect something like:

  • InitiateWithPrimaryCompleteWithRecovery
  • InitiateWithPrimaryCompleteWithConfirmation
  • InitiateWithRecoveryCompleteWithConfirmation
  • InitiateWithPrimaryDelayedCompletion // Either Timed or (in future) external factors
  • InitiateWithRecoveryDelayedCompletion // Either Timed or (in future) external factors

And then in future, there might be modes where the user can't initiate themselves and needs to send a request to an external source (e.g. a friend or custodian) to do, which might look like:

  • ExternalInitiateWithPrimary
  • ExternalInitiateWithRecovery

Copy link
Contributor Author

@CyonAlexRDX CyonAlexRDX Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good point.

Thanks!

I answered on Slack too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants