tezos_smart_rollup_installer_config/binary/
bin.rs

1// SPDX-FileCopyrightText: 2023 TriliTech <contact@trili.tech>
2// SPDX-FileCopyrightText: 2023 Nomadic Labs <contact@nomadic-labs.com>
3//
4// SPDX-License-Identifier: MIT
5
6use tezos_data_encoding::enc::{
7    field, put_byte, put_bytes, BinError, BinResult, BinWriter,
8};
9use tezos_smart_rollup_host::path::Path;
10
11use crate::binary::SetInstruction;
12
13use super::{
14    instr::{ConfigInstruction, MoveInstruction, RefBytes, RevealInstruction},
15    owned::OwnedConfigProgram,
16};
17
18fn put_le_size(size: usize, out: &mut Vec<u8>) -> BinResult {
19    let size = u32::try_from(size).map_err(|_| {
20        BinError::custom(format!(
21            "Expected {} but got {}",
22            (u32::MAX >> 2) as usize,
23            size
24        ))
25    })?;
26    tezos_data_encoding::enc::put_bytes(&size.to_le_bytes(), out);
27    Ok(())
28}
29
30/// Encode RefPath as a bytes with prepended path size
31fn path_dynamic(p: &impl Path, output: &mut Vec<u8>) -> BinResult {
32    let data = p.as_bytes();
33    // We can cast it to u8 as path length doesn't exceed 250
34    let size = data.len() as u8;
35    put_byte(&size, output);
36    put_bytes(data, output);
37    Ok(())
38}
39
40fn bytes_dynamic(b: &impl AsRef<[u8]>, output: &mut Vec<u8>) -> BinResult {
41    put_le_size(b.as_ref().len(), output)?;
42    put_bytes(b.as_ref(), output);
43    Ok(())
44}
45
46impl<'a> BinWriter for RefBytes<'a> {
47    fn bin_write(&self, output: &mut Vec<u8>) -> BinResult {
48        bytes_dynamic(self, output)
49    }
50}
51
52impl<P: Path> BinWriter for MoveInstruction<P> {
53    fn bin_write(&self, out: &mut Vec<u8>) -> tezos_data_encoding::enc::BinResult {
54        (|data: &Self, out: &mut Vec<u8>| {
55            tezos_data_encoding::enc::field("MoveInstruction::from", path_dynamic)(
56                &data.from, out,
57            )?;
58            tezos_data_encoding::enc::field("MoveInstruction::to", path_dynamic)(
59                &data.to, out,
60            )?;
61            Ok(())
62        })(self, out)
63    }
64}
65
66impl<P: Path, B: AsRef<[u8]>> BinWriter for RevealInstruction<P, B> {
67    fn bin_write(&self, out: &mut Vec<u8>) -> tezos_data_encoding::enc::BinResult {
68        (|data: &Self, out: &mut Vec<u8>| {
69            field("RevealInstruction::hash", bytes_dynamic)(&data.hash, out)?;
70            field("RevealInstruction::to", path_dynamic)(&data.to, out)?;
71            Ok(())
72        })(self, out)
73    }
74}
75
76impl<P: Path, B: AsRef<[u8]>> BinWriter for SetInstruction<P, B> {
77    fn bin_write(&self, out: &mut Vec<u8>) -> tezos_data_encoding::enc::BinResult {
78        (|data: &Self, out: &mut Vec<u8>| {
79            field("SetInstruction::value", bytes_dynamic)(&data.value, out)?;
80            field("SetInstruction::to", path_dynamic)(&data.to, out)?;
81            Ok(())
82        })(self, out)
83    }
84}
85
86impl<P: Path, B: AsRef<[u8]>> BinWriter for ConfigInstruction<P, B> {
87    fn bin_write(&self, out: &mut Vec<u8>) -> tezos_data_encoding::enc::BinResult {
88        use tezos_data_encoding::enc::{u8, variant_with_field};
89        match self {
90            ConfigInstruction::Reveal(inner) => variant_with_field(
91                "ConfigInstruction::Reveal",
92                u8,
93                <RevealInstruction<P, B> as BinWriter>::bin_write,
94            )(&0, inner, out),
95            ConfigInstruction::Move(inner) => {
96                tezos_data_encoding::enc::variant_with_field(
97                    "ConfigInstruction::Move",
98                    u8,
99                    <MoveInstruction<P> as BinWriter>::bin_write,
100                )(&1, inner, out)
101            }
102            ConfigInstruction::Set(inner) => variant_with_field(
103                "ConfigInstruction::Set",
104                u8,
105                <SetInstruction<P, B> as BinWriter>::bin_write,
106            )(&2, inner, out),
107        }
108    }
109}
110
111// Encode all commands with appended number of commands at the end.
112// It makes possible for the installer_kernel to
113// parse commands at the end of the kernel binary.
114impl BinWriter for OwnedConfigProgram {
115    fn bin_write(&self, output: &mut Vec<u8>) -> BinResult {
116        let initial_size = output.len();
117        for i in 0..self.0.len() {
118            let mut current_instr = vec![];
119            self.0[i].bin_write(&mut current_instr)?;
120            // Put size of the instruction encoding first,
121            // in order to make a decoding easier
122            put_le_size(current_instr.len(), output)?;
123            output.extend_from_slice(&current_instr);
124        }
125        put_le_size(output.len() - initial_size, output)?;
126        Ok(())
127    }
128}
129
130#[cfg(feature = "alloc")]
131#[cfg(test)]
132mod test {
133    use std::fmt::Debug;
134
135    use tezos_data_encoding::enc::BinWriter;
136
137    use crate::binary::NomReader;
138
139    // I have to pass `out` here because for some reason
140    // borrow checker complaines about this line:
141    //    `T::nom_read(out).unwrap()`
142    // saying that `out` is dropped but still borrowed in this line,
143    // despite the faact `decoded` should be dropped on leaving the function
144    fn roundtrip<'a, T: Debug + PartialEq + Eq + BinWriter + NomReader<'a>>(
145        orig: &T,
146        out: &'a mut Vec<u8>,
147    ) {
148        orig.bin_write(out).unwrap();
149
150        let decoded = T::nom_read(out).unwrap();
151        assert!(decoded.0.is_empty());
152        assert_eq!(*orig, decoded.1);
153    }
154
155    #[test]
156    fn roundtrip_encdec() {
157        use tezos_smart_rollup_host::path::RefPath;
158
159        use crate::binary::instr::{
160            ConfigInstruction, MoveInstruction, RefBytes, RevealInstruction,
161        };
162        roundtrip(&RefBytes("hello".as_bytes()), &mut vec![]);
163
164        roundtrip(
165            &MoveInstruction {
166                from: RefPath::assert_from("/d".as_bytes()),
167                to: RefPath::assert_from("/cc".as_bytes()),
168            },
169            &mut vec![],
170        );
171
172        roundtrip(
173            &RevealInstruction {
174                to: RefPath::assert_from("/fldl/sfjisfkj".as_bytes()),
175                hash: RefBytes("some hash should be 33 bytes".as_bytes()),
176            },
177            &mut vec![],
178        );
179
180        roundtrip(
181            &ConfigInstruction::Reveal(RevealInstruction {
182                to: RefPath::assert_from("/fldl/sfjisfkj".as_bytes()),
183                hash: RefBytes("some hash should be 33 bytes".as_bytes()),
184            }),
185            &mut vec![],
186        );
187    }
188}