dusk_cdf/
decoder.rs

1mod context;
2
3use std::fs::{File, OpenOptions};
4use std::io;
5use std::path::Path;
6
7pub use context::DecoderContext;
8use msgpacker::Message;
9
10use crate::{Constraint, DecodableElement, Preamble, Witness};
11
12/// A circuit description file
13///
14/// Since circuit descriptions are often large, it will perform lazy disk I/O, loading only the
15/// required data to satisfy the user operation.
16#[derive(Debug, Clone)]
17pub struct CircuitDescription<S> {
18    preamble: Preamble,
19    source_names: Vec<String>,
20    source_contents: Vec<String>,
21    source: S,
22}
23
24impl<S> CircuitDescription<S> {
25    pub(crate) fn context(&mut self) -> (DecoderContext, &mut S) {
26        let Self {
27            preamble,
28            source_names,
29            source_contents,
30            source,
31        } = self;
32
33        let ctx = DecoderContext::new(&preamble.config, source_names, source_contents);
34
35        (ctx, source)
36    }
37
38    /// Decoded preamble
39    pub const fn preamble(&self) -> &Preamble {
40        &self.preamble
41    }
42
43    /// Check if the provided name is contained within the available source names
44    pub fn source_name_contains(&self, name: &str) -> bool {
45        self.source_names.iter().any(|n| n.contains(name))
46    }
47}
48
49impl CircuitDescription<File> {
50    /// Open a CDF file as read-only.
51    pub fn open<P>(path: P) -> io::Result<Self>
52    where
53        P: AsRef<Path>,
54    {
55        OpenOptions::new()
56            .read(true)
57            .open(path)
58            .and_then(Self::from_reader)
59    }
60}
61
62impl<S> CircuitDescription<S>
63where
64    S: io::Read + io::Seek,
65{
66    /// Create a new circuit description instance.
67    pub fn from_reader(mut source: S) -> io::Result<Self> {
68        // reset the cursor
69        source.seek(io::SeekFrom::Start(0))?;
70
71        // load the preamble with the base config
72        let preamble = Preamble::try_from_reader(&DecoderContext::BASE, source.by_ref())?;
73
74        let ofs = preamble.source_cache_offset();
75        let ofs = io::SeekFrom::Start(ofs as u64);
76        source.seek(ofs)?;
77
78        let source_names = Message::unpack(source.by_ref())?;
79        let source_contents = Message::unpack(source.by_ref())?;
80
81        let (source_names, source_contents) = match (source_names, source_contents) {
82            (Message::Array(n), Message::Array(c)) => (n, c),
83            _ => {
84                return Err(io::Error::new(
85                    io::ErrorKind::InvalidData,
86                    "the source cache isn't a valid array",
87                ))
88            }
89        };
90
91        let source_names = source_names
92            .into_iter()
93            .map(|m| match m {
94                Message::String(s) => Ok(s),
95                _ => Err(io::Error::new(
96                    io::ErrorKind::InvalidData,
97                    "the source names isn't composed of strings",
98                )),
99            })
100            .collect::<io::Result<Vec<_>>>()?;
101
102        let source_contents = source_contents
103            .into_iter()
104            .map(|m| match m {
105                Message::String(s) => Ok(s),
106                _ => Err(io::Error::new(
107                    io::ErrorKind::InvalidData,
108                    "the source contents isn't composed of strings",
109                )),
110            })
111            .collect::<io::Result<Vec<_>>>()?;
112
113        Ok(Self {
114            preamble,
115            source_names,
116            source_contents,
117            source,
118        })
119    }
120
121    /// Attempt to read an indexed constraint from the source
122    pub fn fetch_constraint(&mut self, idx: usize) -> io::Result<Constraint> {
123        self.preamble
124            .constraint_offset(idx)
125            .ok_or_else(|| {
126                io::Error::new(io::ErrorKind::Other, "attempt to fetch invalid constraint")
127            })
128            .map(|ofs| io::SeekFrom::Start(ofs as u64))
129            .and_then(|ofs| self.source.seek(ofs))?;
130
131        let (ctx, source) = self.context();
132
133        Constraint::try_from_reader(&ctx, source)
134    }
135
136    /// Attempt to read an indexed witness from the source
137    pub fn fetch_witness(&mut self, idx: usize) -> io::Result<Witness> {
138        self.preamble
139            .witness_offset(idx)
140            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "attempt to fetch invalid witness"))
141            .map(|ofs| io::SeekFrom::Start(ofs as u64))
142            .and_then(|ofs| self.source.seek(ofs))?;
143
144        let (ctx, source) = self.context();
145
146        Witness::try_from_reader(&ctx, source)
147    }
148}