Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

SignaturesCollector + KeysCollector #1

Merged
merged 275 commits into from
Sep 2, 2024
Merged

Conversation

CyonAlexRDX
Copy link
Contributor

@CyonAlexRDX CyonAlexRDX commented Aug 26, 2024

This is a migration PR of all the code from one-does-not-simply-sign.

Important

I strongly suggest you read the analog "Bistro Chez Remy" before you review this PR, if you haven't already done so, find it here on Confluence

It contains two modules, put in derivation and signing folders in src and in different test mods in tests.

inside src there is a types folder/mod which contains some new types and also the sargon_types.rs - which is a best effort collection of "stubbed" types from Sargon - that is, it contains the bare minimum impl of the types needed to implement the signing and derivation modules. The goal is - when this repo is migrated into Sargon - to be able to remove sargon_types.rs and the code should just compile with all tests passing.

The DOC is not 100% accurate, I welcome review of it though, but yes, there will be missing doc and outdates docs... so suggestions are welcome.

Design

Note

But the SignaturesCollector and KeysCollector are very very similar, why no generics?
I tried for a while but failed three times (1, 2, 3), ultimately the asymmetry of input and usage of factor sources made a generic solution to complex and hard. Key Derivation lacks one dimension and that is hashmap of intent hashes to sign with which derivation path. Also Key Derivation is much simpler since FactorSources cannot be skipped.

For the purpose of giving an overview of both KeysCollector and SignaturesCollectors, we can think of them as a single collector, or "accumulator" of FactorInstances - I previously called it "FactorInstancesAccumulator": FIA.

Collector / FIA

struct FIA {
    /// Contains a set of FactorSources and "Interactors" ("Driveres" / "Adapters") used by Sargon
    /// to dispatch "UseFactorSourcesRequest"s (list of inputs for each factor source, typically
    /// derivation paths and in the case of signing intent hashes) and async awaiting 
    /// "UseFactorSourcesResponse"s (list of outputs for each factor source, PublicKeys and
    /// HDSignatures respectively).
    dependencies: Dependencies

    /// List "collected"/"accumulated" outputs from factor sources we have used so far.
    /// In case of signing, this also contains information about which factor sources have
    /// been skipped, with the possibility of calculating if any transaction would fail
    /// if we skip with any of the next factor sources.
    state: State,

}

impl FIA {

    pub fn new_signing<Lookup>(
        all_factor_sources_in_profile: Set<FactorSource>,
        transaction_intents: Set<TransactionIntents>,
        interactors: Vec<dyn SignWithFactorSourceInteractor>,
        /// we use RET to get `addresses_of_accounts_required_to_sign` a transaction
        /// then we need to lookup the Account of each AccountAddress in Profile
        /// and get its SecurityState, in case of Unsecurified we can create a 
        /// MatrixOfFactorInstances with a single FactorInstances in threshold factors
        /// with threshold of 1, and in case of Securified we return the Matrix.
        address_to_factor_matrix_lookup: Lookup
    ) -> Self where Lookup: Fn(AccountAddress) -> MatrixOfFactorInstances {
        /// analyze required FactorSources, sort in increasing "friction" order
        /// init state
    }

    pub fn new_derivation(
        all_factor_sources_in_profile: Set<FactorSource>,
        derivation_paths: Map<FactorSourceID, Set<DerivationPath>>,
        interactors: Vec<dyn DeriveKeyWithFactorSourceInteractor>,
    ) -> Self {
        /// analyze required FactorSources, sort in increasing "friction" order
        /// init state
    }
}

impl FIA {
    pub async fn collect(self) -> Outcome {
        for factors_of_kind in self.dependencies.factors_of_kind.iter() {
            let interactor = self.get_interactor(factors_of_kind.kind);
            let client = UseFactorSourceClient::new(interactor);
            client
                .use_factor_sources(factors_of_kind.factor_sources(), self)
                .await?;
        }
        self.state.outcome()
    }
}

Parallel vs Serial requests

On iOS - and also on Android I think (right @micbakos-rdx ?) - we can load MANY mnemonics from Secure Storage in one go, thus only requiring a single biometrics prompt for the user when using many DeviceFactorSources. That is a "Parallel" usage of factor source from the FIA/Collector in Sargons perspective. Which is something we implement in iOS/ Android hosts by impl "Interactor" traits this.

As per current design it will be 4 interactor traits to impl in host:

  • Parallel Sign
  • Serial Sign
  • Parallel Derive Key
  • Serial Derive Key

One very important thing to note is the implication of serial vs parallel for signing, where for serial, if user skips a factor source, the state of "skipable" of the next factor source depends on the last. That is to say, we must lazily/Just-In-Time create the SerialSignRequest between dispatching them, since it contains information about which transactions would fail if we would skip using this factor source. But for ParelellSigning we must aggregate/merge the lists of transaction which would fail, because user might not be skipping one DeviceFactor, but rather two if she cancels biometrics/skips.

@CyonAlexRDX CyonAlexRDX merged commit 29b33b9 into main Sep 2, 2024
4 checks passed
@CyonAlexRDX CyonAlexRDX deleted the migrate_from_other_repo branch September 2, 2024 10:27
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants