1use move_binary_format::{CompiledModule, file_format::SignatureToken};
5use move_bytecode_utils::resolve_struct;
6use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr};
7use serde::{
8 Serialize, Serializer,
9 ser::{SerializeStruct, SerializeStructVariant, SerializeTupleVariant},
10};
11
12use crate::{
13 IOTA_FRAMEWORK_ADDRESS,
14 digests::MoveAuthenticatorDigest,
15 transaction::{CallArg, Command, ProgrammableTransaction},
16 type_input::TypeName,
17};
18
19pub const AUTH_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("auth_context");
20pub const AUTH_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("AuthContext");
21
22#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct AuthContext {
49 auth_digest: MoveAuthenticatorDigest,
51 tx_inputs: Vec<CallArg>,
53 tx_commands: Vec<Command>,
55}
56
57impl AuthContext {
58 pub fn new_from_components(
59 auth_digest: MoveAuthenticatorDigest,
60 ptb: &ProgrammableTransaction,
61 ) -> Self {
62 Self {
63 auth_digest,
64 tx_inputs: ptb.inputs.clone(),
65 tx_commands: ptb.commands.clone(),
66 }
67 }
68
69 pub fn digest(&self) -> &MoveAuthenticatorDigest {
70 &self.auth_digest
71 }
72
73 pub fn tx_inputs(&self) -> &Vec<CallArg> {
74 &self.tx_inputs
75 }
76
77 pub fn tx_commands(&self) -> &Vec<Command> {
78 &self.tx_commands
79 }
80
81 pub fn to_bcs_bytes(&self) -> Vec<u8> {
82 bcs::to_bytes(&self).unwrap()
83 }
84
85 pub fn kind(module: &CompiledModule, token: &SignatureToken) -> AuthContextKind {
88 use SignatureToken as S;
89
90 let (kind, token) = match token {
91 S::MutableReference(token) => (AuthContextKind::Mutable, token),
92 S::Reference(token) => (AuthContextKind::Immutable, token),
93 _ => return AuthContextKind::None,
94 };
95
96 let S::Datatype(idx) = &**token else {
97 return AuthContextKind::None;
98 };
99
100 let (module_addr, module_name, struct_name) = resolve_struct(module, *idx);
101
102 if is_auth_context(module_addr, module_name, struct_name) {
103 kind
104 } else {
105 AuthContextKind::None
106 }
107 }
108}
109
110impl Serialize for AuthContext {
116 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117 where
118 S: Serializer,
119 {
120 let mut state = serializer.serialize_struct("AuthContext", 3)?;
121 state.serialize_field("auth_digest", &self.auth_digest)?;
122 state.serialize_field("tx_inputs", &self.tx_inputs)?;
123
124 struct CommandSer<'a>(&'a Command);
126
127 impl<'a> Serialize for CommandSer<'a> {
128 fn serialize<SC>(&self, serializer: SC) -> Result<SC::Ok, SC::Error>
129 where
130 SC: Serializer,
131 {
132 match self.0 {
133 Command::MoveCall(m) => {
134 let mut s =
135 serializer.serialize_struct_variant("Command", 0, "MoveCall", 5)?;
136 s.serialize_field("package", &m.package)?;
137 s.serialize_field("module", &m.module)?;
138 s.serialize_field("function", &m.function)?;
139 s.serialize_field(
140 "type_arguments",
141 &m.type_arguments
142 .iter()
143 .map(TypeName::from)
144 .collect::<Vec<_>>(),
145 )?;
146 s.serialize_field("arguments", &m.arguments)?;
147 s.end()
148 }
149 Command::TransferObjects(objects, recipient) => {
150 let mut s = serializer.serialize_struct_variant(
151 "Command",
152 1,
153 "TransferObjects",
154 2,
155 )?;
156 s.serialize_field("objects", objects)?;
157 s.serialize_field("recipient", recipient)?;
158 s.end()
159 }
160 Command::SplitCoins(coin, amounts) => {
161 let mut s =
162 serializer.serialize_struct_variant("Command", 2, "SplitCoins", 2)?;
163 s.serialize_field("coin", coin)?;
164 s.serialize_field("amounts", amounts)?;
165 s.end()
166 }
167 Command::MergeCoins(target_coin, source_coins) => {
168 let mut s =
169 serializer.serialize_struct_variant("Command", 3, "MergeCoins", 2)?;
170 s.serialize_field("target_coin", target_coin)?;
171 s.serialize_field("source_coins", source_coins)?;
172 s.end()
173 }
174 Command::Publish(modules, dependencies) => {
175 let mut s =
176 serializer.serialize_struct_variant("Command", 4, "Publish", 2)?;
177 s.serialize_field("modules", modules)?;
178 s.serialize_field("dependencies", dependencies)?;
179 s.end()
180 }
181 Command::MakeMoveVec(type_arg, elements) => {
182 let mut s =
183 serializer.serialize_tuple_variant("Command", 5, "MakeMoveVec", 2)?;
184 s.serialize_field(&type_arg.as_ref().map(TypeName::from))?;
185 s.serialize_field(elements)?;
186 s.end()
187 }
188 Command::Upgrade(modules, dependencies, package, upgrade_ticket) => {
189 let mut s =
190 serializer.serialize_struct_variant("Command", 6, "Upgrade", 4)?;
191 s.serialize_field("modules", modules)?;
192 s.serialize_field("dependencies", dependencies)?;
193 s.serialize_field("package", package)?;
194 s.serialize_field("upgrade_ticket", upgrade_ticket)?;
195 s.end()
196 }
197 }
198 }
199 }
200
201 state.serialize_field(
202 "tx_commands",
203 &self.tx_commands.iter().map(CommandSer).collect::<Vec<_>>(),
204 )?;
205
206 state.end()
207 }
208}
209
210#[derive(PartialEq, Eq, Clone, Copy)]
211pub enum AuthContextKind {
212 None,
214 Mutable,
216 Immutable,
218}
219
220pub fn is_auth_context(
221 module_addr: &AccountAddress,
222 module_name: &IdentStr,
223 struct_name: &IdentStr,
224) -> bool {
225 module_addr == &IOTA_FRAMEWORK_ADDRESS
226 && module_name == AUTH_CONTEXT_MODULE_NAME
227 && struct_name == AUTH_CONTEXT_STRUCT_NAME
228}