Skip to main content

miden_testing/mock_chain/
auth.rs

1// AUTH
2// ================================================================================================
3use alloc::collections::BTreeSet;
4use alloc::vec;
5use alloc::vec::Vec;
6
7use miden_protocol::Word;
8use miden_protocol::account::auth::{AuthScheme, AuthSecretKey};
9use miden_protocol::account::{AccountComponent, AccountProcedureRoot};
10use miden_protocol::note::NoteScriptRoot;
11use miden_protocol::testing::noop_auth_component::NoopAuthComponent;
12use miden_protocol::transaction::TransactionScriptRoot;
13use miden_standards::account::auth::multisig_smart::ProcedurePolicy;
14use miden_standards::account::auth::{
15    Approver,
16    ApproverSet,
17    AuthGuardedMultisig,
18    AuthGuardedMultisigConfig,
19    AuthMultisig,
20    AuthMultisigConfig,
21    AuthMultisigSmart,
22    AuthMultisigSmartConfig,
23    AuthNetworkAccount,
24    AuthSingleSig,
25    AuthTxFeeCollector,
26    GuardianConfig,
27    SponsorshipPolicy,
28};
29use miden_standards::account::fees::FeePolicyManager;
30use miden_standards::testing::account_component::{
31    ConditionalAuthComponent,
32    IncrNonceAuthComponent,
33};
34use miden_tx::auth::BasicAuthenticator;
35use rand::SeedableRng;
36use rand_chacha::ChaCha20Rng;
37
38/// Specifies which authentication mechanism is desired for accounts
39#[derive(Debug, Clone)]
40pub enum Auth {
41    /// Creates a secret key for the account and creates a [BasicAuthenticator] used to
42    /// authenticate the account with [AuthSingleSig].
43    BasicAuth { auth_scheme: AuthScheme },
44
45    /// Multisig
46    Multisig {
47        approver_set: ApproverSet,
48        proc_threshold_map: Vec<(AccountProcedureRoot, u32)>,
49    },
50
51    /// Guarded multisig.
52    GuardedMultisig {
53        approver_set: ApproverSet,
54        guardian_config: GuardianConfig,
55        proc_threshold_map: Vec<(AccountProcedureRoot, u32)>,
56    },
57
58    /// Multisig with smart per-procedure policy configuration.
59    MultisigSmart {
60        approver_set: ApproverSet,
61        proc_policy_map: Vec<(Word, ProcedurePolicy)>,
62    },
63
64    /// Creates a mock authentication mechanism for the account that only increments the nonce.
65    IncrNonce,
66
67    /// Creates a mock authentication mechanism for the account that does nothing.
68    Noop,
69
70    /// TX_FEE collector authentication: forwards the single asset of every consumed note into one
71    /// P2ID note for the target given by the auth args, verifies a signature over the
72    /// transaction summary and leaves the account unchanged (the nonce is only incremented when
73    /// the account is created).
74    ///
75    /// Creates a secret key and a [BasicAuthenticator] to sign with, like [`Auth::BasicAuth`].
76    TxFeeCollector { auth_scheme: AuthScheme },
77
78    /// Creates a mock authentication mechanism for the account that conditionally succeeds and
79    /// conditionally increments the nonce based on the authentication arguments.
80    ///
81    /// The auth procedure expects the first three arguments as [99, 98, 97] to succeed.
82    /// In case it succeeds, it conditionally increments the nonce based on the fourth argument.
83    Conditional,
84
85    /// Network-account authentication that restricts the account to consuming only notes whose
86    /// script roots appear in `allowed_script_roots` (must be non-empty), and to executing only
87    /// transaction scripts whose roots appear in `allowed_tx_script_roots` (may be empty).
88    ///
89    /// The `fee_policy_manager` initializes the fee-policy storage the auth component owns and
90    /// contributes the components making its fee policies dispatchable. Built with
91    /// [`AuthNetworkAccount::new`], the expansion also yields
92    /// [`BasicWallet`](miden_standards::account::wallets::BasicWallet).
93    NetworkAccount {
94        allowed_script_roots: BTreeSet<NoteScriptRoot>,
95        allowed_tx_script_roots: BTreeSet<TransactionScriptRoot>,
96        fee_policy_manager: FeePolicyManager,
97        sponsorship_policy: SponsorshipPolicy,
98    },
99}
100
101impl Default for Auth {
102    /// Returns the most common authentication scheme used in tests:
103    /// [`Auth::BasicAuth`] with [`AuthScheme::Falcon512Poseidon2`].
104    fn default() -> Self {
105        Auth::BasicAuth {
106            auth_scheme: AuthScheme::Falcon512Poseidon2,
107        }
108    }
109}
110
111impl Auth {
112    /// Returns [`Auth::BasicAuth`] with [`AuthScheme::Falcon512Poseidon2`].
113    ///
114    /// Prefer ECDSA over Falcon for tests where the auth scheme itself is not under test.
115    pub fn basic_falcon() -> Self {
116        Auth::BasicAuth {
117            auth_scheme: AuthScheme::Falcon512Poseidon2,
118        }
119    }
120
121    /// Returns [`Auth::BasicAuth`] with [`AuthScheme::EcdsaK256Keccak`].
122    ///
123    /// ECDSA verifies much faster than Falcon, making it the better choice for tests where the
124    /// auth scheme itself is not under test.
125    pub fn basic_ecdsa() -> Self {
126        Auth::BasicAuth { auth_scheme: AuthScheme::EcdsaK256Keccak }
127    }
128
129    /// Converts `self` into the [`AccountComponent`]s implementing this authentication scheme and
130    /// an optional [`BasicAuthenticator`].
131    ///
132    /// The authentication component is always the first component of the returned vector; variants
133    /// that expand into multiple components (e.g. [`Auth::NetworkAccount`]) yield their companion
134    /// components after it. The authenticator is only `Some` when [`Auth::BasicAuth`] or
135    /// [`Auth::TxFeeCollector`] is passed.
136    pub fn build_components(&self) -> (Vec<AccountComponent>, Option<BasicAuthenticator>) {
137        match self {
138            Auth::BasicAuth { auth_scheme } => {
139                Self::build_single_key_auth(*auth_scheme, |approver| {
140                    AuthSingleSig::new(approver).into()
141                })
142            },
143            Auth::Multisig { approver_set, proc_threshold_map } => {
144                let config = AuthMultisigConfig::new(approver_set.clone())
145                    .with_proc_thresholds(proc_threshold_map.clone())
146                    .expect("invalid multisig config");
147                let component =
148                    AuthMultisig::new(config).expect("multisig component creation failed").into();
149
150                (vec![component], None)
151            },
152            Auth::GuardedMultisig {
153                approver_set,
154                guardian_config,
155                proc_threshold_map,
156            } => {
157                let config = AuthGuardedMultisigConfig::new(approver_set.clone(), *guardian_config)
158                    .and_then(|cfg| cfg.with_proc_thresholds(proc_threshold_map.clone()))
159                    .expect("invalid guarded multisig config");
160                let component = AuthGuardedMultisig::new(config)
161                    .expect("guarded multisig component creation failed")
162                    .into();
163
164                (vec![component], None)
165            },
166            Auth::MultisigSmart { approver_set, proc_policy_map } => {
167                let config = AuthMultisigSmartConfig::new(approver_set.clone())
168                    .with_proc_policies(proc_policy_map.clone())
169                    .expect("invalid multisig smart config");
170
171                let component = AuthMultisigSmart::new(config)
172                    .expect("multisig smart component creation failed")
173                    .into();
174
175                (vec![component], None)
176            },
177            Auth::IncrNonce => (vec![IncrNonceAuthComponent.into()], None),
178            Auth::Noop => (vec![NoopAuthComponent.into()], None),
179            Auth::TxFeeCollector { auth_scheme } => {
180                Self::build_single_key_auth(*auth_scheme, |approver| {
181                    AuthTxFeeCollector::new(approver).into()
182                })
183            },
184            Auth::Conditional => (vec![ConditionalAuthComponent.into()], None),
185            Auth::NetworkAccount {
186                allowed_script_roots,
187                allowed_tx_script_roots,
188                fee_policy_manager,
189                sponsorship_policy,
190            } => {
191                let components = AuthNetworkAccount::new(
192                    allowed_script_roots.clone(),
193                    fee_policy_manager.clone(),
194                )
195                .expect("network account allowlist must be non-empty")
196                .with_allowed_tx_scripts(allowed_tx_script_roots.clone())
197                .with_sponsorship_policy(*sponsorship_policy)
198                .into_iter()
199                .collect();
200                (components, None)
201            },
202        }
203    }
204
205    /// Derives a deterministic key pair for `auth_scheme`, builds the single-key auth component
206    /// for it and returns the component with an authenticator that signs with the key.
207    fn build_single_key_auth(
208        auth_scheme: AuthScheme,
209        build_component: impl FnOnce(Approver) -> AccountComponent,
210    ) -> (Vec<AccountComponent>, Option<BasicAuthenticator>) {
211        let mut rng = ChaCha20Rng::from_seed(Default::default());
212        let sec_key = AuthSecretKey::with_scheme_and_rng(auth_scheme, &mut rng)
213            .expect("failed to create secret key");
214        let pub_key = sec_key.public_key().to_commitment();
215
216        let component = build_component(Approver::new(pub_key, auth_scheme));
217        let authenticator = BasicAuthenticator::new(&[sec_key]);
218
219        (vec![component], Some(authenticator))
220    }
221}
222
223impl IntoIterator for Auth {
224    type Item = AccountComponent;
225    type IntoIter = alloc::vec::IntoIter<AccountComponent>;
226
227    /// Yields the [`AccountComponent`]s implementing this authentication scheme, discarding the
228    /// authenticator. Use [`Auth::build_components`] when the authenticator is needed.
229    fn into_iter(self) -> Self::IntoIter {
230        let (components, _) = self.build_components();
231        components.into_iter()
232    }
233}