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, digests::MoveAuthenticatorDigest, transaction::ProgrammableTransaction,
16};
17
18pub const AUTH_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("auth_context");
19pub const AUTH_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("AuthContext");
20
21#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
47pub struct AuthContext {
48 auth_digest: MoveAuthenticatorDigest,
50 tx_inputs: Vec<MoveCallArg>,
52 tx_commands: Vec<MoveCommand>,
54 tx_data_bytes: Vec<u8>,
56}
57
58impl AuthContext {
59 pub fn new_from_components(
60 auth_digest: MoveAuthenticatorDigest,
61 ptb: &ProgrammableTransaction,
62 tx_data_bytes: Vec<u8>,
63 ) -> Self {
64 Self {
65 auth_digest,
66 tx_inputs: ptb.inputs.iter().map(MoveCallArg::from).collect(),
67 tx_commands: ptb.commands.iter().map(MoveCommand::from).collect(),
68 tx_data_bytes,
69 }
70 }
71
72 pub fn new_for_testing() -> Self {
73 Self {
74 auth_digest: MoveAuthenticatorDigest::default(),
75 tx_inputs: Vec::new(),
76 tx_commands: Vec::new(),
77 tx_data_bytes: Vec::new(),
78 }
79 }
80
81 pub fn digest(&self) -> &MoveAuthenticatorDigest {
82 &self.auth_digest
83 }
84
85 pub fn tx_inputs(&self) -> &Vec<MoveCallArg> {
86 &self.tx_inputs
87 }
88
89 pub fn tx_commands(&self) -> &Vec<MoveCommand> {
90 &self.tx_commands
91 }
92
93 pub fn tx_data_bytes(&self) -> &Vec<u8> {
94 &self.tx_data_bytes
95 }
96
97 pub fn to_bcs_bytes(&self) -> Vec<u8> {
98 bcs::to_bytes(&self).unwrap()
99 }
100
101 pub fn to_move_bcs_bytes(&self) -> Vec<u8> {
102 bcs::to_bytes(&MoveAuthContext::default()).unwrap()
103 }
104
105 pub fn kind(module: &CompiledModule, token: &SignatureToken) -> AuthContextKind {
108 use SignatureToken as S;
109
110 let (kind, token) = match token {
111 S::MutableReference(token) => (AuthContextKind::Mutable, token),
112 S::Reference(token) => (AuthContextKind::Immutable, token),
113 _ => return AuthContextKind::None,
114 };
115
116 let S::Datatype(idx) = &**token else {
117 return AuthContextKind::None;
118 };
119
120 let (module_addr, module_name, struct_name) = resolve_struct(module, *idx);
121
122 if is_auth_context(module_addr, module_name, struct_name) {
123 kind
124 } else {
125 AuthContextKind::None
126 }
127 }
128
129 pub fn type_() -> StructTag {
130 StructTag {
131 address: IOTA_FRAMEWORK_ADDRESS,
132 module: AUTH_CONTEXT_MODULE_NAME.to_owned(),
133 name: AUTH_CONTEXT_STRUCT_NAME.to_owned(),
134 type_params: vec![],
135 }
136 }
137
138 pub fn replace(
142 &mut self,
143 auth_digest: MoveAuthenticatorDigest,
144 tx_inputs: Vec<MoveCallArg>,
145 tx_commands: Vec<MoveCommand>,
146 tx_data_bytes: Vec<u8>,
147 ) {
148 self.auth_digest = auth_digest;
149 self.tx_inputs = tx_inputs;
150 self.tx_commands = tx_commands;
151 self.tx_data_bytes = tx_data_bytes;
152 }
153}
154
155#[derive(Default, Serialize)]
159pub struct MoveAuthContext {
160 auth_digest: MoveAuthenticatorDigest,
161 tx_inputs: Vec<MoveCallArg>,
162 tx_commands: Vec<MoveCommand>,
163}
164
165#[derive(PartialEq, Eq, Clone, Copy)]
166pub enum AuthContextKind {
167 None,
169 Mutable,
171 Immutable,
173}
174
175pub fn is_auth_context(
176 module_addr: &AccountAddress,
177 module_name: &IdentStr,
178 struct_name: &IdentStr,
179) -> bool {
180 module_addr == &IOTA_FRAMEWORK_ADDRESS
181 && module_name == AUTH_CONTEXT_MODULE_NAME
182 && struct_name == AUTH_CONTEXT_STRUCT_NAME
183}
184
185#[cfg(test)]
186mod tests {
187
188 use super::*;
189 use crate::{
190 base_types::ObjectID,
191 transaction::{Argument, CallArg, Command, ProgrammableMoveCall, ProgrammableTransaction},
192 type_input::{TypeInput, TypeName},
193 };
194
195 #[test]
196 fn auth_context_new_from_components() {
197 let ptb = ProgrammableTransaction {
198 inputs: vec![CallArg::Pure(vec![0xab])],
199 commands: vec![Command::MoveCall(Box::new(ProgrammableMoveCall {
200 package: ObjectID::from_hex_literal("0x0000000000000000000000000000000000000001")
201 .unwrap(),
202 module: "mod".to_string(),
203 function: "fun".to_string(),
204 type_arguments: vec![TypeInput::U8],
205 arguments: vec![Argument::GasCoin],
206 }))],
207 };
208
209 let ctx =
210 AuthContext::new_from_components(MoveAuthenticatorDigest::default(), &ptb, vec![]);
211
212 assert_eq!(ctx.tx_inputs().len(), 1);
213 assert_eq!(ctx.tx_commands().len(), 1);
214
215 assert!(matches!(ctx.tx_inputs()[0], MoveCallArg::Pure(_)));
216
217 let MoveCommand::MoveCall(call) = &ctx.tx_commands()[0] else {
219 panic!("expected MoveCall");
220 };
221 assert_eq!(
222 call.type_arguments,
223 vec![TypeName {
224 name: "u8".to_string()
225 }]
226 );
227 }
228
229 #[test]
230 fn auth_context_to_bcs_bytes_is_deterministic() {
231 let ctx = AuthContext::new_for_testing();
232 assert_eq!(ctx.to_bcs_bytes(), ctx.to_bcs_bytes());
233 }
234
235 #[test]
236 fn auth_context_to_bcs_bytes_reflects_content() {
237 let mut ctx = AuthContext::new_for_testing();
238 let empty_bytes = ctx.to_bcs_bytes();
239
240 ctx.replace(
241 MoveAuthenticatorDigest::default(),
242 vec![MoveCallArg::Pure(vec![1])],
243 vec![],
244 vec![],
245 );
246 let non_empty_bytes = ctx.to_bcs_bytes();
247
248 assert_ne!(empty_bytes, non_empty_bytes);
249 }
250}