Skip to main content

thruput_sdk/
collect_auth_handles.rs

1use crate::FaultString;
2
3#[cfg(not(feature = "alloc"))]
4const AUTH_HANDLES_MAX: usize = 64;
5#[cfg(not(feature = "alloc"))]
6pub type AuthHandleBuf = heapless::Vec<u32, AUTH_HANDLES_MAX>;
7#[cfg(feature = "alloc")]
8pub type AuthHandleBuf = alloc::vec::Vec<u32>;
9
10/// Extract the auth-token handles of every [`Signed`](crate::signer::Signed)
11/// value nested in `self`, in **declaration order**, so they can travel
12/// alongside the XDR payload of a contract call (`Signed` encodes to zero
13/// bytes; the handles are passed out-of-band).
14///
15/// This is the write half of an order-matching pair with
16/// [`ReceiveAuthHandles`](crate::receive_auth_handles::ReceiveAuthHandles):
17/// the callee re-attaches handles to its decoded `Signed` fields by walking
18/// the same structure in the same order. Implementations of the two traits
19/// for a type MUST visit `Signed` fields in the identical order — a mismatch
20/// silently assigns auth tokens to the wrong fields. Derived implementations
21/// (via `#[contract_type]`) visit struct fields in declaration order.
22pub trait CollectAuthHandles {
23    fn collect_auth_handles(
24        &self,
25        out: &mut AuthHandleBuf,
26    ) -> Result<(), FaultString>;
27}
28
29macro_rules! impl_collect_noop {
30    ($($ty:ty),+ $(,)?) => {
31        $(
32            impl CollectAuthHandles for $ty {
33                #[inline(always)]
34                fn collect_auth_handles(&self, _out: &mut AuthHandleBuf) -> Result<(), FaultString> {
35                    Ok(())
36                }
37            }
38        )+
39    };
40}
41
42impl_collect_noop!(
43    u8,
44    u16,
45    u32,
46    u64,
47    u128,
48    i8,
49    i16,
50    i32,
51    i64,
52    i128,
53    bool,
54    (),
55    str,
56    crate::types::ContractAddress,
57    crate::types::BlockHeight,
58    crate::types::SHA256Hash,
59    crate::types::Ed25519PublicKey,
60    crate::types::Ed25519Signature,
61    crate::types::MlDsa65PublicKey,
62    crate::types::MlDsa65Signature,
63    crate::types::PublicKey,
64    crate::types::Signature,
65    thruput_u256::U256,
66    thruput_u256::I256,
67    crate::types::ContractEntrypointID,
68);
69
70#[cfg(feature = "alloc")]
71impl_collect_noop!(alloc::string::String);
72
73impl<T: CollectAuthHandles, const N: usize> CollectAuthHandles for [T; N] {
74    #[inline(always)]
75    fn collect_auth_handles(
76        &self,
77        out: &mut AuthHandleBuf,
78    ) -> Result<(), FaultString> {
79        for item in self {
80            item.collect_auth_handles(out)?;
81        }
82        Ok(())
83    }
84}
85
86impl<T: CollectAuthHandles + ?Sized> CollectAuthHandles for &T {
87    #[inline(always)]
88    fn collect_auth_handles(
89        &self,
90        out: &mut AuthHandleBuf,
91    ) -> Result<(), FaultString> {
92        (**self).collect_auth_handles(out)
93    }
94}
95
96impl<T: CollectAuthHandles> CollectAuthHandles for Option<T> {
97    #[inline(always)]
98    fn collect_auth_handles(
99        &self,
100        out: &mut AuthHandleBuf,
101    ) -> Result<(), FaultString> {
102        if let Some(inner) = self {
103            inner.collect_auth_handles(out)?;
104        }
105        Ok(())
106    }
107}
108
109impl<T: CollectAuthHandles> CollectAuthHandles for [T] {
110    #[inline(always)]
111    fn collect_auth_handles(
112        &self,
113        out: &mut AuthHandleBuf,
114    ) -> Result<(), FaultString> {
115        for item in self {
116            item.collect_auth_handles(out)?;
117        }
118        Ok(())
119    }
120}
121
122#[cfg(feature = "alloc")]
123impl<T: CollectAuthHandles, const MAX: u32> CollectAuthHandles
124    for xdrs::VecM<T, MAX>
125{
126    #[inline(always)]
127    fn collect_auth_handles(
128        &self,
129        out: &mut AuthHandleBuf,
130    ) -> Result<(), FaultString> {
131        self.0.as_slice().collect_auth_handles(out)
132    }
133}
134
135impl<const MAX: u32> CollectAuthHandles for xdrs::BytesM<MAX> {
136    #[inline(always)]
137    fn collect_auth_handles(
138        &self,
139        _out: &mut AuthHandleBuf,
140    ) -> Result<(), FaultString> {
141        Ok(())
142    }
143}
144
145impl<const MAX: usize, T: CollectAuthHandles> CollectAuthHandles
146    for heapless::Vec<T, MAX>
147{
148    #[inline(always)]
149    fn collect_auth_handles(
150        &self,
151        out: &mut AuthHandleBuf,
152    ) -> Result<(), FaultString> {
153        for item in self {
154            item.collect_auth_handles(out)?;
155        }
156        Ok(())
157    }
158}
159
160impl<const MAX: usize> CollectAuthHandles for heapless::String<MAX> {
161    #[inline(always)]
162    fn collect_auth_handles(
163        &self,
164        _out: &mut AuthHandleBuf,
165    ) -> Result<(), FaultString> {
166        Ok(())
167    }
168}
169
170#[cfg(feature = "alloc")]
171impl<T: CollectAuthHandles + ?Sized> CollectAuthHandles
172    for alloc::boxed::Box<T>
173{
174    #[inline(always)]
175    fn collect_auth_handles(
176        &self,
177        out: &mut AuthHandleBuf,
178    ) -> Result<(), FaultString> {
179        (**self).collect_auth_handles(out)
180    }
181}
182
183#[cfg(feature = "alloc")]
184impl<T: CollectAuthHandles> CollectAuthHandles for alloc::vec::Vec<T> {
185    #[inline(always)]
186    fn collect_auth_handles(
187        &self,
188        out: &mut AuthHandleBuf,
189    ) -> Result<(), FaultString> {
190        self.as_slice().collect_auth_handles(out)
191    }
192}
193
194macro_rules! impl_collect_tuple {
195    ($(($($name:ident),+)),+ $(,)?) => {
196        $(
197            impl<$($name: CollectAuthHandles),+> CollectAuthHandles for ($($name,)+) {
198                #[inline(always)]
199                #[allow(non_snake_case)]
200                fn collect_auth_handles(&self, out: &mut AuthHandleBuf) -> Result<(), FaultString> {
201                    let ($($name,)+) = self;
202                    $($name.collect_auth_handles(out)?;)+
203                    Ok(())
204                }
205            }
206        )+
207    };
208}
209
210impl_collect_tuple!(
211    (A),
212    (A, B),
213    (A, B, C),
214    (A, B, C, D),
215    (A, B, C, D, E),
216    (A, B, C, D, E, F),
217    (A, B, C, D, E, F, G),
218    (A, B, C, D, E, F, G, H),
219    (A, B, C, D, E, F, G, H, I),
220    (A, B, C, D, E, F, G, H, I, J),
221    (A, B, C, D, E, F, G, H, I, J, K),
222    (A, B, C, D, E, F, G, H, I, J, K, L),
223    (A, B, C, D, E, F, G, H, I, J, K, L, M),
224    (A, B, C, D, E, F, G, H, I, J, K, L, M, N),
225    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O),
226    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P),
227    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q),
228    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R),
229    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S),
230    (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T),
231);
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236    use crate::signer::Signed;
237
238    #[test]
239    fn primitives_collect_nothing() {
240        let mut buf = AuthHandleBuf::new();
241        42u64.collect_auth_handles(&mut buf).unwrap();
242        true.collect_auth_handles(&mut buf).unwrap();
243        (-1i32).collect_auth_handles(&mut buf).unwrap();
244        assert!(buf.is_empty());
245    }
246
247    #[test]
248    fn signer_collects_handle() {
249        let mut buf = AuthHandleBuf::new();
250        let s: Signed<u32> = Signed::from_handle(99);
251        s.collect_auth_handles(&mut buf).unwrap();
252        assert_eq!(buf.as_slice(), &[99]);
253    }
254
255    #[test]
256    fn option_some_collects() {
257        let mut buf = AuthHandleBuf::new();
258        let s: Option<Signed<u32>> = Some(Signed::from_handle(5));
259        s.collect_auth_handles(&mut buf).unwrap();
260        assert_eq!(buf.as_slice(), &[5]);
261    }
262
263    #[test]
264    fn option_none_collects_nothing() {
265        let mut buf = AuthHandleBuf::new();
266        let s: Option<Signed<u32>> = None;
267        s.collect_auth_handles(&mut buf).unwrap();
268        assert!(buf.is_empty());
269    }
270
271    #[test]
272    fn slice_of_signeds() {
273        let mut buf = AuthHandleBuf::new();
274        let signeds: &[Signed<u32>] = &[
275            Signed::from_handle(1),
276            Signed::from_handle(2),
277            Signed::from_handle(3),
278        ];
279        signeds.collect_auth_handles(&mut buf).unwrap();
280        assert_eq!(buf.as_slice(), &[1, 2, 3]);
281    }
282
283    #[test]
284    fn mixed_tuple_collects_only_signeds() {
285        let mut buf = AuthHandleBuf::new();
286        let args: (u64, Signed<u32>, i32, Signed<bool>) =
287            (100, Signed::from_handle(10), -1, Signed::from_handle(20));
288        args.collect_auth_handles(&mut buf).unwrap();
289        assert_eq!(buf.as_slice(), &[10, 20]);
290    }
291
292    #[test]
293    fn single_element_tuple() {
294        let mut buf = AuthHandleBuf::new();
295        let args: (Signed<u32>,) = (Signed::from_handle(42),);
296        args.collect_auth_handles(&mut buf).unwrap();
297        assert_eq!(buf.as_slice(), &[42]);
298    }
299
300    #[test]
301    fn tuple_with_no_signeds() {
302        let mut buf = AuthHandleBuf::new();
303        let args: (u64, i32, bool) = (1, 2, true);
304        args.collect_auth_handles(&mut buf).unwrap();
305        assert!(buf.is_empty());
306    }
307}