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