Skip to main content

miden_testing/mock_chain/
auth.rs

1// AUTH
2// ================================================================================================
3use alloc::collections::BTreeSet;
4use alloc::vec::Vec;
5
6use miden_protocol::Word;
7use miden_protocol::account::auth::{AuthScheme, AuthSecretKey};
8use miden_protocol::account::{AccountComponent, AccountProcedureRoot};
9use miden_protocol::note::NoteScriptRoot;
10use miden_protocol::testing::noop_auth_component::NoopAuthComponent;
11use miden_protocol::transaction::TransactionScriptRoot;
12use miden_standards::account::auth::multisig_smart::ProcedurePolicy;
13use miden_standards::account::auth::{
14    Approver,
15    ApproverSet,
16    AuthGuardedMultisig,
17    AuthGuardedMultisigConfig,
18    AuthMultisig,
19    AuthMultisigConfig,
20    AuthMultisigSmart,
21    AuthMultisigSmartConfig,
22    AuthNetworkAccount,
23    AuthSingleSig,
24    AuthSingleSigAcl,
25    AuthSingleSigAclConfig,
26    GuardianConfig,
27};
28use miden_standards::testing::account_component::{
29    ConditionalAuthComponent,
30    IncrNonceAuthComponent,
31};
32use miden_tx::auth::BasicAuthenticator;
33use rand::SeedableRng;
34use rand_chacha::ChaCha20Rng;
35
36/// Specifies which authentication mechanism is desired for accounts
37#[derive(Debug, Clone)]
38pub enum Auth {
39    /// Creates a secret key for the account and creates a [BasicAuthenticator] used to
40    /// authenticate the account with [AuthSingleSig].
41    BasicAuth { auth_scheme: AuthScheme },
42
43    /// Multisig
44    Multisig {
45        approver_set: ApproverSet,
46        proc_threshold_map: Vec<(AccountProcedureRoot, u32)>,
47    },
48
49    /// Guarded multisig.
50    GuardedMultisig {
51        approver_set: ApproverSet,
52        guardian_config: GuardianConfig,
53        proc_threshold_map: Vec<(AccountProcedureRoot, u32)>,
54    },
55
56    /// Multisig with smart per-procedure policy configuration.
57    MultisigSmart {
58        approver_set: ApproverSet,
59        proc_policy_map: Vec<(Word, ProcedurePolicy)>,
60    },
61
62    /// Creates a secret key for the account, and creates a [BasicAuthenticator] used to
63    /// authenticate the account with [AuthSingleSigAcl]. Any called procedure that is not
64    /// in `exempt_procedures` forces signature verification.
65    Acl {
66        exempt_procedures: BTreeSet<AccountProcedureRoot>,
67        auth_scheme: AuthScheme,
68    },
69
70    /// Creates a mock authentication mechanism for the account that only increments the nonce.
71    IncrNonce,
72
73    /// Creates a mock authentication mechanism for the account that does nothing.
74    Noop,
75
76    /// Creates a mock authentication mechanism for the account that conditionally succeeds and
77    /// conditionally increments the nonce based on the authentication arguments.
78    ///
79    /// The auth procedure expects the first three arguments as [99, 98, 97] to succeed.
80    /// In case it succeeds, it conditionally increments the nonce based on the fourth argument.
81    Conditional,
82
83    /// Network-account authentication that restricts the account to consuming only notes whose
84    /// script roots appear in `allowed_script_roots` (must be non-empty), and to executing only
85    /// transaction scripts whose roots appear in `allowed_tx_script_roots` (may be empty).
86    NetworkAccount {
87        allowed_script_roots: BTreeSet<NoteScriptRoot>,
88        allowed_tx_script_roots: BTreeSet<TransactionScriptRoot>,
89    },
90}
91
92impl Default for Auth {
93    /// Returns the most common authentication scheme used in tests:
94    /// [`Auth::BasicAuth`] with [`AuthScheme::Falcon512Poseidon2`].
95    fn default() -> Self {
96        Auth::BasicAuth {
97            auth_scheme: AuthScheme::Falcon512Poseidon2,
98        }
99    }
100}
101
102impl Auth {
103    /// Returns [`Auth::BasicAuth`] with [`AuthScheme::EcdsaK256Keccak`].
104    ///
105    /// ECDSA verifies much faster than Falcon, making it the better choice for tests where the
106    /// auth scheme itself is not under test.
107    pub fn basic_ecdsa() -> Self {
108        Auth::BasicAuth { auth_scheme: AuthScheme::EcdsaK256Keccak }
109    }
110
111    /// Converts `self` into its corresponding authentication [`AccountComponent`] and an optional
112    /// [`BasicAuthenticator`]. The component is always returned, but the authenticator is only
113    /// `Some` when [`Auth::BasicAuth`] is passed."
114    pub fn build_component(&self) -> (AccountComponent, Option<BasicAuthenticator>) {
115        match self {
116            Auth::BasicAuth { auth_scheme } => {
117                let mut rng = ChaCha20Rng::from_seed(Default::default());
118                let sec_key = AuthSecretKey::with_scheme_and_rng(*auth_scheme, &mut rng)
119                    .expect("failed to create secret key");
120                let pub_key = sec_key.public_key().to_commitment();
121
122                let component = AuthSingleSig::new(Approver::new(pub_key, *auth_scheme)).into();
123                let authenticator = BasicAuthenticator::new(&[sec_key]);
124
125                (component, Some(authenticator))
126            },
127            Auth::Multisig { approver_set, proc_threshold_map } => {
128                let config = AuthMultisigConfig::new(approver_set.clone())
129                    .with_proc_thresholds(proc_threshold_map.clone())
130                    .expect("invalid multisig config");
131                let component =
132                    AuthMultisig::new(config).expect("multisig component creation failed").into();
133
134                (component, None)
135            },
136            Auth::GuardedMultisig {
137                approver_set,
138                guardian_config,
139                proc_threshold_map,
140            } => {
141                let config = AuthGuardedMultisigConfig::new(approver_set.clone(), *guardian_config)
142                    .and_then(|cfg| cfg.with_proc_thresholds(proc_threshold_map.clone()))
143                    .expect("invalid guarded multisig config");
144                let component = AuthGuardedMultisig::new(config)
145                    .expect("guarded multisig component creation failed")
146                    .into();
147
148                (component, None)
149            },
150            Auth::MultisigSmart { approver_set, proc_policy_map } => {
151                let config = AuthMultisigSmartConfig::new(approver_set.clone())
152                    .with_proc_policies(proc_policy_map.clone())
153                    .expect("invalid multisig smart config");
154
155                let component = AuthMultisigSmart::new(config)
156                    .expect("multisig smart component creation failed")
157                    .into();
158
159                (component, None)
160            },
161            Auth::Acl { exempt_procedures, auth_scheme } => {
162                let mut rng = ChaCha20Rng::from_seed(Default::default());
163                let sec_key = AuthSecretKey::with_scheme_and_rng(*auth_scheme, &mut rng)
164                    .expect("failed to create secret key");
165                let pub_key = sec_key.public_key().to_commitment();
166
167                let component = AuthSingleSigAcl::new(
168                    Approver::new(pub_key, *auth_scheme),
169                    AuthSingleSigAclConfig::new(exempt_procedures.clone()).expect(
170                        "AuthSingleSigAcl component creation failed: too many exempt procedures",
171                    ),
172                )
173                .into();
174                let authenticator = BasicAuthenticator::new(&[sec_key]);
175
176                (component, Some(authenticator))
177            },
178            Auth::IncrNonce => (IncrNonceAuthComponent.into(), None),
179            Auth::Noop => (NoopAuthComponent.into(), None),
180            Auth::Conditional => (ConditionalAuthComponent.into(), None),
181            Auth::NetworkAccount {
182                allowed_script_roots,
183                allowed_tx_script_roots,
184            } => {
185                let component =
186                    AuthNetworkAccount::with_allowed_notes(allowed_script_roots.clone())
187                        .expect("network account allowlist must be non-empty")
188                        .with_allowed_tx_scripts(allowed_tx_script_roots.clone())
189                        .into();
190                (component, None)
191            },
192        }
193    }
194}
195
196impl From<Auth> for AccountComponent {
197    fn from(auth: Auth) -> Self {
198        let (component, _) = auth.build_component();
199        component
200    }
201}