Skip to main content

iota_types/
move_authenticator.rs

1// Copyright (c) 2025 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::HashSet;
5
6use iota_protocol_config::ProtocolConfig;
7use iota_sdk_types::{Address, ObjectId, TypeTag, crypto::IntentMessage};
8pub use iota_sdk_types::{MoveAuthenticator, MoveAuthenticatorV1};
9use serde::Serialize;
10
11use crate::{
12    base_types::{ObjectRef, SequenceNumber},
13    digests::ObjectDigest,
14    error::{IotaError, IotaResult, UserInputError, UserInputResult},
15    signature::{AuthenticatorTrait, VerifyParams},
16    transaction::{CallArg, CallArgExt, InputObjectKind, SharedObjectRef},
17};
18
19mod move_authenticator_ext {
20    pub trait Sealed {}
21    impl Sealed for super::MoveAuthenticator {}
22    impl Sealed for super::MoveAuthenticatorV1 {}
23}
24
25pub trait MoveAuthenticatorExt: Sized + move_authenticator_ext::Sealed {
26    /// Returns the address of the object being authenticated, which acts as the
27    /// sender of the transaction.
28    fn address(&self) -> Address;
29
30    /// Returns the input objects or primitive values passed to the authenticate
31    /// function.
32    fn call_args(&self) -> &[CallArg];
33
34    /// Returns the type arguments for the Move authenticate function.
35    fn type_args(&self) -> &[TypeTag];
36
37    /// Returns the object that is being authenticated (the account/sender).
38    fn object_to_authenticate(&self) -> &CallArg;
39
40    fn object_to_authenticate_components(
41        &self,
42    ) -> UserInputResult<(ObjectId, Option<SequenceNumber>, Option<ObjectDigest>)>;
43
44    fn input_objects(&self) -> Vec<InputObjectKind>;
45
46    fn receiving_objects(&self) -> Vec<ObjectRef>;
47
48    fn shared_objects(&self) -> Vec<SharedObjectRef>;
49
50    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
51}
52
53impl MoveAuthenticatorExt for MoveAuthenticator {
54    fn address(&self) -> Address {
55        match self {
56            Self::V1(v1) => v1.address(),
57            _ => unimplemented!(
58                "a new MoveAuthenticator enum variant was added and needs to be handled"
59            ),
60        }
61    }
62
63    fn call_args(&self) -> &[CallArg] {
64        match self {
65            Self::V1(v1) => v1.call_args(),
66            _ => unimplemented!(
67                "a new MoveAuthenticator enum variant was added and needs to be handled"
68            ),
69        }
70    }
71
72    fn type_args(&self) -> &[TypeTag] {
73        match self {
74            Self::V1(v1) => v1.type_args(),
75            _ => unimplemented!(
76                "a new MoveAuthenticator enum variant was added and needs to be handled"
77            ),
78        }
79    }
80
81    fn object_to_authenticate(&self) -> &CallArg {
82        match self {
83            Self::V1(v1) => v1.object_to_authenticate(),
84            _ => unimplemented!(
85                "a new MoveAuthenticator enum variant was added and needs to be handled"
86            ),
87        }
88    }
89
90    fn object_to_authenticate_components(
91        &self,
92    ) -> UserInputResult<(ObjectId, Option<SequenceNumber>, Option<ObjectDigest>)> {
93        match self {
94            Self::V1(v1) => v1.object_to_authenticate_components(),
95            _ => unimplemented!(
96                "a new MoveAuthenticator enum variant was added and needs to be handled"
97            ),
98        }
99    }
100
101    fn input_objects(&self) -> Vec<InputObjectKind> {
102        match self {
103            Self::V1(v1) => v1.input_objects(),
104            _ => unimplemented!(
105                "a new MoveAuthenticator enum variant was added and needs to be handled"
106            ),
107        }
108    }
109
110    fn receiving_objects(&self) -> Vec<ObjectRef> {
111        match self {
112            Self::V1(v1) => v1.receiving_objects(),
113            _ => unimplemented!(
114                "a new MoveAuthenticator enum variant was added and needs to be handled"
115            ),
116        }
117    }
118
119    fn shared_objects(&self) -> Vec<SharedObjectRef> {
120        match self {
121            Self::V1(v1) => v1.shared_objects(),
122            _ => unimplemented!(
123                "a new MoveAuthenticator enum variant was added and needs to be handled"
124            ),
125        }
126    }
127
128    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
129        match self {
130            Self::V1(v1) => v1.validity_check(config),
131            _ => unimplemented!(
132                "a new MoveAuthenticator enum variant was added and needs to be handled"
133            ),
134        }
135    }
136}
137
138impl MoveAuthenticatorExt for MoveAuthenticatorV1 {
139    // Forward to the inherent accessors of the same name; inherent methods take
140    // priority in resolution, so these are not recursive.
141    fn address(&self) -> Address {
142        self.address()
143    }
144
145    fn call_args(&self) -> &[CallArg] {
146        self.call_args()
147    }
148
149    fn type_args(&self) -> &[TypeTag] {
150        self.type_args()
151    }
152
153    fn object_to_authenticate(&self) -> &CallArg {
154        self.object_to_authenticate()
155    }
156
157    fn object_to_authenticate_components(
158        &self,
159    ) -> UserInputResult<(ObjectId, Option<SequenceNumber>, Option<ObjectDigest>)> {
160        Ok(match self.object_to_authenticate() {
161            CallArg::Pure(_) => {
162                return Err(UserInputError::Unsupported(
163                    "MoveAuthenticatorV1 cannot authenticate pure inputs".to_string(),
164                ));
165            }
166            CallArg::ImmutableOrOwned(object_ref) => (
167                object_ref.object_id,
168                Some(object_ref.version),
169                Some(object_ref.digest),
170            ),
171            CallArg::Shared(SharedObjectRef {
172                object_id, mutable, ..
173            }) => {
174                if *mutable {
175                    return Err(UserInputError::Unsupported(
176                        "MoveAuthenticatorV1 cannot authenticate mutable shared objects"
177                            .to_string(),
178                    ));
179                }
180
181                (*object_id, None, None)
182            }
183            CallArg::Receiving(_) => {
184                return Err(UserInputError::Unsupported(
185                    "MoveAuthenticator cannot authenticate receiving objects".to_string(),
186                ));
187            }
188            _ => unimplemented!("a new CallArg enum variant was added and needs to be handled"),
189        })
190    }
191
192    /// Returns all input objects used by the MoveAuthenticatorV1,
193    /// including those from the object to authenticate.
194    fn input_objects(&self) -> Vec<InputObjectKind> {
195        self.call_args()
196            .iter()
197            .filter_map(|arg| arg.input_object_kind())
198            .chain(self.object_to_authenticate().input_object_kind())
199            .collect::<Vec<_>>()
200    }
201
202    fn receiving_objects(&self) -> Vec<ObjectRef> {
203        self.call_args()
204            .iter()
205            .filter_map(|arg| arg.as_opt_receiving().copied())
206            .collect()
207    }
208
209    /// Returns all shared input objects used by the MoveAuthenticatorV1,
210    /// including those from the object to authenticate.
211    fn shared_objects(&self) -> Vec<SharedObjectRef> {
212        self.call_args()
213            .iter()
214            .filter_map(|arg| arg.as_opt_shared())
215            .chain(self.object_to_authenticate().as_opt_shared())
216            .cloned()
217            .collect()
218    }
219
220    /// Validity check for MoveAuthenticatorV1.
221    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
222        // Check that the object to authenticate is valid.
223        self.object_to_authenticate_components()?;
224
225        // Inputs validity check.
226        //
227        // `validity_check` is not called for `object_to_authenticate` because it is
228        // already validated with a dedicated function.
229
230        // `ProtocolConfig::max_function_parameters` is used to check the call arguments
231        // because MoveAuthenticatorV1 is considered as a simple programmable call to a
232        // Move function.
233        //
234        // The limit includes the object to authenticate, the auth context and the tx
235        // context, so we subtract 3 here.
236        let max_args = (config.max_function_parameters() - 3) as usize;
237        fp_ensure!(
238            self.call_args().len() < max_args,
239            UserInputError::SizeLimitExceeded {
240                limit: "maximum arguments in MoveAuthenticatorV1".to_string(),
241                value: max_args.to_string()
242            }
243        );
244
245        fp_ensure!(
246            self.receiving_objects().is_empty(),
247            UserInputError::Unsupported(
248                "MoveAuthenticatorV1 cannot have receiving objects as input".to_string(),
249            )
250        );
251
252        let mut used = HashSet::new();
253        fp_ensure!(
254            self.input_objects()
255                .iter()
256                .all(|o| used.insert(o.object_id())),
257            UserInputError::DuplicateObjectRefInput
258        );
259
260        self.call_args()
261            .iter()
262            .try_for_each(|obj| obj.validity_check(config))?;
263
264        // Type arguments validity check.
265        //
266        // Each type argument is checked for validity in the same way as it is done for
267        // `MoveCall`.
268        let mut type_arguments_count = 0;
269        self.type_args().iter().try_for_each(|type_arg| {
270            crate::transaction::type_tag_validity_check(type_arg, config, &mut type_arguments_count)
271        })?;
272
273        Ok(())
274    }
275}
276
277impl AuthenticatorTrait for MoveAuthenticator {
278    // This function accepts all inputs, as signature verification is performed
279    // later on the Move side.
280    fn verify_claims<T>(
281        &self,
282        value: &IntentMessage<T>,
283        author: Address,
284        aux_verify_data: &VerifyParams,
285    ) -> IotaResult
286    where
287        T: Serialize,
288    {
289        match self {
290            Self::V1(v1) => v1.verify_claims(value, author, aux_verify_data),
291            _ => unimplemented!(
292                "a new MoveAuthenticator enum variant was added and needs to be handled"
293            ),
294        }
295    }
296}
297
298impl AuthenticatorTrait for MoveAuthenticatorV1 {
299    // This function accepts all inputs, as signature verification is performed
300    // later on the Move side.
301    fn verify_claims<T>(
302        &self,
303        _value: &IntentMessage<T>,
304        author: Address,
305        _aux_verify_data: &VerifyParams,
306    ) -> IotaResult
307    where
308        T: Serialize,
309    {
310        if author != self.address() {
311            return Err(IotaError::InvalidSignature {
312                error: "Invalid author".to_string(),
313            });
314        };
315
316        Ok(())
317    }
318}