rock_n_rollup/plugins/
dac.rs

1use crate::core::{Runtime, PREIMAGE_HASH_SIZE};
2
3pub struct PreimageHash {
4    inner: [u8; PREIMAGE_HASH_SIZE],
5}
6
7impl TryFrom<&str> for PreimageHash {
8    type Error = ();
9
10    fn try_from(value: &str) -> Result<Self, Self::Error> {
11        let decoded = hex::decode(value).map_err(|_| ())?;
12        let inner = decoded.try_into().map_err(|_| ())?;
13        Ok(Self { inner })
14    }
15}
16
17impl AsRef<[u8; PREIMAGE_HASH_SIZE]> for PreimageHash {
18    fn as_ref(&self) -> &[u8; PREIMAGE_HASH_SIZE] {
19        &self.inner
20    }
21}
22
23impl<'a> TryFrom<&'a [u8]> for PreimageHash {
24    type Error = ();
25
26    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
27        let inner = value.try_into().map_err(|_| ())?;
28        Ok(Self { inner })
29    }
30}
31
32impl<'a> TryFrom<&'a Vec<u8>> for PreimageHash {
33    type Error = ();
34
35    fn try_from(value: &'a Vec<u8>) -> Result<Self, Self::Error> {
36        let inner = value.clone().try_into().map_err(|_| ())?;
37        Ok(Self { inner })
38    }
39}
40
41impl PreimageHash {
42    fn new(inner: [u8; PREIMAGE_HASH_SIZE]) -> Self {
43        Self { inner }
44    }
45}
46
47/// Borrowing version of [V0ContentPage].
48#[derive(Debug)]
49pub struct V0SliceContentPage<'a> {
50    inner: &'a [u8],
51}
52
53/// Borrowing version of [V0HashPage].
54#[derive(Debug)]
55pub struct V0SliceHashPage<'a> {
56    // Guaranteed to be a multiple of PREIMAGE_HASH_SIZE
57    inner: &'a [u8],
58}
59
60/// A Dac [Page] that borrows the underlying buffer.
61///
62/// Can be used in `no_std` & `alloc`-free environments.
63#[derive(Debug)]
64pub enum SlicePage<'a> {
65    /// Contents of borrowed bytes.
66    V0ContentPage(V0SliceContentPage<'a>),
67    /// Contents of borrowed hashes.
68    V0HashPage(V0SliceHashPage<'a>),
69}
70
71/// Errors that may occur when dealing with [SlicePage].
72#[derive(Debug)]
73pub enum SlicePageError {
74    /// Unknown page tag.
75    InvalidTag(Option<u8>),
76    /// Invalid size prefix.
77    InvalidSizePrefix,
78}
79
80/// Maximum size of dac pages is 4Kb.
81pub const MAX_PAGE_SIZE: usize = 4096;
82
83/// Tag size to distinguish hash/contents pages.
84pub(crate) const PAGE_TAG_SIZE: usize = 1;
85
86/// Prefix of 4-bytes to define how large contents/hash page is.
87pub(crate) const PAGE_SIZE_PREFIX_SIZE: usize = 4;
88
89/// Maximum content/hashes size that can fit in a page.
90pub(crate) const MAX_USABLE_PAGE_SIZE: usize =
91    MAX_PAGE_SIZE - (PAGE_TAG_SIZE + PAGE_SIZE_PREFIX_SIZE);
92
93impl<'a> V0SliceContentPage<'a> {
94    /// Maximum size of content in each page.
95    pub const MAX_CONTENT_SIZE: usize = MAX_USABLE_PAGE_SIZE;
96
97    // Assumes magic byte has been discarded
98    fn parse(slice: &'a [u8]) -> Result<Self, SlicePageError> {
99        if slice.len() < 4 {
100            return Err(SlicePageError::InvalidSizePrefix);
101        }
102
103        let size = u32::from_be_bytes([slice[0], slice[1], slice[2], slice[3]]) as usize;
104
105        let end_offset = 4 + size;
106
107        if slice.len() < end_offset {
108            return Err(SlicePageError::InvalidSizePrefix);
109        }
110
111        Ok(Self {
112            inner: &slice[4..end_offset],
113        })
114    }
115}
116
117impl<'a> V0SliceHashPage<'a> {
118    /// Maximum number of hashes able to fit into a hash page.
119    pub const MAX_HASHES_PER_PAGE: usize = MAX_USABLE_PAGE_SIZE / PREIMAGE_HASH_SIZE;
120
121    /// Returns an iterator over the preimage hashes contained within.
122    pub fn hashes(&self) -> impl Iterator<Item = &'a [u8; PREIMAGE_HASH_SIZE]> {
123        // there is a nightly(only) API called `as_chunks` that would return
124        // `(&[[u8; PREIMAPREIMAGE_HASH_SIZE]], &[u8])` that we could use in
125        // future
126        self.inner
127            .chunks_exact(PREIMAGE_HASH_SIZE)
128            .map(|chunk| chunk.try_into().expect("Guaranteed to be exact."))
129    }
130
131    // Assumes magic byte has been discarded
132    fn parse(slice: &'a [u8]) -> Result<Self, SlicePageError> {
133        if slice.len() < 4 {
134            return Err(SlicePageError::InvalidSizePrefix);
135        }
136
137        let size = u32::from_be_bytes([slice[0], slice[1], slice[2], slice[3]]) as usize;
138
139        let end_offset = 4 + size; // for prefix bytes
140
141        if slice.len() < end_offset || size % PREIMAGE_HASH_SIZE != 0 {
142            return Err(SlicePageError::InvalidSizePrefix);
143        }
144
145        Ok(Self {
146            inner: &slice[4..end_offset],
147        })
148    }
149}
150
151impl<'a> TryFrom<&'a [u8]> for SlicePage<'a> {
152    type Error = SlicePageError;
153
154    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
155        match value {
156            [0, rest @ ..] => Ok(SlicePage::V0ContentPage(V0SliceContentPage::parse(rest)?)),
157            [1, rest @ ..] => Ok(SlicePage::V0HashPage(V0SliceHashPage::parse(rest)?)),
158            _ => Err(SlicePageError::InvalidTag(value.first().cloned())),
159        }
160    }
161}
162
163/// Recursively traverses a Merkle Tree of hashes up to `max_dac_levels` depth where each hash
164/// corresponds to a preimage that can be revealed via [Runtime::reveal_preimage]. The closure
165/// `save_content` is applied on each content page found.
166///
167/// N.B `max_dac_levels`, should probably be kept under 4 (4 gives 7.9GB of data approximately)
168pub fn reveal_loop<Host: Runtime>(
169    host: &mut Host,
170    level: usize,
171    hash: &PreimageHash,
172    max_dac_levels: usize,
173    acc: &mut Vec<Vec<u8>>,
174) -> Result<(), ()> {
175    if level >= max_dac_levels {
176        return Err(());
177    }
178
179    let page = host.reveal_preimage(hash.as_ref())?;
180    let page = page.as_ref();
181
182    let page = SlicePage::try_from(page).map_err(|_| ())?;
183
184    match page {
185        SlicePage::V0HashPage(hashes) => {
186            for hash in hashes.hashes() {
187                let hash = PreimageHash::new(*hash); // TODO: avoid cloning
188                reveal_loop(host, level + 1, &hash, max_dac_levels, acc)?;
189            }
190            Ok(())
191        }
192        SlicePage::V0ContentPage(content) => {
193            let content = content.inner.to_vec();
194            acc.push(content);
195            Ok(())
196        }
197    }
198}
199
200pub trait Dac {
201    /// Read the data from the DAC and returns you the data as a vector of bytes
202    fn read_from_dac(&mut self, hash: &PreimageHash) -> Result<Vec<u8>, ()>;
203}
204
205impl<R> Dac for R
206where
207    R: Runtime,
208{
209    fn read_from_dac(&mut self, hash: &PreimageHash) -> Result<Vec<u8>, ()> {
210        let mut data = Vec::default();
211        reveal_loop(self, 0, hash, 3, &mut data)?;
212        let data = data.iter().flatten().copied().collect::<Vec<u8>>();
213        Ok(data)
214    }
215}