1mod fields_v1;
5
6pub use fields_v1::*;
7use iota_sdk_types::ProgrammableTransaction;
8use move_binary_format::{CompiledModule, file_format::SignatureToken};
9use move_bytecode_utils::resolve_struct;
10use move_core_types::{
11 account_address::AccountAddress, ident_str, identifier::IdentStr, language_storage::StructTag,
12};
13use serde::Serialize;
14
15use crate::{
16 IOTA_FRAMEWORK_ADDRESS,
17 account_abstraction::authenticator_function::{
18 AuthenticatorFunctionRef, AuthenticatorFunctionRefV1,
19 },
20 digests::{Digest, MoveAuthenticatorDigest},
21};
22
23pub const AUTH_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("auth_context");
24pub const AUTH_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("AuthContext");
25
26#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
52pub struct AuthContext {
53 auth_digest: MoveAuthenticatorDigest,
55 sender_auth_digest: Digest,
59 sponsor_auth_digest: Option<Digest>,
64 sender_authenticator_function_ref_v1: Option<AuthenticatorFunctionRefV1>,
67 sponsor_authenticator_function_ref_v1: Option<AuthenticatorFunctionRefV1>,
70 tx_inputs: Vec<MoveCallArg>,
72 tx_commands: Vec<MoveCommand>,
74 tx_data_bytes: Vec<u8>,
76}
77
78impl AuthContext {
79 pub fn new_from_components(
80 auth_digest: MoveAuthenticatorDigest,
81 sender_auth_digest: Digest,
82 sponsor_auth_digest: Option<Digest>,
83 sender_authenticator_function_ref_v1: Option<AuthenticatorFunctionRefV1>,
84 sponsor_authenticator_function_ref_v1: Option<AuthenticatorFunctionRefV1>,
85 ptb: &ProgrammableTransaction,
86 tx_data_bytes: Vec<u8>,
87 ) -> Self {
88 Self {
89 auth_digest,
90 sender_auth_digest,
91 sponsor_auth_digest,
92 sender_authenticator_function_ref_v1,
93 sponsor_authenticator_function_ref_v1,
94 tx_inputs: ptb.inputs.iter().map(MoveCallArg::from).collect(),
95 tx_commands: ptb.commands.iter().map(MoveCommand::from).collect(),
96 tx_data_bytes,
97 }
98 }
99
100 pub fn new_for_testing() -> Self {
101 Self {
102 auth_digest: MoveAuthenticatorDigest::default(),
103 sender_auth_digest: Digest::default(),
104 sponsor_auth_digest: None,
105 sender_authenticator_function_ref_v1: None,
106 sponsor_authenticator_function_ref_v1: None,
107 tx_inputs: Vec::new(),
108 tx_commands: Vec::new(),
109 tx_data_bytes: Vec::new(),
110 }
111 }
112
113 pub fn digest(&self) -> &MoveAuthenticatorDigest {
115 &self.auth_digest
116 }
117
118 pub fn sender_auth_digest(&self) -> &Digest {
124 &self.sender_auth_digest
125 }
126
127 pub fn sponsor_auth_digest(&self) -> Option<&Digest> {
134 self.sponsor_auth_digest.as_ref()
135 }
136
137 pub fn sender_authenticator_function_ref_v1(&self) -> Option<&AuthenticatorFunctionRefV1> {
140 self.sender_authenticator_function_ref_v1.as_ref()
141 }
142
143 pub fn sponsor_authenticator_function_ref_v1(&self) -> Option<&AuthenticatorFunctionRefV1> {
148 self.sponsor_authenticator_function_ref_v1.as_ref()
149 }
150
151 pub fn tx_inputs(&self) -> &Vec<MoveCallArg> {
152 &self.tx_inputs
153 }
154
155 pub fn tx_commands(&self) -> &Vec<MoveCommand> {
156 &self.tx_commands
157 }
158
159 pub fn tx_data_bytes(&self) -> &Vec<u8> {
160 &self.tx_data_bytes
161 }
162
163 pub fn to_bcs_bytes(&self) -> Vec<u8> {
164 bcs::to_bytes(&self).unwrap()
165 }
166
167 pub fn to_move_bcs_bytes(&self) -> Vec<u8> {
168 bcs::to_bytes(&MoveAuthContext::default()).unwrap()
169 }
170
171 pub fn kind(module: &CompiledModule, token: &SignatureToken) -> AuthContextKind {
174 use SignatureToken as S;
175
176 let (kind, token) = match token {
177 S::MutableReference(token) => (AuthContextKind::Mutable, token),
178 S::Reference(token) => (AuthContextKind::Immutable, token),
179 _ => return AuthContextKind::None,
180 };
181
182 let S::Datatype(idx) = &**token else {
183 return AuthContextKind::None;
184 };
185
186 let (module_addr, module_name, struct_name) = resolve_struct(module, *idx);
187
188 if is_auth_context(module_addr, module_name, struct_name) {
189 kind
190 } else {
191 AuthContextKind::None
192 }
193 }
194
195 pub fn type_() -> StructTag {
196 StructTag {
197 address: IOTA_FRAMEWORK_ADDRESS,
198 module: AUTH_CONTEXT_MODULE_NAME.to_owned(),
199 name: AUTH_CONTEXT_STRUCT_NAME.to_owned(),
200 type_params: vec![],
201 }
202 }
203
204 pub fn replace(
208 &mut self,
209 auth_digest: MoveAuthenticatorDigest,
210 tx_inputs: Vec<MoveCallArg>,
211 tx_commands: Vec<MoveCommand>,
212 tx_data_bytes: Vec<u8>,
213 sender_auth_digest: Digest,
214 sponsor_auth_digest: Option<Digest>,
215 sender_authenticator_function_ref_v1: Option<AuthenticatorFunctionRefV1>,
216 sponsor_authenticator_function_ref_v1: Option<AuthenticatorFunctionRefV1>,
217 ) {
218 self.auth_digest = auth_digest;
219 self.tx_inputs = tx_inputs;
220 self.tx_commands = tx_commands;
221 self.tx_data_bytes = tx_data_bytes;
222 self.sender_auth_digest = sender_auth_digest;
223 self.sponsor_auth_digest = sponsor_auth_digest;
224 self.sender_authenticator_function_ref_v1 = sender_authenticator_function_ref_v1;
225 self.sponsor_authenticator_function_ref_v1 = sponsor_authenticator_function_ref_v1;
226 }
227}
228
229#[derive(Default, Serialize)]
233pub struct MoveAuthContext {
234 auth_digest: MoveAuthenticatorDigest,
235 tx_inputs: Vec<MoveCallArg>,
236 tx_commands: Vec<MoveCommand>,
237}
238
239#[derive(PartialEq, Eq, Clone, Copy)]
240pub enum AuthContextKind {
241 None,
243 Mutable,
245 Immutable,
247}
248
249pub fn is_auth_context(
250 module_addr: &AccountAddress,
251 module_name: &IdentStr,
252 struct_name: &IdentStr,
253) -> bool {
254 module_addr == &IOTA_FRAMEWORK_ADDRESS
255 && module_name == AUTH_CONTEXT_MODULE_NAME
256 && struct_name == AUTH_CONTEXT_STRUCT_NAME
257}
258
259#[derive(Clone, Debug, PartialEq, Eq)]
260pub struct AuthContextData {
261 pub transaction_data_bytes: Vec<u8>,
262 pub sender_auth_digest: Digest,
263 pub sponsor_auth_digest: Option<Digest>,
264 pub sender_authenticator_function_ref: Option<AuthenticatorFunctionRef>,
265 pub sponsor_authenticator_function_ref: Option<AuthenticatorFunctionRef>,
266}
267
268#[cfg(test)]
269mod tests {
270 use iota_sdk_types::{
271 Argument, Command, Identifier, ObjectId, ProgrammableTransaction, TypeTag,
272 };
273
274 use super::*;
275 use crate::transaction::CallArg;
276
277 #[test]
278 fn auth_context_new_from_components() {
279 let auth_digest = MoveAuthenticatorDigest::new([1u8; 32]);
280 let sender_auth_digest = Digest::new([2u8; 32]);
281 let sponsor_auth_digest = Some(Digest::new([3u8; 32]));
282 let tx_data_bytes = vec![0xde, 0xad, 0xbe, 0xef];
283
284 let ptb = ProgrammableTransaction {
285 inputs: vec![CallArg::Pure(vec![0xab])],
286 commands: vec![Command::new_move_call(
287 ObjectId::from_prefixed_short_hex("0x0000000000000000000000000000000000000001")
288 .unwrap(),
289 Identifier::new_unchecked("mod"),
290 Identifier::new_unchecked("fun"),
291 vec![TypeTag::U8],
292 vec![Argument::Gas],
293 )],
294 };
295
296 let sender_auth_fun_ref_v1 = AuthenticatorFunctionRefV1 {
297 package: ObjectId::from([0xAAu8; 32]),
298 module: "sender_mod".to_string(),
299 function: "authenticate".to_string(),
300 };
301 let sponsor_auth_fun_ref_v1 = AuthenticatorFunctionRefV1 {
302 package: ObjectId::from([0xBBu8; 32]),
303 module: "sponsor_mod".to_string(),
304 function: "authenticate".to_string(),
305 };
306
307 let ctx = AuthContext::new_from_components(
308 auth_digest,
309 sender_auth_digest,
310 sponsor_auth_digest,
311 Some(sender_auth_fun_ref_v1.clone()),
312 Some(sponsor_auth_fun_ref_v1.clone()),
313 &ptb,
314 tx_data_bytes.clone(),
315 );
316
317 assert_eq!(ctx.digest(), &auth_digest);
319
320 assert_eq!(ctx.sender_auth_digest(), &sender_auth_digest);
322
323 assert_eq!(ctx.sponsor_auth_digest(), sponsor_auth_digest.as_ref());
325
326 assert_eq!(
328 ctx.sender_authenticator_function_ref_v1(),
329 Some(&sender_auth_fun_ref_v1)
330 );
331
332 assert_eq!(
334 ctx.sponsor_authenticator_function_ref_v1(),
335 Some(&sponsor_auth_fun_ref_v1)
336 );
337
338 assert_eq!(ctx.tx_inputs().len(), 1);
340 assert!(matches!(ctx.tx_inputs()[0], MoveCallArg::Pure(_)));
341
342 assert_eq!(ctx.tx_commands().len(), 1);
344 let MoveCommand::MoveCall(call) = &ctx.tx_commands()[0] else {
345 panic!("expected MoveCall");
346 };
347 assert_eq!(call.type_arguments, vec![TypeTag::U8]);
348
349 assert_eq!(ctx.tx_data_bytes(), &tx_data_bytes);
351 }
352
353 #[test]
354 fn auth_context_to_bcs_bytes_is_deterministic() {
355 let ctx = AuthContext::new_for_testing();
356 assert_eq!(ctx.to_bcs_bytes(), ctx.to_bcs_bytes());
357 }
358
359 #[test]
360 fn auth_context_to_bcs_bytes_reflects_content() {
361 let mut ctx = AuthContext::new_for_testing();
362 let empty_bytes = ctx.to_bcs_bytes();
363
364 ctx.replace(
365 MoveAuthenticatorDigest::default(),
366 vec![MoveCallArg::Pure(vec![1])],
367 vec![],
368 vec![],
369 Digest::default(),
370 None,
371 None,
372 None,
373 );
374 let non_empty_bytes = ctx.to_bcs_bytes();
375
376 assert_ne!(empty_bytes, non_empty_bytes);
377 }
378}