1mod fields_v1;
5
6pub use fields_v1::*;
7use move_binary_format::{CompiledModule, file_format::SignatureToken};
8use move_bytecode_utils::resolve_struct;
9use move_core_types::{
10 account_address::AccountAddress, ident_str, identifier::IdentStr, language_storage::StructTag,
11};
12use serde::Serialize;
13
14use crate::{
15 IOTA_FRAMEWORK_ADDRESS,
16 account_abstraction::authenticator_function::{
17 AuthenticatorFunctionRef, AuthenticatorFunctionRefV1,
18 },
19 digests::{Digest, MoveAuthenticatorDigest},
20 transaction::ProgrammableTransaction,
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::{Argument, Command, Identifier, ObjectId, TypeTag};
271
272 use super::*;
273 use crate::transaction::{CallArg, ProgrammableTransaction};
274
275 #[test]
276 fn auth_context_new_from_components() {
277 let auth_digest = MoveAuthenticatorDigest::new([1u8; 32]);
278 let sender_auth_digest = Digest::new([2u8; 32]);
279 let sponsor_auth_digest = Some(Digest::new([3u8; 32]));
280 let tx_data_bytes = vec![0xde, 0xad, 0xbe, 0xef];
281
282 let ptb = ProgrammableTransaction {
283 inputs: vec![CallArg::Pure(vec![0xab])],
284 commands: vec![Command::new_move_call(
285 ObjectId::from_prefixed_short_hex("0x0000000000000000000000000000000000000001")
286 .unwrap(),
287 Identifier::new_unchecked("mod"),
288 Identifier::new_unchecked("fun"),
289 vec![TypeTag::U8],
290 vec![Argument::Gas],
291 )],
292 };
293
294 let sender_auth_fun_ref_v1 = AuthenticatorFunctionRefV1 {
295 package: ObjectId::from([0xAAu8; 32]),
296 module: "sender_mod".to_string(),
297 function: "authenticate".to_string(),
298 };
299 let sponsor_auth_fun_ref_v1 = AuthenticatorFunctionRefV1 {
300 package: ObjectId::from([0xBBu8; 32]),
301 module: "sponsor_mod".to_string(),
302 function: "authenticate".to_string(),
303 };
304
305 let ctx = AuthContext::new_from_components(
306 auth_digest,
307 sender_auth_digest,
308 sponsor_auth_digest,
309 Some(sender_auth_fun_ref_v1.clone()),
310 Some(sponsor_auth_fun_ref_v1.clone()),
311 &ptb,
312 tx_data_bytes.clone(),
313 );
314
315 assert_eq!(ctx.digest(), &auth_digest);
317
318 assert_eq!(ctx.sender_auth_digest(), &sender_auth_digest);
320
321 assert_eq!(ctx.sponsor_auth_digest(), sponsor_auth_digest.as_ref());
323
324 assert_eq!(
326 ctx.sender_authenticator_function_ref_v1(),
327 Some(&sender_auth_fun_ref_v1)
328 );
329
330 assert_eq!(
332 ctx.sponsor_authenticator_function_ref_v1(),
333 Some(&sponsor_auth_fun_ref_v1)
334 );
335
336 assert_eq!(ctx.tx_inputs().len(), 1);
338 assert!(matches!(ctx.tx_inputs()[0], MoveCallArg::Pure(_)));
339
340 assert_eq!(ctx.tx_commands().len(), 1);
342 let MoveCommand::MoveCall(call) = &ctx.tx_commands()[0] else {
343 panic!("expected MoveCall");
344 };
345 assert_eq!(call.type_arguments, vec![TypeTag::U8]);
346
347 assert_eq!(ctx.tx_data_bytes(), &tx_data_bytes);
349 }
350
351 #[test]
352 fn auth_context_to_bcs_bytes_is_deterministic() {
353 let ctx = AuthContext::new_for_testing();
354 assert_eq!(ctx.to_bcs_bytes(), ctx.to_bcs_bytes());
355 }
356
357 #[test]
358 fn auth_context_to_bcs_bytes_reflects_content() {
359 let mut ctx = AuthContext::new_for_testing();
360 let empty_bytes = ctx.to_bcs_bytes();
361
362 ctx.replace(
363 MoveAuthenticatorDigest::default(),
364 vec![MoveCallArg::Pure(vec![1])],
365 vec![],
366 vec![],
367 Digest::default(),
368 None,
369 None,
370 None,
371 );
372 let non_empty_bytes = ctx.to_bcs_bytes();
373
374 assert_ne!(empty_bytes, non_empty_bytes);
375 }
376}