triton_opcodes/
program.rs1use std::fmt::Display;
2use std::io::Cursor;
3
4use anyhow::bail;
5use anyhow::Result;
6use get_size::GetSize;
7use serde_derive::Deserialize;
8use serde_derive::Serialize;
9use twenty_first::shared_math::b_field_element::BFieldElement;
10use twenty_first::shared_math::bfield_codec::BFieldCodec;
11use twenty_first::shared_math::digest::Digest;
12use twenty_first::util_types::algebraic_hasher::AlgebraicHasher;
13
14use crate::instruction::convert_labels;
15use crate::instruction::Instruction;
16use crate::instruction::LabelledInstruction;
17use crate::parser::parse;
18use crate::parser::to_labelled;
19
20#[derive(Debug, Clone, Default, PartialEq, Eq, GetSize, Serialize, Deserialize)]
25pub struct Program {
26 pub instructions: Vec<Instruction>,
27}
28
29impl Display for Program {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 let mut stream = self.instructions.iter();
32 while let Some(instruction) = stream.next() {
33 writeln!(f, "{instruction}")?;
34
35 for _ in 1..instruction.size() {
37 stream.next();
38 }
39 }
40 Ok(())
41 }
42}
43
44impl BFieldCodec for Program {
45 fn decode(sequence: &[BFieldElement]) -> Result<Box<Self>> {
46 if sequence.is_empty() {
47 bail!("Sequence to decode must not be empty.");
48 }
49 let program_length = sequence[0].value() as usize;
50 let sequence = &sequence[1..];
51 if sequence.len() != program_length {
52 bail!(
53 "Sequence to decode must have length {program_length}, but has length {}.",
54 sequence.len()
55 );
56 }
57
58 let mut idx = 0;
59 let mut instructions = Vec::with_capacity(program_length);
60 while idx < program_length {
61 let opcode: u32 = match sequence[idx].value().try_into() {
62 Ok(opcode) => opcode,
63 Err(_) => bail!("Invalid opcode {} at index {idx}.", sequence[idx].value()),
64 };
65 let instruction: Instruction = opcode.try_into()?;
66 if !instruction.has_arg() {
67 instructions.push(instruction);
68 } else if instructions.len() + 1 >= program_length {
69 bail!("Missing argument for instruction {instruction} at index {idx}.");
70 } else {
71 let instruction = match instruction.change_arg(sequence[idx + 1]) {
72 Some(instruction) => instruction,
73 None => {
74 bail!("Invalid argument for instruction {instruction} at index {idx}.")
75 }
76 };
77 instructions.push(instruction);
79 instructions.push(instruction);
80 }
81 idx += instruction.size();
82 }
83
84 if idx != program_length {
85 bail!("Decoded program must have length {program_length}, but has length {idx}.",);
86 }
87 Ok(Box::new(Program { instructions }))
88 }
89
90 fn encode(&self) -> Vec<BFieldElement> {
91 let mut sequence = Vec::with_capacity(self.len_bwords() + 1);
92 sequence.push(BFieldElement::new(self.len_bwords() as u64));
93 sequence.extend(self.to_bwords());
94 sequence
95 }
96
97 fn static_length() -> Option<usize> {
98 None
99 }
100}
101
102pub struct InstructionIter {
104 cursor: Cursor<Vec<Instruction>>,
105}
106
107impl Iterator for InstructionIter {
108 type Item = Instruction;
109
110 fn next(&mut self) -> Option<Self::Item> {
111 let pos = self.cursor.position() as usize;
112 let instructions = self.cursor.get_ref();
113 let instruction = *instructions.get(pos)?;
114 self.cursor.set_position((pos + instruction.size()) as u64);
115
116 Some(instruction)
117 }
118}
119
120impl IntoIterator for Program {
121 type Item = Instruction;
122
123 type IntoIter = InstructionIter;
124
125 fn into_iter(self) -> Self::IntoIter {
126 let cursor = Cursor::new(self.instructions);
127 InstructionIter { cursor }
128 }
129}
130
131impl Program {
132 pub fn new(input: &[LabelledInstruction]) -> Self {
134 let instructions = convert_labels(input)
135 .iter()
136 .flat_map(|instr| vec![*instr; instr.size()])
137 .collect::<Vec<_>>();
138
139 Program { instructions }
140 }
141
142 pub fn from_code(code: &str) -> Result<Self> {
144 parse(code)
145 .map(|program| Program::new(&to_labelled(&program)))
146 .map_err(|err| anyhow::anyhow!("{}", err))
147 }
148
149 pub fn to_bwords(&self) -> Vec<BFieldElement> {
155 self.clone()
156 .into_iter()
157 .flat_map(|instruction| {
158 let opcode = instruction.opcode_b();
159 if let Some(arg) = instruction.arg() {
160 vec![opcode, arg]
161 } else {
162 vec![opcode]
163 }
164 })
165 .collect()
166 }
167
168 pub fn len_bwords(&self) -> usize {
171 self.instructions.len()
172 }
173
174 pub fn is_empty(&self) -> bool {
175 self.instructions.is_empty()
176 }
177
178 pub fn hash<H: AlgebraicHasher>(&self) -> Digest {
180 H::hash_varlen(&self.to_bwords())
181 }
182}
183
184#[cfg(test)]
185mod test {
186 use rand::thread_rng;
187 use rand::Rng;
188 use twenty_first::shared_math::tip5::Tip5;
189
190 use crate::parser::parser_tests::program_gen;
191
192 use super::*;
193
194 #[test]
195 fn random_program_encode_decode_equivalence() {
196 let mut rng = thread_rng();
197 for _ in 0..50 {
198 let program_len = rng.gen_range(20..420);
199 let source_code = program_gen(program_len);
200 let program = Program::from_code(&source_code).unwrap();
201
202 let encoded = program.encode();
203 let decoded = *Program::decode(&encoded).unwrap();
204
205 assert_eq!(program, decoded);
206 }
207 }
208
209 #[test]
210 fn decode_program_with_missing_argument_as_last_instruction() {
211 let program = Program::from_code("push 3 push 3 eq assert push 3").unwrap();
212 let program_length = program.len_bwords() as u64;
213 let encoded = program.encode();
214
215 let mut encoded = encoded[0..encoded.len() - 1].to_vec();
216 encoded[0] = BFieldElement::new(program_length - 1);
217
218 let err = Program::decode(&encoded).err().unwrap();
219 assert_eq!(
220 "Missing argument for instruction push 0 at index 6.",
221 err.to_string(),
222 );
223 }
224
225 #[test]
226 fn decode_program_with_length_mismatch() {
227 let program = Program::from_code("nop nop hash push 0 skiz end: halt call end").unwrap();
228 let program_length = program.len_bwords() as u64;
229 let mut encoded = program.encode();
230
231 encoded[0] = BFieldElement::new(program_length + 1);
232
233 let err = Program::decode(&encoded).err().unwrap();
234 assert_eq!(
235 "Sequence to decode must have length 10, but has length 9.",
236 err.to_string(),
237 );
238 }
239
240 #[test]
241 fn decode_program_from_empty_sequence() {
242 let encoded = vec![];
243 let err = Program::decode(&encoded).err().unwrap();
244 assert_eq!("Sequence to decode must not be empty.", err.to_string(),);
245 }
246
247 #[test]
248 fn hash_simple_program() {
249 let program = Program::from_code("halt").unwrap();
250 let digest = program.hash::<Tip5>();
251
252 let expected_digest = [
253 04843866011885844809,
254 16618866032559590857,
255 18247689143239181392,
256 07637465675240023996,
257 09104890367162237026,
258 ]
259 .map(BFieldElement::new);
260 let expected_digest = Digest::new(expected_digest);
261
262 assert_eq!(expected_digest, digest);
263 }
264
265 #[test]
266 fn empty_program_is_empty() {
267 let program = Program::from_code("").unwrap();
268 assert!(program.is_empty());
269 }
270}