Skip to main content

midnight_base_crypto/fab/
conversions.rs

1// This file is part of midnight-ledger.
2// Copyright (C) 2025 Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use super::{Aligned, DynAligned};
15use super::{AlignedValue, Value, ValueAtom, ValueSlice};
16use crate::hash::{HashOutput, PERSISTENT_HASH_BYTES as PHB};
17use std::convert::Infallible;
18use std::error::Error;
19use std::fmt::{self, Debug, Display, Formatter};
20
21impl<T: Clone + Into<Value>> From<&T> for Value {
22    fn from(val: &T) -> Value {
23        val.clone().into()
24    }
25}
26
27macro_rules! forward_primitive_value {
28    ($($ty:ty),*) => {
29        $(
30            impl From<$ty> for Value {
31                fn from(val: $ty) -> Value {
32                    Value(vec![val.into()])
33                }
34            }
35
36            impl TryFrom<&ValueSlice> for $ty {
37                type Error = InvalidBuiltinDecode;
38
39                fn try_from(value: &ValueSlice) -> Result<$ty, InvalidBuiltinDecode> {
40                    if value.0.len() == 1 {
41                        Ok(<$ty>::try_from(&value.0[0])?)
42                    } else {
43                        Err(InvalidBuiltinDecode(stringify!($ty)))
44                    }
45                }
46            }
47        )*
48    }
49}
50
51impl From<u128> for ValueAtom {
52    fn from(val: u128) -> ValueAtom {
53        ValueAtom(val.to_le_bytes().to_vec()).normalize()
54    }
55}
56
57impl TryFrom<&ValueAtom> for u128 {
58    type Error = InvalidBuiltinDecode;
59
60    fn try_from(value: &ValueAtom) -> Result<Self, Self::Error> {
61        if value.0.len() <= 128 / 8 {
62            let mut le_bytes = [0u8; 128 / 8];
63            le_bytes[..value.0.len()].copy_from_slice(&value.0);
64            Ok(u128::from_le_bytes(le_bytes))
65        } else {
66            Err(InvalidBuiltinDecode("Fr"))
67        }
68    }
69}
70
71macro_rules! wrap_via_u128 {
72    ($($ty:ty),*) => {
73        $(
74            impl From<$ty> for ValueAtom {
75                fn from(val: $ty) -> ValueAtom {
76                    u128::from(val).into()
77                }
78            }
79
80            impl TryFrom<&ValueAtom> for $ty {
81                type Error = InvalidBuiltinDecode;
82
83                fn try_from(value: &ValueAtom) -> Result<$ty, InvalidBuiltinDecode> {
84                    u128::try_from(value)?.try_into().map_err(|_| InvalidBuiltinDecode(stringify!($ty)))
85                }
86            }
87        )*
88    }
89}
90
91impl From<bool> for ValueAtom {
92    fn from(value: bool) -> Self {
93        ValueAtom(vec![value as u8]).normalize()
94    }
95}
96
97impl TryFrom<&ValueAtom> for bool {
98    type Error = InvalidBuiltinDecode;
99
100    fn try_from(value: &ValueAtom) -> Result<Self, Self::Error> {
101        let byte = u8::try_from(value)?;
102        match byte {
103            0 => Ok(false),
104            1 => Ok(true),
105            _ => Err(InvalidBuiltinDecode("bool")),
106        }
107    }
108}
109
110wrap_via_u128!(u8, u16, u32, u64);
111
112forward_primitive_value!(HashOutput, u8, u16, u32, u64, u128, bool, Vec<u8>);
113
114impl From<HashOutput> for ValueAtom {
115    fn from(hash: HashOutput) -> ValueAtom {
116        ValueAtom(hash.0.to_vec()).normalize()
117    }
118}
119
120impl TryFrom<&ValueAtom> for HashOutput {
121    type Error = InvalidBuiltinDecode;
122
123    fn try_from(value: &ValueAtom) -> Result<HashOutput, InvalidBuiltinDecode> {
124        let mut buf = [0u8; PHB];
125        if value.0.len() <= PHB {
126            buf[..value.0.len()].copy_from_slice(&value.0[..]);
127            Ok(HashOutput(buf))
128        } else {
129            Err(InvalidBuiltinDecode("HashOutput"))
130        }
131    }
132}
133
134impl From<&[u8]> for ValueAtom {
135    fn from(val: &[u8]) -> ValueAtom {
136        let mut vec = val.to_vec();
137        while let Some(0u8) = vec.last() {
138            vec.pop();
139        }
140        ValueAtom(vec)
141    }
142}
143
144impl From<&ValueAtom> for Vec<u8> {
145    fn from(value: &ValueAtom) -> Vec<u8> {
146        value.0.clone()
147    }
148}
149
150impl From<()> for ValueAtom {
151    fn from((): ()) -> ValueAtom {
152        ValueAtom(vec![])
153    }
154}
155
156impl TryFrom<&ValueAtom> for () {
157    type Error = InvalidBuiltinDecode;
158
159    fn try_from(value: &ValueAtom) -> Result<(), InvalidBuiltinDecode> {
160        if value.0.is_empty() {
161            Ok(())
162        } else {
163            Err(InvalidBuiltinDecode("()"))
164        }
165    }
166}
167
168impl From<Vec<u8>> for ValueAtom {
169    fn from(mut value: Vec<u8>) -> ValueAtom {
170        while let Some(0u8) = value.last() {
171            value.pop();
172        }
173        ValueAtom(value)
174    }
175}
176
177macro_rules! tuple_conversions {
178    () => {
179        impl From<()> for Value {
180            fn from(_: ()) -> Value {
181                Value(Vec::new())
182            }
183        }
184
185        impl TryFrom<&ValueSlice> for () {
186            type Error = InvalidBuiltinDecode;
187
188            fn try_from(value: &ValueSlice) -> Result<(), InvalidBuiltinDecode> {
189                if value.0.is_empty() {
190                    Ok(())
191                } else {
192                    Err(InvalidBuiltinDecode("()"))
193                }
194            }
195        }
196    };
197    ($a:ident$(, $as:ident)*) => {
198        impl<$a$(, $as)*> From<($a, $($as, )*)> for Value
199            where Value: From<$a>$( + From<$as>)*
200        {
201            #[allow(non_snake_case)]
202            fn from(($a, $($as, )*): ($a, $($as, )*)) -> Value {
203                Value::concat([&Value::from($a)$(, &$as.into())*])
204            }
205        }
206
207        impl<$a$(, $as)*> TryFrom<&ValueSlice> for ($a, $($as, )*)
208            where
209                $a: Aligned + for<'a> TryFrom<&'a ValueSlice, Error = InvalidBuiltinDecode>,
210                $($as: Aligned + for<'a> TryFrom<&'a ValueSlice, Error = InvalidBuiltinDecode>,)*
211        {
212            type Error = InvalidBuiltinDecode;
213
214            #[allow(non_snake_case)]
215            fn try_from(mut val: &ValueSlice) -> Result<Self, InvalidBuiltinDecode> {
216                let err = || InvalidBuiltinDecode(stringify!(($a, $($as),*)));
217                let a_align = <$a>::alignment();
218                let a_end = a_align.consume_internal(val, &|idx: &mut usize, _| *idx += 1, &|idx| *idx, 0usize).ok_or_else(err)?;
219                let a_slice = ValueSlice::from_prim_slice(&val.0[..a_end]);
220                let a_val = <$a>::try_from(a_slice)?;
221                val = ValueSlice::from_prim_slice(&val.0[a_end..]);
222                $(
223                    let as_align = <$as>::alignment();
224                    let as_end = as_align.consume_internal(val, &|idx: &mut usize, _| *idx += 1, &|idx| *idx, 0usize).ok_or_else(err)?;
225                    let as_slice = ValueSlice::from_prim_slice(&val.0[..as_end]);
226                    let $as = <$as>::try_from(as_slice)?;
227                    val = ValueSlice::from_prim_slice(&val.0[as_end..]);
228                )*
229                if val.0.is_empty() {
230                    Ok((a_val, $($as, )*))
231                } else {
232                    Err(err())
233                }
234            }
235        }
236
237        tuple_conversions!($($as),*);
238    }
239}
240
241tuple_conversions!(A, B, C, D, E, F, G, H, I, J, K);
242
243#[allow(clippy::from_over_into)]
244impl Into<Value> for AlignedValue {
245    fn into(self) -> Value {
246        self.value.clone()
247    }
248}
249
250// Implemented via From instead of into to allow Into<Value> impl above
251impl<T: DynAligned> From<T> for AlignedValue
252where
253    Value: From<T>,
254{
255    fn from(inp: T) -> AlignedValue {
256        let align = inp.dyn_alignment();
257        let value = inp.into();
258        AlignedValue::new(value, align).expect("Aligned value should match alignment")
259    }
260}
261
262impl<T: Into<Value> + Default> From<Option<T>> for Value {
263    fn from(inp: Option<T>) -> Value {
264        let (is_some, value) = match inp {
265            Some(val) => (true, val),
266            None => (false, T::default()),
267        };
268        Value::concat([Value::from(is_some), value.into()].iter())
269    }
270}
271
272impl<const N: usize> From<[u8; N]> for ValueAtom {
273    fn from(inp: [u8; N]) -> ValueAtom {
274        let mut vec = inp.to_vec();
275        while let Some(0) = vec.last() {
276            vec.pop();
277        }
278        ValueAtom(vec)
279    }
280}
281
282impl<const N: usize> TryFrom<ValueAtom> for [u8; N] {
283    type Error = InvalidBuiltinDecode;
284
285    fn try_from(atom: ValueAtom) -> Result<[u8; N], InvalidBuiltinDecode> {
286        let mut buf = [0u8; N];
287        if atom.0.len() <= buf.len() {
288            buf[..atom.0.len()].copy_from_slice(&atom.0);
289            Ok(buf)
290        } else {
291            Err(InvalidBuiltinDecode(std::any::type_name::<[u8; N]>()))
292        }
293    }
294}
295
296impl<const N: usize> From<[u8; N]> for Value {
297    fn from(inp: [u8; N]) -> Value {
298        Value(vec![inp.into()])
299    }
300}
301
302impl<const N: usize> TryFrom<Value> for [u8; N] {
303    type Error = InvalidBuiltinDecode;
304
305    fn try_from(mut value: Value) -> Result<[u8; N], InvalidBuiltinDecode> {
306        if value.0.len() == 1 {
307            value.0.remove(0).try_into()
308        } else {
309            Err(InvalidBuiltinDecode(std::any::type_name::<[u8; N]>()))
310        }
311    }
312}
313
314/// An error decoding data from a field-aligned binary format into a builtin
315/// data type.
316#[derive(Debug, Clone, PartialEq, Eq)]
317pub struct InvalidBuiltinDecode(pub &'static str);
318
319impl Display for InvalidBuiltinDecode {
320    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
321        write!(
322            formatter,
323            "failed to decode for built-in type {} after successful typecheck",
324            self.0
325        )
326    }
327}
328
329impl From<Infallible> for InvalidBuiltinDecode {
330    fn from(e: Infallible) -> InvalidBuiltinDecode {
331        match e {}
332    }
333}
334
335impl Error for InvalidBuiltinDecode {}