diff --git a/bin/zkas/src/main.rs b/bin/zkas/src/main.rs index 23c4fa607..2682922c5 100644 --- a/bin/zkas/src/main.rs +++ b/bin/zkas/src/main.rs @@ -63,7 +63,7 @@ fn main() { // the initial AST, not caring much about the semantics, just enforcing // syntax and general structure. let parser = Parser::new(filename, source.chars(), tokens); - let (constants, witnesses, statements) = parser.parse(); + let (namespace, constants, witnesses, statements) = parser.parse(); // The analyzer goes through the initial AST provided by the parser and // converts return and variable types to their correct forms, and also @@ -86,6 +86,7 @@ fn main() { let compiler = Compiler::new( filename, source.chars(), + namespace, analyzer.constants, analyzer.witnesses, analyzer.statements, diff --git a/doc/src/zkas/bincode.md b/doc/src/zkas/bincode.md index 6450cfec7..7db249e7b 100644 --- a/doc/src/zkas/bincode.md +++ b/doc/src/zkas/bincode.md @@ -18,6 +18,7 @@ The compiled binary blob has the following layout: ``` MAGIC_BYTES BINARY_VERSION +NAMESPACE .constant CONSTANT_TYPE CONSTANT_NAME CONSTANT_TYPE CONSTANT_NAME @@ -39,8 +40,8 @@ TBD ``` Integers in the binary are encoded using variable-integer encoding. -See [`serial.rs`](https://github.com/darkrenaissance/darkfi/blob/master/src/util/serial.rs) -for our Rust implementation. +See the [`serial`](https://github.com/darkrenaissance/darkfi/blob/master/src/serial/src/lib.rs) +crate and module for our Rust implementation. ## Sections @@ -59,6 +60,20 @@ potential different formats in the future. > `0x02` +### `NAMESPACE` + +This sector after `MAGIC_BYTES` and `BINARY_VERSION` contains the +reference namespace of the code. This is the namespace used in the +source code, e.g.: + +``` +constant "MyNamespace" { ... } +contract "MyNamespace" { ... } +circuit "MyNamespace" { ... } +``` + +The string is serialized with variable-integer encoding. + ### `.constant` The constants in the `.constant` section are declared with their type diff --git a/src/zkas/compiler.rs b/src/zkas/compiler.rs index baa32e73f..2d4bd0564 100644 --- a/src/zkas/compiler.rs +++ b/src/zkas/compiler.rs @@ -14,6 +14,7 @@ pub const BINARY_VERSION: u8 = 2; pub const MAGIC_BYTES: [u8; 4] = [0x0b, 0x01, 0xb1, 0x35]; pub struct Compiler { + namespace: String, constants: Vec, witnesses: Vec, statements: Vec, @@ -26,6 +27,7 @@ impl Compiler { pub fn new( filename: &str, source: Chars, + namespace: String, constants: Vec, witnesses: Vec, statements: Vec, @@ -37,7 +39,7 @@ impl Compiler { let lines: Vec = source.as_str().lines().map(|x| x.to_string()).collect(); let error = ErrorEmitter::new("Compiler", filename, lines); - Self { constants, witnesses, statements, literals, debug_info, error } + Self { namespace, constants, witnesses, statements, literals, debug_info, error } } pub fn compile(&self) -> Vec { @@ -47,6 +49,9 @@ impl Compiler { bincode.extend_from_slice(&MAGIC_BYTES); bincode.push(BINARY_VERSION); + // Write the circuit's namespace + bincode.extend_from_slice(&serialize(&self.namespace)); + // Temporaty stack vector for lookups let mut tmp_stack = vec![]; diff --git a/src/zkas/decoder.rs b/src/zkas/decoder.rs index 75fa0d6ab..d0a4dffea 100644 --- a/src/zkas/decoder.rs +++ b/src/zkas/decoder.rs @@ -7,6 +7,7 @@ use crate::{Error::ZkasDecoderError as ZkasErr, Result}; /// This is used by the zkvm. #[derive(Clone, Debug)] pub struct ZkBinary { + pub namespace: String, pub constants: Vec<(VarType, String)>, pub literals: Vec<(LitType, String)>, pub witnesses: Vec, @@ -27,6 +28,9 @@ impl ZkBinary { let _binary_version = &bytes[4]; + // After the binary version, we're supposed to have the contract namespace + let (namespace, _) = deserialize_partial(&bytes[5..])?; + let constants_offset = match find_subslice(bytes, b".constant") { Some(v) => v, None => return Err(ZkasErr("Could not find .constant section".to_string())), @@ -77,9 +81,10 @@ impl ZkBinary { let literals = ZkBinary::parse_literals(literals_section)?; let witnesses = ZkBinary::parse_contract(contract_section)?; let opcodes = ZkBinary::parse_circuit(circuit_section)?; + // TODO: Debug info - Ok(Self { constants, literals, witnesses, opcodes }) + Ok(Self { namespace, constants, literals, witnesses, opcodes }) } fn parse_constants(bytes: &[u8]) -> Result> { diff --git a/src/zkas/parser.rs b/src/zkas/parser.rs index ac2afcc46..9cb7901f5 100644 --- a/src/zkas/parser.rs +++ b/src/zkas/parser.rs @@ -14,6 +14,9 @@ use super::{ /// These can not be used anywhere except where they are expected. const KEYWORDS: [&str; 3] = ["constant", "contract", "circuit"]; +/// Forbidden namespaces +const NOPE_NS: [&str; 4] = [".constant", ".literal", ".contract", ".circuit"]; + /// Valid EcFixedPoint constant names supported by the VM. const VALID_ECFIXEDPOINT: [&str; 1] = ["VALUE_COMMIT_RANDOM"]; @@ -38,7 +41,7 @@ impl Parser { Self { tokens, error } } - pub fn parse(&self) -> (Vec, Vec, Vec) { + pub fn parse(&self) -> (String, Vec, Vec, Vec) { // We use these to keep state while parsing. let mut namespace = None; let (mut declaring_constant, mut declared_constant) = (false, false); @@ -133,6 +136,13 @@ impl Parser { ); } } else { + if NOPE_NS.contains(&$t[0].token.as_str()) { + self.error.abort( + &format!("'{}' cannot be a namespace.", $t[0].token), + $t[0].line, + $t[0].column, + ); + } namespace = Some($t[0].token.clone()); } }; @@ -273,7 +283,7 @@ impl Parser { self.error.abort("Circuit section is empty.", 0, 0); } - (constants, witnesses, statements) + (ns, constants, witnesses, statements) } fn check_section_structure(&self, section: &str, tokens: Vec) {