decoder.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2025 Dyne.org foundation 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 use darkfi_serial::{deserialize_partial, VarInt}; 20 21 use super::{ 22 compiler::MAGIC_BYTES, 23 constants::{MAX_K, MAX_NS_LEN, MIN_BIN_SIZE}, 24 types::HeapType, 25 LitType, Opcode, VarType, 26 }; 27 use crate::{Error::ZkasDecoderError as ZkasErr, Result}; 28 29 /// A ZkBinary decoded from compiled zkas code. 30 /// This is used by the zkvm. 31 #[derive(Clone, Debug)] 32 pub struct ZkBinary { 33 pub namespace: String, 34 pub k: u32, 35 pub constants: Vec<(VarType, String)>, 36 pub literals: Vec<(LitType, String)>, 37 pub witnesses: Vec<VarType>, 38 pub opcodes: Vec<(Opcode, Vec<(HeapType, usize)>)>, 39 } 40 41 // https://stackoverflow.com/questions/35901547/how-can-i-find-a-subsequence-in-a-u8-slice 42 fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> { 43 haystack.windows(needle.len()).position(|window| window == needle) 44 } 45 46 impl ZkBinary { 47 pub fn decode(bytes: &[u8]) -> Result<Self> { 48 // Ensure that bytes is a certain minimum length. Otherwise the code 49 // below will panic due to an index out of bounds error. 50 if bytes.len() < MIN_BIN_SIZE { 51 return Err(ZkasErr("Not enough bytes".to_string())) 52 } 53 let magic_bytes = &bytes[0..4]; 54 if magic_bytes != MAGIC_BYTES { 55 return Err(ZkasErr("Magic bytes are incorrect".to_string())) 56 } 57 58 let _binary_version = &bytes[4]; 59 60 // Deserialize the k param 61 let (k, _): (u32, _) = deserialize_partial(&bytes[5..9])?; 62 63 // For now, we'll limit k. 64 if k > MAX_K { 65 return Err(ZkasErr("k param is too high, max allowed is 16".to_string())) 66 } 67 68 // After the binary version and k, we're supposed to have the witness namespace 69 let (namespace, _): (String, _) = deserialize_partial(&bytes[9..])?; 70 71 // Enforce a limit on the namespace string length 72 if namespace.len() > MAX_NS_LEN { 73 return Err(ZkasErr("Namespace too long".to_string())) 74 } 75 76 let constants_offset = match find_subslice(bytes, b".constant") { 77 Some(v) => v, 78 None => return Err(ZkasErr("Could not find .constant section".to_string())), 79 }; 80 81 let literals_offset = match find_subslice(bytes, b".literal") { 82 Some(v) => v, 83 None => return Err(ZkasErr("Could not find .literal section".to_string())), 84 }; 85 86 let witness_offset = match find_subslice(bytes, b".witness") { 87 Some(v) => v, 88 None => return Err(ZkasErr("Could not find .witness section".to_string())), 89 }; 90 91 let circuit_offset = match find_subslice(bytes, b".circuit") { 92 Some(v) => v, 93 None => return Err(ZkasErr("Could not find .circuit section".to_string())), 94 }; 95 96 let debug_offset = match find_subslice(bytes, b".debug") { 97 Some(v) => v, 98 None => bytes.len(), 99 }; 100 101 if constants_offset > literals_offset { 102 return Err(ZkasErr(".literal section appeared before .constant".to_string())) 103 } 104 105 if literals_offset > witness_offset { 106 return Err(ZkasErr(".witness section appeared before .literal".to_string())) 107 } 108 109 if witness_offset > circuit_offset { 110 return Err(ZkasErr(".circuit section appeared before .witness".to_string())) 111 } 112 113 if circuit_offset > debug_offset { 114 return Err(ZkasErr(".debug section appeared before .circuit or EOF".to_string())) 115 } 116 117 let constants_section = &bytes[constants_offset + b".constant".len()..literals_offset]; 118 let literals_section = &bytes[literals_offset + b".literal".len()..witness_offset]; 119 let witness_section = &bytes[witness_offset + b".witness".len()..circuit_offset]; 120 let circuit_section = &bytes[circuit_offset + b".circuit".len()..debug_offset]; 121 122 let constants = ZkBinary::parse_constants(constants_section)?; 123 let literals = ZkBinary::parse_literals(literals_section)?; 124 let witnesses = ZkBinary::parse_witness(witness_section)?; 125 let opcodes = ZkBinary::parse_circuit(circuit_section)?; 126 127 // TODO: Debug info 128 129 Ok(Self { namespace, k, constants, literals, witnesses, opcodes }) 130 } 131 132 fn parse_constants(bytes: &[u8]) -> Result<Vec<(VarType, String)>> { 133 let mut constants = vec![]; 134 135 let mut iter_offset = 0; 136 while iter_offset < bytes.len() { 137 let c_type = match VarType::from_repr(bytes[iter_offset]) { 138 Some(v) => v, 139 None => { 140 return Err(ZkasErr(format!( 141 "Could not decode constant VarType from {}", 142 bytes[iter_offset], 143 ))) 144 } 145 }; 146 iter_offset += 1; 147 let (name, offset) = deserialize_partial::<String>(&bytes[iter_offset..])?; 148 iter_offset += offset; 149 150 constants.push((c_type, name)); 151 } 152 153 Ok(constants) 154 } 155 156 fn parse_literals(bytes: &[u8]) -> Result<Vec<(LitType, String)>> { 157 let mut literals = vec![]; 158 159 let mut iter_offset = 0; 160 while iter_offset < bytes.len() { 161 let l_type = match LitType::from_repr(bytes[iter_offset]) { 162 Some(v) => v, 163 None => { 164 return Err(ZkasErr(format!( 165 "Could not decode literal LitType from {}", 166 bytes[iter_offset], 167 ))) 168 } 169 }; 170 iter_offset += 1; 171 let (name, offset) = deserialize_partial::<String>(&bytes[iter_offset..])?; 172 iter_offset += offset; 173 174 literals.push((l_type, name)); 175 } 176 177 Ok(literals) 178 } 179 180 fn parse_witness(bytes: &[u8]) -> Result<Vec<VarType>> { 181 let mut witnesses = vec![]; 182 183 let mut iter_offset = 0; 184 while iter_offset < bytes.len() { 185 let w_type = match VarType::from_repr(bytes[iter_offset]) { 186 Some(v) => v, 187 None => { 188 return Err(ZkasErr(format!( 189 "Could not decode witness VarType from {}", 190 bytes[iter_offset], 191 ))) 192 } 193 }; 194 195 iter_offset += 1; 196 197 witnesses.push(w_type); 198 } 199 200 Ok(witnesses) 201 } 202 203 #[allow(clippy::type_complexity)] 204 fn parse_circuit(bytes: &[u8]) -> Result<Vec<(Opcode, Vec<(HeapType, usize)>)>> { 205 let mut opcodes = vec![]; 206 207 let mut iter_offset = 0; 208 while iter_offset < bytes.len() { 209 let opcode = match Opcode::from_repr(bytes[iter_offset]) { 210 Some(v) => v, 211 None => { 212 return Err(ZkasErr(format!( 213 "Could not decode Opcode from {}", 214 bytes[iter_offset] 215 ))) 216 } 217 }; 218 iter_offset += 1; 219 220 // TODO: Check that the types and arg number are correct 221 222 let (arg_num, offset) = deserialize_partial::<VarInt>(&bytes[iter_offset..])?; 223 iter_offset += offset; 224 225 let mut args = vec![]; 226 for _ in 0..arg_num.0 { 227 // Check bounds each time bytes[iter_offset] is accessed to prevent panics. 228 if iter_offset >= bytes.len() { 229 return Err(ZkasErr(format!( 230 "Bad offset for circuit: offset {} is >= circuit length {}", 231 iter_offset, 232 bytes.len() 233 ))) 234 } 235 let heap_type = bytes[iter_offset]; 236 iter_offset += 1; 237 238 if iter_offset >= bytes.len() { 239 return Err(ZkasErr(format!( 240 "Bad offset for circuit: offset {} is >= circuit length {}", 241 iter_offset, 242 bytes.len() 243 ))) 244 } 245 let (heap_index, offset) = deserialize_partial::<VarInt>(&bytes[iter_offset..])?; 246 iter_offset += offset; 247 let heap_type = match HeapType::from_repr(heap_type) { 248 Some(v) => v, 249 None => { 250 return Err(ZkasErr(format!("Could not decode HeapType from {heap_type}"))) 251 } 252 }; 253 args.push((heap_type, heap_index.0 as usize)); 254 } 255 256 opcodes.push((opcode, args)); 257 } 258 259 Ok(opcodes) 260 } 261 } 262 263 #[cfg(test)] 264 mod tests { 265 use crate::zkas::ZkBinary; 266 267 #[test] 268 fn panic_regression_001() { 269 // Out-of-memory panic from string deserialization. 270 // Read `doc/src/zkas/bincode.md` to understand the input. 271 let data = vec![11u8, 1, 177, 53, 1, 0, 0, 0, 0, 255, 0, 204, 200, 72, 72, 72, 72, 1]; 272 let _dec = ZkBinary::decode(&data); 273 } 274 275 #[test] 276 fn panic_regression_002() { 277 // Index out of bounds panic in parse_circuit(). 278 // Read `doc/src/zkas/bincode.md` to understand the input. 279 let data = vec![ 280 11u8, 1, 177, 53, 2, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 83, 105, 281 109, 112, 108, 101, 46, 99, 111, 110, 115, 116, 97, 110, 116, 3, 18, 86, 65, 76, 85, 282 69, 95, 67, 79, 77, 77, 73, 84, 95, 86, 65, 76, 85, 69, 2, 19, 86, 65, 76, 85, 69, 95, 283 67, 79, 77, 77, 73, 84, 95, 82, 65, 77, 68, 79, 77, 46, 108, 105, 116, 101, 114, 97, 284 108, 46, 119, 105, 116, 110, 101, 115, 115, 16, 18, 46, 99, 105, 114, 99, 117, 105, 285 116, 4, 2, 0, 2, 0, 0, 2, 2, 0, 3, 0, 1, 8, 2, 0, 4, 0, 5, 8, 1, 0, 6, 9, 1, 0, 6, 240, 286 1, 0, 7, 240, 41, 0, 0, 0, 1, 0, 8, 287 ]; 288 let _dec = ZkBinary::decode(&data); 289 } 290 }