Skip to main content

thruput_sdk/
receive_auth_handles.rs

1use crate::{AuthTokenError, FaultString};
2
3pub fn receive_auth_handles_from_iter<T: ReceiveAuthHandles>(
4    value: &mut T,
5    handles: impl IntoIterator<Item = u32>,
6) -> Result<(), AuthTokenError> {
7    let mut iter = handles.into_iter();
8    value.receive_auth_handles(&mut iter)?;
9
10    if iter.next().is_some() {
11        Err(AuthTokenError::ReceiveAuthHandlesFailure(
12            FaultString::from_display("unexpected auth handle remaining"),
13        ))
14    } else {
15        Ok(())
16    }
17}
18
19/// Re-attach auth-token handles to every [`Signed`](crate::signer::Signed)
20/// value nested in `self`, drawing them from `handles` in **declaration
21/// order**. `Signed` decodes from zero XDR bytes with a sentinel handle;
22/// this trait fills in the real handles delivered out-of-band.
23///
24/// This is the read half of an order-matching pair with
25/// [`CollectAuthHandles`](crate::collect_auth_handles::CollectAuthHandles):
26/// implementations of the two traits for a type MUST visit `Signed` fields
27/// in the identical order — a mismatch silently assigns auth tokens to the
28/// wrong fields. Derived implementations (via `#[contract_type]`) visit
29/// struct fields in declaration order.
30///
31/// Errors if `handles` runs out before every `Signed` field is filled.
32/// Callers that own the full handle list (the entrypoint path via
33/// [`receive_auth_handles_from_iter`], and
34/// [`Signed::consume`](crate::signer::Signed::consume)) additionally reject
35/// leftover handles after the walk.
36pub trait ReceiveAuthHandles {
37    fn receive_auth_handles(
38        &mut self,
39        handles: &mut impl Iterator<Item = u32>,
40    ) -> Result<(), AuthTokenError>;
41}
42
43macro_rules! impl_receive_noop {
44    ($($ty:ty),+ $(,)?) => {
45        $(
46            impl ReceiveAuthHandles for $ty {
47                #[inline(always)]
48                fn receive_auth_handles(&mut self, _handles: &mut impl Iterator<Item = u32>) -> Result<(), AuthTokenError> {
49                    Ok(())
50                }
51            }
52        )+
53    };
54}
55
56impl_receive_noop!(
57    u8,
58    u16,
59    u32,
60    u64,
61    u128,
62    i8,
63    i16,
64    i32,
65    i64,
66    i128,
67    bool,
68    (),
69    str,
70    crate::types::ContractAddress,
71    crate::types::BlockHeight,
72    crate::types::SHA256Hash,
73    crate::types::Ed25519PublicKey,
74    crate::types::Ed25519Signature,
75    crate::types::MlDsa65PublicKey,
76    crate::types::MlDsa65Signature,
77    crate::types::PublicKey,
78    crate::types::Signature,
79    thruput_u256::U256,
80    thruput_u256::I256,
81    crate::types::ContractEntrypointID,
82);
83
84#[cfg(feature = "alloc")]
85impl_receive_noop!(alloc::string::String);
86
87impl<T: ReceiveAuthHandles, const N: usize> ReceiveAuthHandles for [T; N] {
88    #[inline(always)]
89    fn receive_auth_handles(
90        &mut self,
91        handles: &mut impl Iterator<Item = u32>,
92    ) -> Result<(), AuthTokenError> {
93        for item in self {
94            item.receive_auth_handles(handles)?;
95        }
96        Ok(())
97    }
98}
99
100impl<T: ReceiveAuthHandles + ?Sized> ReceiveAuthHandles for &mut T {
101    #[inline(always)]
102    fn receive_auth_handles(
103        &mut self,
104        handles: &mut impl Iterator<Item = u32>,
105    ) -> Result<(), AuthTokenError> {
106        (**self).receive_auth_handles(handles)
107    }
108}
109
110impl<T: ReceiveAuthHandles> ReceiveAuthHandles for Option<T> {
111    #[inline(always)]
112    fn receive_auth_handles(
113        &mut self,
114        handles: &mut impl Iterator<Item = u32>,
115    ) -> Result<(), AuthTokenError> {
116        if let Some(inner) = self {
117            return inner.receive_auth_handles(handles);
118        }
119        Ok(())
120    }
121}
122
123impl<T: ReceiveAuthHandles> ReceiveAuthHandles for [T] {
124    #[inline(always)]
125    fn receive_auth_handles(
126        &mut self,
127        handles: &mut impl Iterator<Item = u32>,
128    ) -> Result<(), AuthTokenError> {
129        for item in self {
130            item.receive_auth_handles(handles)?;
131        }
132        Ok(())
133    }
134}
135
136#[cfg(feature = "alloc")]
137impl<T: ReceiveAuthHandles, const MAX: u32> ReceiveAuthHandles
138    for xdrs::VecM<T, MAX>
139{
140    #[inline(always)]
141    fn receive_auth_handles(
142        &mut self,
143        handles: &mut impl Iterator<Item = u32>,
144    ) -> Result<(), AuthTokenError> {
145        for item in self.0.iter_mut() {
146            item.receive_auth_handles(handles)?;
147        }
148        Ok(())
149    }
150}
151
152impl<const MAX: u32> ReceiveAuthHandles for xdrs::BytesM<MAX> {
153    #[inline(always)]
154    fn receive_auth_handles(
155        &mut self,
156        _handles: &mut impl Iterator<Item = u32>,
157    ) -> Result<(), AuthTokenError> {
158        Ok(())
159    }
160}
161
162impl<const MAX: usize, T: ReceiveAuthHandles> ReceiveAuthHandles
163    for heapless::Vec<T, MAX>
164{
165    #[inline(always)]
166    fn receive_auth_handles(
167        &mut self,
168        handles: &mut impl Iterator<Item = u32>,
169    ) -> Result<(), AuthTokenError> {
170        for item in self {
171            item.receive_auth_handles(handles)?;
172        }
173        Ok(())
174    }
175}
176
177impl<const MAX: usize> ReceiveAuthHandles for heapless::String<MAX> {
178    #[inline(always)]
179    fn receive_auth_handles(
180        &mut self,
181        _handles: &mut impl Iterator<Item = u32>,
182    ) -> Result<(), AuthTokenError> {
183        Ok(())
184    }
185}
186
187#[cfg(feature = "alloc")]
188impl<T: ReceiveAuthHandles + ?Sized> ReceiveAuthHandles
189    for alloc::boxed::Box<T>
190{
191    #[inline(always)]
192    fn receive_auth_handles(
193        &mut self,
194        handles: &mut impl Iterator<Item = u32>,
195    ) -> Result<(), AuthTokenError> {
196        (**self).receive_auth_handles(handles)
197    }
198}
199
200impl<T: ReceiveAuthHandles + ?Sized> ReceiveAuthHandles for &T {
201    #[inline(always)]
202    fn receive_auth_handles(
203        &mut self,
204        _handles: &mut impl Iterator<Item = u32>,
205    ) -> Result<(), AuthTokenError> {
206        Ok(())
207    }
208}
209
210#[cfg(feature = "alloc")]
211impl<T: ReceiveAuthHandles> ReceiveAuthHandles for alloc::vec::Vec<T> {
212    #[inline(always)]
213    fn receive_auth_handles(
214        &mut self,
215        handles: &mut impl Iterator<Item = u32>,
216    ) -> Result<(), AuthTokenError> {
217        self.as_mut_slice().receive_auth_handles(handles)
218    }
219}
220
221macro_rules! impl_receive_tuple {
222    ($(($($name:ident),+)),+ $(,)?) => {
223        $(
224            impl<$($name: ReceiveAuthHandles),+> ReceiveAuthHandles for ($($name,)+) {
225                #[inline(always)]
226                #[allow(non_snake_case)]
227                fn receive_auth_handles(&mut self, handles: &mut impl Iterator<Item = u32>) -> Result<(), AuthTokenError> {
228                    let ($($name,)+) = self;
229                    $($name.receive_auth_handles(handles)?;)+
230                    Ok(())
231                }
232            }
233        )+
234    };
235}
236
237impl_receive_tuple!(
238    (A),
239    (A, B),
240    (A, B, C),
241    (A, B, C, D),
242    (A, B, C, D, E),
243    (A, B, C, D, E, F),
244    (A, B, C, D, E, F, G),
245    (A, B, C, D, E, F, G, H),
246    (A, B, C, D, E, F, G, H, I),
247    (A, B, C, D, E, F, G, H, I, J),
248    (A, B, C, D, E, F, G, H, I, J, K),
249    (A, B, C, D, E, F, G, H, I, J, K, L),
250    (A, B, C, D, E, F, G, H, I, J, K, L, M),
251    (A, B, C, D, E, F, G, H, I, J, K, L, M, N),
252    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O),
253    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P),
254    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q),
255    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R),
256    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S),
257    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T),
258);
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263    use crate::signer::Signed;
264
265    #[test]
266    fn primitives_receive_nothing() {
267        let mut x = 42u64;
268        let mut handles = core::iter::empty();
269        x.receive_auth_handles(&mut handles).unwrap();
270    }
271
272    #[test]
273    fn signer_receives_handle() {
274        let mut s: Signed<u32> = Signed::from_handle(u32::MAX);
275        let mut iter = [99u32].into_iter();
276        s.receive_auth_handles(&mut iter).unwrap();
277        assert_eq!(s.handle(), 99);
278    }
279
280    #[test]
281    fn option_some_receives() {
282        let mut s: Option<Signed<u32>> = Some(Signed::from_handle(u32::MAX));
283        let mut iter = [5u32].into_iter();
284        s.receive_auth_handles(&mut iter).unwrap();
285        assert_eq!(s.unwrap().handle(), 5);
286    }
287
288    #[test]
289    fn option_none_skips() {
290        let mut s: Option<Signed<u32>> = None;
291        let mut iter = [5u32].into_iter();
292        s.receive_auth_handles(&mut iter).unwrap();
293        // handle 5 was NOT consumed
294        assert_eq!(iter.next(), Some(5));
295    }
296
297    #[test]
298    fn mixed_tuple_receives_in_order() {
299        let mut args: (u64, Signed<u32>, i32, Signed<bool>) = (
300            100,
301            Signed::from_handle(u32::MAX),
302            -1,
303            Signed::from_handle(u32::MAX),
304        );
305        let mut iter = [10u32, 20u32].into_iter();
306        args.receive_auth_handles(&mut iter).unwrap();
307        assert_eq!(args.1.handle(), 10);
308        assert_eq!(args.3.handle(), 20);
309    }
310
311    #[cfg(feature = "alloc")]
312    #[test]
313    fn owned_strings_preserve_signed_handle_order() {
314        use crate::{AuthHandleBuf, CollectAuthHandles};
315
316        let mut args = (
317            alloc::string::String::from("before 🦀"),
318            Signed::<u32>::from_handle(7),
319            Some(alloc::string::String::from("between")),
320            Signed::<bool>::from_handle(9),
321        );
322        let mut collected = AuthHandleBuf::new();
323        args.collect_auth_handles(&mut collected).unwrap();
324        assert_eq!(collected.as_slice(), &[7, 9]);
325
326        let mut received = [11, 13].into_iter();
327        args.receive_auth_handles(&mut received).unwrap();
328        assert_eq!(args.0, "before 🦀");
329        assert_eq!(args.1.handle(), 11);
330        assert_eq!(args.2.as_deref(), Some("between"));
331        assert_eq!(args.3.handle(), 13);
332        assert_eq!(received.next(), None);
333    }
334
335    #[test]
336    fn array_receives_handles_in_order() {
337        let mut arr: [Signed<u32>; 2] =
338            [Signed::from_handle(u32::MAX), Signed::from_handle(u32::MAX)];
339        let mut iter = [7u32, 8u32].into_iter();
340        arr.receive_auth_handles(&mut iter).unwrap();
341        assert_eq!(arr[0].handle(), 7);
342        assert_eq!(arr[1].handle(), 8);
343        assert_eq!(iter.next(), None);
344    }
345
346    #[test]
347    fn struct_field_after_array_gets_correct_handle() {
348        // Regression: the old [T; N] impl was a no-op, so a Signed field
349        // declared after an array field received the array's handles.
350        let mut args: ([Signed<u32>; 2], Signed<bool>) = (
351            [Signed::from_handle(u32::MAX), Signed::from_handle(u32::MAX)],
352            Signed::from_handle(u32::MAX),
353        );
354        let mut iter = [1u32, 2u32, 3u32].into_iter();
355        args.receive_auth_handles(&mut iter).unwrap();
356        assert_eq!(args.0[0].handle(), 1);
357        assert_eq!(args.0[1].handle(), 2);
358        assert_eq!(args.1.handle(), 3);
359    }
360
361    #[test]
362    fn receive_insufficient_handles() {
363        let mut args: (u64, Signed<u32>, i32, Signed<bool>) = (
364            100,
365            Signed::from_handle(u32::MAX),
366            -1,
367            Signed::from_handle(u32::MAX),
368        );
369        let mut iter = [10u32].into_iter();
370        args.receive_auth_handles(&mut iter).unwrap_err();
371    }
372}