Explorar o código

added the Supervisor which gives us VM serialization, and added a plugin to the compiler to export binary data for the supervisor

narodnik %!s(int64=5) %!d(string=hai) anos
pai
achega
d0a30306e5
Modificáronse 14 ficheiros con 1602 adicións e 11 borrados
  1. 16 0
      Cargo.toml
  2. 6 0
      run_mint3.sh
  3. 14 0
      scripts/vm.py
  4. 161 0
      scripts/vm_export_supervisor.py
  5. 81 0
      src/bin/mint.rs
  6. 54 0
      src/bls_extensions.rs
  7. 137 0
      src/endian.rs
  8. 64 0
      src/error.rs
  9. 75 0
      src/lib.rs
  10. 3 6
      src/mint2.rs
  11. 768 0
      src/serial.rs
  12. 7 5
      src/vm.rs
  13. 214 0
      src/vm_serial.rs
  14. 2 0
      src/vmtest.rs

+ 16 - 0
Cargo.toml

@@ -6,6 +6,9 @@ edition = "2018"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
+[lib]
+name = "sapvi"
+
 [dependencies]
 ff = "0.8"
 group = "0.8"
@@ -26,6 +29,11 @@ bitvec = "0.18"
 
 hex = "0.4.2"
 
+simplelog = "0.7.4"
+clap = "3.0.0-beta.1"
+failure = "0.1.8"
+failure_derive = "0.1.8"
+
 [[bin]]
 name = "sha256"
 path = "src/sha256.rs"
@@ -86,3 +94,11 @@ path = "src/zkmimc.rs"
 name = "mint2"
 path = "src/mint2.rs"
 
+[[bin]]
+name = "zkvm"
+path = "src/zkvm.rs"
+
+[[bin]]
+name = "mint3"
+path = "src/bin/mint.rs"
+

+ 6 - 0
run_mint3.sh

@@ -0,0 +1,6 @@
+#!/bin/bash -x
+python scripts/preprocess.py proofs/mint2.psm > /tmp/mint2.psm || exit $?
+#python scripts/preprocess.py proofs/jubjub.pism > /tmp/mint2.psm || exit $?
+python scripts/vm.py --supervisor /tmp/mint2.psm --output mint.zcd || exit $?
+cargo run --release --bin mint3
+

+ 14 - 0
scripts/vm.py

@@ -347,6 +347,12 @@ class Contract:
                 repr_str += "    // %s\n" % constraint.args_comment()
             repr_str += "    %s %s\n" % (constraint.command, constraint.args)
 
+        repr_str += "Stats:\n"
+        repr_str += "    Constants: %s\n" % len(self.constants)
+        repr_str += "    Alloc: %s\n" % len(self.alloc)
+        repr_str += "    Operations: %s\n" % len(self.ops)
+        repr_str += "    Constraint Instructions: %s\n" % len(self.constraints)
+
         return repr_str
 
 def compile(contract, constants):
@@ -412,11 +418,15 @@ def process(contents):
 def main(argv):
     parser = argparse.ArgumentParser()
     parser.add_argument("filename", help="VM PISM file: proofs/vm.pism")
+    parser.add_argument("--output", type=argparse.FileType('wb', 0),
+                        default=sys.stdout.buffer, help="Output file")
     group = parser.add_mutually_exclusive_group()
     group.add_argument('--display', action='store_true',
                        help="show the compiled code in human readable format")
     group.add_argument('--rust', action='store_true',
                        help="output compiled code to rust for testing")
+    group.add_argument('--supervisor', action='store_true',
+                       help="output compiled code to zkvm supervisor")
     args = parser.parse_args()
 
     src_filename = args.filename
@@ -435,6 +445,10 @@ def main(argv):
         import vm_export_rust
         for contract_name, contract in contracts.items():
             vm_export_rust.display(contract)
+    elif args.supervisor:
+        import vm_export_supervisor
+        for contract_name, contract in contracts.items():
+            vm_export_supervisor.export(args.output, contract_name, contract)
     else:
         default_display()
 

+ 161 - 0
scripts/vm_export_supervisor.py

@@ -0,0 +1,161 @@
+import struct
+from vm import VariableType, VariableRefType
+
+class Operation:
+
+    def __init__(self, ident, args):
+        self.ident = ident
+        self.args = args
+
+class ArgVarRef:
+
+    def __init__(self, type, index):
+        self.type = type
+        self.index = index
+
+    def bytes(self):
+        return struct.pack("<BI", self.type, self.index)
+
+class ArgVarIndex:
+
+    def __init__(self, _, index):
+        self.index = index
+
+    def bytes(self):
+        return struct.pack("<I", self.index)
+
+class ArgString:
+
+    def __init__(self, description, index):
+        self.description = description
+        self.index = index
+
+ops_table = {
+        "set": Operation(0, [ArgVarRef, ArgVarRef]),
+        "mul": Operation(1, [ArgVarRef, ArgVarRef]),
+        "add": Operation(2, [ArgVarRef, ArgVarRef]),
+        "sub": Operation(3, [ArgVarRef, ArgVarRef]),
+        "divide": Operation(4, [ArgVarRef, ArgVarRef]),
+        "double": Operation(5, [ArgVarRef]),
+        "square": Operation(6, [ArgVarRef]),
+        "invert": Operation(7, [ArgVarRef]),
+        "unpack_bits": Operation(8, [ArgVarRef, ArgVarRef, ArgVarRef]),
+        "local": Operation(9, []),
+        "load": Operation(10, [ArgVarRef, ArgVarIndex]),
+        "debug": Operation(11, [ArgString, ArgVarRef]),
+        "dump_alloc": Operation(12, []),
+        "dump_local": Operation(13, []),
+}
+
+constraint_ident_map = {
+    "lc0_add": 0,
+    "lc1_add": 1,
+    "lc2_add": 2,
+    "lc0_sub": 3,
+    "lc1_sub": 4,
+    "lc2_sub": 5,
+    "lc0_add_one": 6,
+    "lc1_add_one": 7,
+    "lc2_add_one": 8,
+    "lc0_sub_one": 9,
+    "lc1_sub_one": 10,
+    "lc2_sub_one": 11,
+    "lc0_add_coeff": 12,
+    "lc1_add_coeff": 13,
+    "lc2_add_coeff": 14,
+    "lc0_add_one_coeff": 15,
+    "lc1_add_one_coeff": 16,
+    "lc2_add_one_coeff": 17,
+    "enforce": 18,
+    "lc_coeff_reset": 19,
+    "lc_coeff_double": 20,
+}
+
+def varuint(value):
+    if value <= 0xfc:
+        return struct.pack("<B", value)
+    elif value <= 0xffff:
+        return struct.pack("<BH", 0xfd, value)
+    elif value <= 0xffffffff:
+        return struct.pack("<BI", 0xfe, value)
+    else:
+        return struct.pack("<BQ", 0xff, value)
+
+def export(output, contract_name, contract):
+    output.write(varuint(len(contract_name)))
+    output.write(contract_name.encode())
+
+    constants = list(contract.constants.items())
+    constants.sort(key=lambda obj: obj[1][0])
+    constants = [(obj[0], obj[1][1]) for obj in constants]
+
+    output.write(varuint(len(constants)))
+    for symbol, value in constants:
+        print("Constant '%s' = %s" % (symbol, value))
+        # Bellman uses little endian for Scalars from_bytes function
+        const_bytes = bytearray.fromhex(value)[::-1]
+        assert len(const_bytes) == 32
+        output.write(const_bytes)
+
+    output.write(varuint(len(contract.alloc)))
+    for symbol, variable in contract.alloc.items():
+        print("Alloc '%s' = (%s, %s)" % (symbol, 
+                                         variable.type.name, variable.index))
+        if variable.type.name == VariableType.PRIVATE.name:
+            typeval = 0
+        elif variable.type.name == VariableType.PUBLIC.name:
+            typeval = 1
+        else:
+            assert False
+        alloc_bytes = struct.pack("<BI", typeval, variable.index)
+        assert len(alloc_bytes) == 5
+        output.write(alloc_bytes)
+
+    output.write(varuint(len(contract.ops)))
+    for op in contract.ops:
+        op_form = ops_table[op.command]
+        output.write(struct.pack("B", op_form.ident))
+
+        if op.command == "debug":
+            # Special case
+            assert len(op.args) == 1
+            line_str = str(op.line).encode()
+            output.write(varuint(len(line_str)))
+            output.write(line_str)
+
+            op_arg = op.args[0]
+            if op_arg.type.name == VariableRefType.AUX.name:
+                arg_type = 0
+            elif op_arg.type.name == VariableRefType.LOCAL.name:
+                arg_type = 1
+            arg = ArgVarRef(arg_type, op_arg.index)
+            output.write(arg.bytes())
+            continue
+
+        assert len(op_form.args) == len(op.args)
+        for arg_form, op_arg in zip(op_form.args, op.args):
+            if op_arg.type.name == VariableRefType.AUX.name:
+                arg_type = 0
+            elif op_arg.type.name == VariableRefType.LOCAL.name:
+                arg_type = 1
+            arg = arg_form(arg_type, op_arg.index)
+            output.write(arg.bytes())
+        print("Operation", op.command,
+              [(arg.type.name, arg.index) for arg in op.args])
+
+    output.write(varuint(len(contract.constraints)))
+    for constraint in contract.constraints:
+        args = constraint.args[:]
+        if (constraint.command == "lc0_add_coeff" or
+            constraint.command == "lc1_add_coeff" or
+            constraint.command == "lc2_add_coeff" or
+            constraint.command == "lc0_add_one_coeff" or
+            constraint.command == "lc1_add_one_coeff" or
+            constraint.command == "lc2_add_one_coeff"):
+            args[0] = args[0][0]
+        print("Constraint", constraint.command, args)
+        enum_ident = constraint_ident_map[constraint.command]
+        output.write(struct.pack("B", enum_ident))
+        for arg in args:
+            output.write(struct.pack("<I", arg))
+

+ 81 - 0
src/bin/mint.rs

@@ -0,0 +1,81 @@
+use sapvi::{Decodable, ZKSupervisor};
+use std::fs::File;
+use std::time::Instant;
+
+use bls12_381::Scalar;
+use ff::{Field, PrimeField};
+use group::{Curve, Group, GroupEncoding};
+use rand::rngs::OsRng;
+
+type Result<T> = std::result::Result<T, failure::Error>;
+
+fn main() -> Result<()> {
+    let start = Instant::now();
+    let file = File::open("mint.zcd")?;
+    let mut visor = ZKSupervisor::decode(file)?;
+    println!("{}", visor.name);
+    //ZKSupervisor::load_contract(bytes);
+    println!("Finished: [{:?}]", start.elapsed());
+
+    println!("Stats:");
+    println!("    Constants: {}", visor.vm.constants.len());
+    println!("    Alloc: {}", visor.vm.alloc.len());
+    println!("    Operations: {}", visor.vm.ops.len());
+    println!("    Constraint Instructions: {}", visor.vm.constraints.len());
+
+    visor.vm.setup();
+
+    let params = vec![
+        (
+            0,
+            Scalar::from_raw([
+                0xb981_9dc8_2d90_607e,
+                0xa361_ee3f_d48f_df77,
+                0x52a3_5a8c_1908_dd87,
+                0x15a3_6d1f_0f39_0d88,
+            ]),
+        ),
+        (
+            1,
+            Scalar::from_raw([
+                0x7b0d_c53c_4ebf_1891,
+                0x1f3a_beeb_98fa_d3e8,
+                0xf789_1142_c001_d925,
+                0x015d_8c7f_5b43_fe33,
+            ]),
+        ),
+        (
+            2,
+            Scalar::from_raw([
+                0xb981_9dc8_2d90_607e,
+                0xa361_ee3f_d48f_df77,
+                0x52a3_5a8c_1908_dd87,
+                0x15a3_6d1f_0f39_0d88,
+            ]),
+        ),
+        (
+            3,
+            Scalar::from_raw([
+                0x7b0d_c53c_4ebf_1891,
+                0x1f3a_beeb_98fa_d3e8,
+                0xf789_1142_c001_d925,
+                0x015d_8c7f_5b43_fe33,
+            ]),
+        ),
+    ];
+    visor.vm.initialize(&params);
+
+    let proof = visor.vm.prove();
+
+    let public = visor.vm.public();
+
+    assert_eq!(public.len(), 2);
+    // 0x66ced46f14e5616d12b993f60a6e66558d6b6afe4c321ed212e0b9cfbd81061a
+    // 0x4731570fdd57cf280eadc8946fa00df81112502e44e497e794ab9a221f1bcca
+    println!("u = {:?}", public[0]);
+    println!("v = {:?}", public[1]);
+
+    assert!(visor.vm.verify(&proof, &public));
+
+    Ok(())
+}

+ 54 - 0
src/bls_extensions.rs

@@ -0,0 +1,54 @@
+use bls12_381 as bls;
+use rand_core::{OsRng, RngCore};
+use std::io;
+
+use crate::error::{Error, Result};
+use crate::serial::{Decodable, Encodable, ReadExt, WriteExt};
+
+macro_rules! serialization_bls {
+    ($type:ty, $to_x:ident, $from_x:ident, $size:literal) => {
+        impl Encodable for $type {
+            fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+                let data = self.$to_x();
+                assert_eq!(data.len(), $size);
+                s.write_slice(&data)?;
+                Ok(data.len())
+            }
+        }
+
+        impl Decodable for $type {
+            fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+                let mut slice = [0u8; $size];
+                d.read_slice(&mut slice)?;
+                let result = Self::$from_x(&slice);
+                if bool::from(result.is_none()) {
+                    return Err(Error::ParseFailed("$t conversion from slice failed"));
+                }
+                Ok(result.unwrap())
+            }
+        }
+    };
+}
+
+serialization_bls!(bls::Scalar, to_bytes, from_bytes, 32);
+
+macro_rules! make_serialize_deserialize_test {
+    ($name:ident, $type:ty, $default_func:ident) => {
+        #[test]
+        fn $name() {
+            let point = <$type>::$default_func();
+
+            let mut data: Vec<u8> = vec![];
+            let result = point.encode(&mut data);
+            assert!(result.is_ok());
+
+            let point2 = <$type>::decode(&data[..]);
+            assert!(point2.is_ok());
+            let point2 = point2.unwrap();
+
+            assert_eq!(point, point2);
+        }
+    };
+}
+
+make_serialize_deserialize_test!(serial_test_scalar, bls::Scalar, zero);

+ 137 - 0
src/endian.rs

@@ -0,0 +1,137 @@
+macro_rules! define_slice_to_be {
+    ($name: ident, $type: ty) => {
+        #[inline]
+        pub fn $name(slice: &[u8]) -> $type {
+            assert_eq!(slice.len(), ::std::mem::size_of::<$type>());
+            let mut res = 0;
+            for i in 0..::std::mem::size_of::<$type>() {
+                res |= (slice[i] as $type) << (::std::mem::size_of::<$type>() - i - 1) * 8;
+            }
+            res
+        }
+    };
+}
+macro_rules! define_slice_to_le {
+    ($name: ident, $type: ty) => {
+        #[inline]
+        pub fn $name(slice: &[u8]) -> $type {
+            assert_eq!(slice.len(), ::std::mem::size_of::<$type>());
+            let mut res = 0;
+            for i in 0..::std::mem::size_of::<$type>() {
+                res |= (slice[i] as $type) << i * 8;
+            }
+            res
+        }
+    };
+}
+macro_rules! define_be_to_array {
+    ($name: ident, $type: ty, $byte_len: expr) => {
+        #[inline]
+        pub fn $name(val: $type) -> [u8; $byte_len] {
+            assert_eq!(::std::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22
+            let mut res = [0; $byte_len];
+            for i in 0..$byte_len {
+                res[i] = ((val >> ($byte_len - i - 1) * 8) & 0xff) as u8;
+            }
+            res
+        }
+    };
+}
+macro_rules! define_le_to_array {
+    ($name: ident, $type: ty, $byte_len: expr) => {
+        #[inline]
+        pub fn $name(val: $type) -> [u8; $byte_len] {
+            assert_eq!(::std::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22
+            let mut res = [0; $byte_len];
+            for i in 0..$byte_len {
+                res[i] = ((val >> i * 8) & 0xff) as u8;
+            }
+            res
+        }
+    };
+}
+
+define_slice_to_be!(slice_to_u32_be, u32);
+define_be_to_array!(u32_to_array_be, u32, 4);
+define_slice_to_le!(slice_to_u16_le, u16);
+define_slice_to_le!(slice_to_u32_le, u32);
+define_slice_to_le!(slice_to_u64_le, u64);
+define_le_to_array!(u16_to_array_le, u16, 2);
+define_le_to_array!(u32_to_array_le, u32, 4);
+define_le_to_array!(u64_to_array_le, u64, 8);
+
+#[inline]
+pub fn i16_to_array_le(val: i16) -> [u8; 2] {
+    u16_to_array_le(val as u16)
+}
+#[inline]
+pub fn slice_to_i16_le(slice: &[u8]) -> i16 {
+    slice_to_u16_le(slice) as i16
+}
+#[inline]
+pub fn slice_to_i32_le(slice: &[u8]) -> i32 {
+    slice_to_u32_le(slice) as i32
+}
+#[inline]
+pub fn i32_to_array_le(val: i32) -> [u8; 4] {
+    u32_to_array_le(val as u32)
+}
+#[inline]
+pub fn slice_to_i64_le(slice: &[u8]) -> i64 {
+    slice_to_u64_le(slice) as i64
+}
+#[inline]
+pub fn i64_to_array_le(val: i64) -> [u8; 8] {
+    u64_to_array_le(val as u64)
+}
+
+macro_rules! define_chunk_slice_to_int {
+    ($name: ident, $type: ty, $converter: ident) => {
+        #[inline]
+        pub fn $name(inp: &[u8], outp: &mut [$type]) {
+            assert_eq!(inp.len(), outp.len() * ::std::mem::size_of::<$type>());
+            for (outp_val, data_bytes) in outp
+                .iter_mut()
+                .zip(inp.chunks(::std::mem::size_of::<$type>()))
+            {
+                *outp_val = $converter(data_bytes);
+            }
+        }
+    };
+}
+define_chunk_slice_to_int!(bytes_to_u64_slice_le, u64, slice_to_u64_le);
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn endianness_test() {
+        assert_eq!(slice_to_u32_be(&[0xde, 0xad, 0xbe, 0xef]), 0xdeadbeef);
+        assert_eq!(u32_to_array_be(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]);
+
+        assert_eq!(slice_to_u16_le(&[0xad, 0xde]), 0xdead);
+        assert_eq!(slice_to_u32_le(&[0xef, 0xbe, 0xad, 0xde]), 0xdeadbeef);
+        assert_eq!(
+            slice_to_u64_le(&[0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]),
+            0x1badcafedeadbeef
+        );
+        assert_eq!(u16_to_array_le(0xdead), [0xad, 0xde]);
+        assert_eq!(u32_to_array_le(0xdeadbeef), [0xef, 0xbe, 0xad, 0xde]);
+        assert_eq!(
+            u64_to_array_le(0x1badcafedeadbeef),
+            [0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]
+        );
+    }
+
+    #[test]
+    fn endian_chunk_test() {
+        let inp = [
+            0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b, 0xfe, 0xca, 0xad, 0x1b, 0xce, 0xfa,
+            0x01, 0x02,
+        ];
+        let mut out = [0; 2];
+        bytes_to_u64_slice_le(&inp, &mut out);
+        assert_eq!(out, [0x1badcafedeadbeef, 0x0201face1badcafe]);
+    }
+}

+ 64 - 0
src/error.rs

@@ -0,0 +1,64 @@
+use std::fmt;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+pub enum Error {
+    Foo,
+    CommitsDontAdd,
+    InvalidCredential,
+    TransactionPedersenCheckFailed,
+    TokenAlreadySpent,
+    InputTokenVerifyFailed,
+    RangeproofPedersenMatchFailed,
+    ProofsFailed,
+    MissingProofs,
+    Io(std::io::Error),
+    /// VarInt was encoded in a non-minimal way
+    NonMinimalVarInt,
+    /// Parsing error
+    ParseFailed(&'static str),
+    AsyncChannelError,
+    MalformedPacket,
+    AddrParseError,
+    BadVariableRefType,
+    BadOperationType,
+    BadConstraintType,
+}
+
+impl std::error::Error for Error {}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
+        match *self {
+            Error::Foo => f.write_str("foo"),
+            Error::CommitsDontAdd => f.write_str("Commits don't add up properly"),
+            Error::InvalidCredential => f.write_str("Credential is invalid"),
+            Error::TransactionPedersenCheckFailed => {
+                f.write_str("Transaction pedersens for input and output don't sum up")
+            }
+            Error::TokenAlreadySpent => f.write_str("This input token is already spent"),
+            Error::InputTokenVerifyFailed => f.write_str("Input token verify of credential failed"),
+            Error::RangeproofPedersenMatchFailed => {
+                f.write_str("Rangeproof pedersen check for match failed")
+            }
+            Error::ProofsFailed => f.write_str("Proof validation failed"),
+            Error::MissingProofs => f.write_str("Missing proofs"),
+            Error::Io(ref err) => fmt::Display::fmt(err, f),
+            Error::NonMinimalVarInt => f.write_str("non-minimal varint"),
+            Error::ParseFailed(ref err) => write!(f, "parse failed: {}", err),
+            Error::AsyncChannelError => f.write_str("async_channel error"),
+            Error::MalformedPacket => f.write_str("Malformed packet"),
+            Error::AddrParseError => f.write_str("Unable to parse address"),
+            Error::BadVariableRefType => f.write_str("Bad variable ref type byte"),
+            Error::BadOperationType => f.write_str("Bad operation type byte"),
+            Error::BadConstraintType => f.write_str("Bad constraint type byte"),
+        }
+    }
+}
+
+impl From<std::io::Error> for Error {
+    fn from(err: std::io::Error) -> Error {
+        Error::Io(err)
+    }
+}

+ 75 - 0
src/lib.rs

@@ -0,0 +1,75 @@
+use bls12_381::Scalar;
+use std::collections::HashMap;
+
+pub mod bls_extensions;
+pub mod endian;
+pub mod error;
+pub mod serial;
+pub mod vm;
+pub mod vm_serial;
+
+pub use crate::serial::{Decodable, Encodable};
+pub use crate::vm::{
+    AllocType, ConstraintInstruction, CryptoOperation, VariableIndex, VariableRef, ZKVMCircuit,
+    ZKVirtualMachine,
+};
+
+pub type Bytes = Vec<u8>;
+
+pub struct ZKSupervisor {
+    pub name: String,
+    pub vm: ZKVirtualMachine,
+    params_map: HashMap<String, VariableIndex>,
+    params: HashMap<VariableIndex, Scalar>,
+    public_map: HashMap<String, VariableIndex>,
+}
+
+struct ZKProof {
+    public_values: HashMap<String, Scalar>,
+    //proof:
+}
+
+impl ZKSupervisor {
+    // Just have a load() and save()
+    // Load the contract, do the setup, save it...
+
+    pub fn load_contract(bytes: Bytes) -> Self {
+        Self {
+            name: "".to_string(),
+            vm: ZKVirtualMachine {
+                ops: Vec::new(),
+                aux: Vec::new(),
+                alloc: Vec::new(),
+                constraints: Vec::new(),
+                params: None,
+                verifying_key: None,
+                constants: Vec::new(),
+            },
+            params_map: HashMap::new(),
+            params: HashMap::new(),
+            public_map: HashMap::new(),
+        }
+    }
+
+    fn setup(&self) {}
+    fn save_setup(&self) {}
+
+    fn load_setup(&self) {}
+
+    fn param_names(&self) -> Vec<String> {
+        self.params_map.keys().cloned().collect()
+    }
+    fn set_param(&self, name: &str, value: Scalar) {}
+
+    fn prove(&self) {
+        // error if params not all set
+
+        // execute
+        // prove
+        // return proof and public values (Hashmap string -> scalars)
+    }
+    fn verify(&self) {
+        // takes proof and public values
+    }
+}
+

+ 3 - 6
src/mint2.rs

@@ -1,5 +1,5 @@
 use bls12_381::Scalar;
-use ff::{PrimeField, Field};
+use ff::{Field, PrimeField};
 use group::{Curve, Group, GroupEncoding};
 
 mod mint2_contract;
@@ -46,7 +46,7 @@ fn do_vcr_test(value: &jubjub::Fr) {
         };
         result += thisbase;
         curbase = curbase.double();
-        print!("{}", if bit { 1} else { 0 });
+        print!("{}", if bit { 1 } else { 0 });
     }
     println!("");
     let result = jubjub::ExtendedPoint::from(result).to_affine();
@@ -79,10 +79,7 @@ fn main() -> std::result::Result<(), vm::ZKVMError> {
 
     vm.setup();
 
-    let mut params = vec![
-        public_affine.get_u(),
-        public_affine.get_v(),
-    ];
+    let mut params = vec![public_affine.get_u(), public_affine.get_v()];
     for x in unpack(randomness_value) {
         params.push(x);
     }

+ 768 - 0
src/serial.rs

@@ -0,0 +1,768 @@
+use bls12_381 as bls;
+use std::borrow::Cow;
+use std::io::{Cursor, Read, Write};
+use std::rc::Rc;
+use std::{io, mem};
+
+use crate::endian;
+use crate::error::{Error, Result};
+
+/// Encode an object into a vector
+pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
+    let mut encoder = Vec::new();
+    let len = data.encode(&mut encoder).unwrap();
+    assert_eq!(len, encoder.len());
+    encoder
+}
+
+/// Encode an object into a hex-encoded string
+pub fn serialize_hex<T: Encodable + ?Sized>(data: &T) -> String {
+    hex::encode(serialize(data))
+}
+
+/// Deserialize an object from a vector, will error if said deserialization
+/// doesn't consume the entire vector.
+pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T> {
+    let (rv, consumed) = deserialize_partial(data)?;
+
+    // Fail if data are not consumed entirely.
+    if consumed == data.len() {
+        Ok(rv)
+    } else {
+        Err(Error::ParseFailed(
+            "data not consumed entirely when explicitly deserializing",
+        ))
+    }
+}
+
+/// Deserialize an object from a vector, but will not report an error if said deserialization
+/// doesn't consume the entire vector.
+pub fn deserialize_partial<T: Decodable>(data: &[u8]) -> Result<(T, usize)> {
+    let mut decoder = Cursor::new(data);
+    let rv = Decodable::decode(&mut decoder)?;
+    let consumed = decoder.position() as usize;
+
+    Ok((rv, consumed))
+}
+
+/// Extensions of `Write` to encode data as per Bitcoin consensus
+pub trait WriteExt {
+    /// Output a 64-bit uint
+    fn write_u64(&mut self, v: u64) -> Result<()>;
+    /// Output a 32-bit uint
+    fn write_u32(&mut self, v: u32) -> Result<()>;
+    /// Output a 16-bit uint
+    fn write_u16(&mut self, v: u16) -> Result<()>;
+    /// Output a 8-bit uint
+    fn write_u8(&mut self, v: u8) -> Result<()>;
+
+    /// Output a 64-bit int
+    fn write_i64(&mut self, v: i64) -> Result<()>;
+    /// Output a 32-bit int
+    fn write_i32(&mut self, v: i32) -> Result<()>;
+    /// Output a 16-bit int
+    fn write_i16(&mut self, v: i16) -> Result<()>;
+    /// Output a 8-bit int
+    fn write_i8(&mut self, v: i8) -> Result<()>;
+
+    /// Output a boolean
+    fn write_bool(&mut self, v: bool) -> Result<()>;
+
+    /// Output a byte slice
+    fn write_slice(&mut self, v: &[u8]) -> Result<()>;
+}
+
+/// Extensions of `Read` to decode data as per Bitcoin consensus
+pub trait ReadExt {
+    /// Read a 64-bit uint
+    fn read_u64(&mut self) -> Result<u64>;
+    /// Read a 32-bit uint
+    fn read_u32(&mut self) -> Result<u32>;
+    /// Read a 16-bit uint
+    fn read_u16(&mut self) -> Result<u16>;
+    /// Read a 8-bit uint
+    fn read_u8(&mut self) -> Result<u8>;
+
+    /// Read a 64-bit int
+    fn read_i64(&mut self) -> Result<i64>;
+    /// Read a 32-bit int
+    fn read_i32(&mut self) -> Result<i32>;
+    /// Read a 16-bit int
+    fn read_i16(&mut self) -> Result<i16>;
+    /// Read a 8-bit int
+    fn read_i8(&mut self) -> Result<i8>;
+
+    /// Read a boolean
+    fn read_bool(&mut self) -> Result<bool>;
+
+    /// Read a byte slice
+    fn read_slice(&mut self, slice: &mut [u8]) -> Result<()>;
+}
+
+macro_rules! encoder_fn {
+    ($name:ident, $val_type:ty, $writefn:ident) => {
+        #[inline]
+        fn $name(&mut self, v: $val_type) -> Result<()> {
+            self.write_all(&endian::$writefn(v)).map_err(Error::Io)
+        }
+    };
+}
+
+macro_rules! decoder_fn {
+    ($name:ident, $val_type:ty, $readfn:ident, $byte_len: expr) => {
+        #[inline]
+        fn $name(&mut self) -> Result<$val_type> {
+            assert_eq!(::std::mem::size_of::<$val_type>(), $byte_len); // size_of isn't a constfn in 1.22
+            let mut val = [0; $byte_len];
+            self.read_exact(&mut val[..]).map_err(Error::Io)?;
+            Ok(endian::$readfn(&val))
+        }
+    };
+}
+
+impl<W: Write> WriteExt for W {
+    encoder_fn!(write_u64, u64, u64_to_array_le);
+    encoder_fn!(write_u32, u32, u32_to_array_le);
+    encoder_fn!(write_u16, u16, u16_to_array_le);
+    encoder_fn!(write_i64, i64, i64_to_array_le);
+    encoder_fn!(write_i32, i32, i32_to_array_le);
+    encoder_fn!(write_i16, i16, i16_to_array_le);
+
+    #[inline]
+    fn write_i8(&mut self, v: i8) -> Result<()> {
+        self.write_all(&[v as u8]).map_err(Error::Io)
+    }
+    #[inline]
+    fn write_u8(&mut self, v: u8) -> Result<()> {
+        self.write_all(&[v]).map_err(Error::Io)
+    }
+    #[inline]
+    fn write_bool(&mut self, v: bool) -> Result<()> {
+        self.write_all(&[v as u8]).map_err(Error::Io)
+    }
+    #[inline]
+    fn write_slice(&mut self, v: &[u8]) -> Result<()> {
+        self.write_all(v).map_err(Error::Io)
+    }
+}
+
+impl<R: Read> ReadExt for R {
+    decoder_fn!(read_u64, u64, slice_to_u64_le, 8);
+    decoder_fn!(read_u32, u32, slice_to_u32_le, 4);
+    decoder_fn!(read_u16, u16, slice_to_u16_le, 2);
+    decoder_fn!(read_i64, i64, slice_to_i64_le, 8);
+    decoder_fn!(read_i32, i32, slice_to_i32_le, 4);
+    decoder_fn!(read_i16, i16, slice_to_i16_le, 2);
+
+    #[inline]
+    fn read_u8(&mut self) -> Result<u8> {
+        let mut slice = [0u8; 1];
+        self.read_exact(&mut slice)?;
+        Ok(slice[0])
+    }
+    #[inline]
+    fn read_i8(&mut self) -> Result<i8> {
+        let mut slice = [0u8; 1];
+        self.read_exact(&mut slice)?;
+        Ok(slice[0] as i8)
+    }
+    #[inline]
+    fn read_bool(&mut self) -> Result<bool> {
+        ReadExt::read_i8(self).map(|bit| bit != 0)
+    }
+    #[inline]
+    fn read_slice(&mut self, slice: &mut [u8]) -> Result<()> {
+        self.read_exact(slice).map_err(Error::Io)
+    }
+}
+
+/// Data which can be encoded in a consensus-consistent way
+pub trait Encodable {
+    /// Encode an object with a well-defined format, should only ever error if
+    /// the underlying `Write` errors. Returns the number of bytes written on
+    /// success
+    fn encode<W: io::Write>(&self, e: W) -> Result<usize>;
+}
+
+/// Data which can be encoded in a consensus-consistent way
+pub trait Decodable: Sized {
+    /// Decode an object with a well-defined format
+    fn decode<D: io::Read>(d: D) -> Result<Self>;
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
+pub struct VarInt(pub u64);
+
+// Primitive types
+macro_rules! impl_int_encodable {
+    ($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
+        impl Decodable for $ty {
+            #[inline]
+            fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+                ReadExt::$meth_dec(&mut d).map($ty::from_le)
+            }
+        }
+        impl Encodable for $ty {
+            #[inline]
+            fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize> {
+                s.$meth_enc(self.to_le())?;
+                Ok(mem::size_of::<$ty>())
+            }
+        }
+    };
+}
+
+impl_int_encodable!(u8, read_u8, write_u8);
+impl_int_encodable!(u16, read_u16, write_u16);
+impl_int_encodable!(u32, read_u32, write_u32);
+impl_int_encodable!(u64, read_u64, write_u64);
+impl_int_encodable!(i8, read_i8, write_i8);
+impl_int_encodable!(i16, read_i16, write_i16);
+impl_int_encodable!(i32, read_i32, write_i32);
+impl_int_encodable!(i64, read_i64, write_i64);
+
+impl VarInt {
+    /// Gets the length of this VarInt when encoded.
+    /// Returns 1 for 0...0xFC, 3 for 0xFD...(2^16-1), 5 for 0x10000...(2^32-1),
+    /// and 9 otherwise.
+    #[inline]
+    pub fn len(&self) -> usize {
+        match self.0 {
+            0..=0xFC => 1,
+            0xFD..=0xFFFF => 3,
+            0x10000..=0xFFFFFFFF => 5,
+            _ => 9,
+        }
+    }
+}
+
+impl Encodable for VarInt {
+    #[inline]
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        match self.0 {
+            0..=0xFC => {
+                (self.0 as u8).encode(s)?;
+                Ok(1)
+            }
+            0xFD..=0xFFFF => {
+                s.write_u8(0xFD)?;
+                (self.0 as u16).encode(s)?;
+                Ok(3)
+            }
+            0x10000..=0xFFFFFFFF => {
+                s.write_u8(0xFE)?;
+                (self.0 as u32).encode(s)?;
+                Ok(5)
+            }
+            _ => {
+                s.write_u8(0xFF)?;
+                (self.0 as u64).encode(s)?;
+                Ok(9)
+            }
+        }
+    }
+}
+
+impl Decodable for VarInt {
+    #[inline]
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let n = ReadExt::read_u8(&mut d)?;
+        match n {
+            0xFF => {
+                let x = ReadExt::read_u64(&mut d)?;
+                if x < 0x100000000 {
+                    Err(self::Error::NonMinimalVarInt)
+                } else {
+                    Ok(VarInt(x))
+                }
+            }
+            0xFE => {
+                let x = ReadExt::read_u32(&mut d)?;
+                if x < 0x10000 {
+                    Err(self::Error::NonMinimalVarInt)
+                } else {
+                    Ok(VarInt(x as u64))
+                }
+            }
+            0xFD => {
+                let x = ReadExt::read_u16(&mut d)?;
+                if x < 0xFD {
+                    Err(self::Error::NonMinimalVarInt)
+                } else {
+                    Ok(VarInt(x as u64))
+                }
+            }
+            n => Ok(VarInt(n as u64)),
+        }
+    }
+}
+
+// Booleans
+impl Encodable for bool {
+    #[inline]
+    fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize> {
+        s.write_bool(*self)?;
+        Ok(1)
+    }
+}
+
+impl Decodable for bool {
+    #[inline]
+    fn decode<D: io::Read>(mut d: D) -> Result<bool> {
+        ReadExt::read_bool(&mut d)
+    }
+}
+
+// Strings
+impl Encodable for String {
+    #[inline]
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let b = self.as_bytes();
+        let vi_len = VarInt(b.len() as u64).encode(&mut s)?;
+        s.write_slice(&b)?;
+        Ok(vi_len + b.len())
+    }
+}
+
+impl Decodable for String {
+    #[inline]
+    fn decode<D: io::Read>(d: D) -> Result<String> {
+        String::from_utf8(Decodable::decode(d)?)
+            .map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
+    }
+}
+
+// Cow<'static, str>
+impl Encodable for Cow<'static, str> {
+    #[inline]
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let b = self.as_bytes();
+        let vi_len = VarInt(b.len() as u64).encode(&mut s)?;
+        s.write_slice(&b)?;
+        Ok(vi_len + b.len())
+    }
+}
+
+impl Decodable for Cow<'static, str> {
+    #[inline]
+    fn decode<D: io::Read>(d: D) -> Result<Cow<'static, str>> {
+        String::from_utf8(Decodable::decode(d)?)
+            .map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
+            .map(Cow::Owned)
+    }
+}
+
+// Arrays
+macro_rules! impl_array {
+    ( $size:expr ) => {
+        impl Encodable for [u8; $size] {
+            #[inline]
+            fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize> {
+                s.write_slice(&self[..])?;
+                Ok(self.len())
+            }
+        }
+
+        impl Decodable for [u8; $size] {
+            #[inline]
+            fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+                let mut ret = [0; $size];
+                d.read_slice(&mut ret)?;
+                Ok(ret)
+            }
+        }
+    };
+}
+
+impl_array!(2);
+impl_array!(4);
+impl_array!(8);
+impl_array!(12);
+impl_array!(16);
+impl_array!(32);
+impl_array!(33);
+
+// Vectors
+#[macro_export]
+macro_rules! impl_vec {
+    ($type: ty) => {
+        impl Encodable for Vec<$type> {
+            #[inline]
+            fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+                let mut len = 0;
+                len += VarInt(self.len() as u64).encode(&mut s)?;
+                for c in self.iter() {
+                    len += c.encode(&mut s)?;
+                }
+                Ok(len)
+            }
+        }
+        impl Decodable for Vec<$type> {
+            #[inline]
+            fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+                let len = VarInt::decode(&mut d)?.0;
+                let mut ret = Vec::with_capacity(len as usize);
+                for _ in 0..len {
+                    ret.push(Decodable::decode(&mut d)?);
+                }
+                Ok(ret)
+            }
+        }
+    };
+}
+impl_vec!(bls::Scalar);
+
+pub fn encode_with_size<S: io::Write>(data: &[u8], mut s: S) -> Result<usize> {
+    let vi_len = VarInt(data.len() as u64).encode(&mut s)?;
+    s.write_slice(&data)?;
+    Ok(vi_len + data.len())
+}
+
+impl Encodable for Vec<u8> {
+    #[inline]
+    fn encode<S: io::Write>(&self, s: S) -> Result<usize> {
+        encode_with_size(self, s)
+    }
+}
+
+impl Decodable for Vec<u8> {
+    #[inline]
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let len = VarInt::decode(&mut d)?.0 as usize;
+        let mut ret = vec![0u8; len];
+        d.read_slice(&mut ret)?;
+        Ok(ret)
+    }
+}
+
+impl Encodable for Box<[u8]> {
+    #[inline]
+    fn encode<S: io::Write>(&self, s: S) -> Result<usize> {
+        encode_with_size(self, s)
+    }
+}
+
+impl Decodable for Box<[u8]> {
+    #[inline]
+    fn decode<D: io::Read>(d: D) -> Result<Self> {
+        <Vec<u8>>::decode(d).map(From::from)
+    }
+}
+
+// Tuples
+macro_rules! tuple_encode {
+    ($($x:ident),*) => (
+        impl <$($x: Encodable),*> Encodable for ($($x),*) {
+            #[inline]
+            #[allow(non_snake_case)]
+            fn encode<S: io::Write>(
+                &self,
+                mut s: S,
+            ) -> Result<usize> {
+                let &($(ref $x),*) = self;
+                let mut len = 0;
+                $(len += $x.encode(&mut s)?;)*
+                Ok(len)
+            }
+        }
+
+        impl<$($x: Decodable),*> Decodable for ($($x),*) {
+            #[inline]
+            #[allow(non_snake_case)]
+            fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+                Ok(($({let $x = Decodable::decode(&mut d)?; $x }),*))
+            }
+        }
+    );
+}
+
+tuple_encode!(T0, T1);
+tuple_encode!(T0, T1, T2, T3);
+tuple_encode!(T0, T1, T2, T3, T4, T5);
+tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
+
+#[cfg(test)]
+mod tests {
+    use super::{deserialize, serialize, Error, Result, VarInt};
+    use super::{deserialize_partial, Encodable};
+    use crate::endian::{u16_to_array_le, u32_to_array_le, u64_to_array_le};
+    use std::io;
+    use std::mem::discriminant;
+
+    #[test]
+    fn serialize_int_test() {
+        // bool
+        assert_eq!(serialize(&false), vec![0u8]);
+        assert_eq!(serialize(&true), vec![1u8]);
+        // u8
+        assert_eq!(serialize(&1u8), vec![1u8]);
+        assert_eq!(serialize(&0u8), vec![0u8]);
+        assert_eq!(serialize(&255u8), vec![255u8]);
+        // u16
+        assert_eq!(serialize(&1u16), vec![1u8, 0]);
+        assert_eq!(serialize(&256u16), vec![0u8, 1]);
+        assert_eq!(serialize(&5000u16), vec![136u8, 19]);
+        // u32
+        assert_eq!(serialize(&1u32), vec![1u8, 0, 0, 0]);
+        assert_eq!(serialize(&256u32), vec![0u8, 1, 0, 0]);
+        assert_eq!(serialize(&5000u32), vec![136u8, 19, 0, 0]);
+        assert_eq!(serialize(&500000u32), vec![32u8, 161, 7, 0]);
+        assert_eq!(serialize(&168430090u32), vec![10u8, 10, 10, 10]);
+        // i32
+        assert_eq!(serialize(&-1i32), vec![255u8, 255, 255, 255]);
+        assert_eq!(serialize(&-256i32), vec![0u8, 255, 255, 255]);
+        assert_eq!(serialize(&-5000i32), vec![120u8, 236, 255, 255]);
+        assert_eq!(serialize(&-500000i32), vec![224u8, 94, 248, 255]);
+        assert_eq!(serialize(&-168430090i32), vec![246u8, 245, 245, 245]);
+        assert_eq!(serialize(&1i32), vec![1u8, 0, 0, 0]);
+        assert_eq!(serialize(&256i32), vec![0u8, 1, 0, 0]);
+        assert_eq!(serialize(&5000i32), vec![136u8, 19, 0, 0]);
+        assert_eq!(serialize(&500000i32), vec![32u8, 161, 7, 0]);
+        assert_eq!(serialize(&168430090i32), vec![10u8, 10, 10, 10]);
+        // u64
+        assert_eq!(serialize(&1u64), vec![1u8, 0, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(serialize(&256u64), vec![0u8, 1, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(serialize(&5000u64), vec![136u8, 19, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(serialize(&500000u64), vec![32u8, 161, 7, 0, 0, 0, 0, 0]);
+        assert_eq!(
+            serialize(&723401728380766730u64),
+            vec![10u8, 10, 10, 10, 10, 10, 10, 10]
+        );
+        // i64
+        assert_eq!(
+            serialize(&-1i64),
+            vec![255u8, 255, 255, 255, 255, 255, 255, 255]
+        );
+        assert_eq!(
+            serialize(&-256i64),
+            vec![0u8, 255, 255, 255, 255, 255, 255, 255]
+        );
+        assert_eq!(
+            serialize(&-5000i64),
+            vec![120u8, 236, 255, 255, 255, 255, 255, 255]
+        );
+        assert_eq!(
+            serialize(&-500000i64),
+            vec![224u8, 94, 248, 255, 255, 255, 255, 255]
+        );
+        assert_eq!(
+            serialize(&-723401728380766730i64),
+            vec![246u8, 245, 245, 245, 245, 245, 245, 245]
+        );
+        assert_eq!(serialize(&1i64), vec![1u8, 0, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(serialize(&256i64), vec![0u8, 1, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(serialize(&5000i64), vec![136u8, 19, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(serialize(&500000i64), vec![32u8, 161, 7, 0, 0, 0, 0, 0]);
+        assert_eq!(
+            serialize(&723401728380766730i64),
+            vec![10u8, 10, 10, 10, 10, 10, 10, 10]
+        );
+    }
+
+    #[test]
+    fn serialize_varint_test() {
+        assert_eq!(serialize(&VarInt(10)), vec![10u8]);
+        assert_eq!(serialize(&VarInt(0xFC)), vec![0xFCu8]);
+        assert_eq!(serialize(&VarInt(0xFD)), vec![0xFDu8, 0xFD, 0]);
+        assert_eq!(serialize(&VarInt(0xFFF)), vec![0xFDu8, 0xFF, 0xF]);
+        assert_eq!(
+            serialize(&VarInt(0xF0F0F0F)),
+            vec![0xFEu8, 0xF, 0xF, 0xF, 0xF]
+        );
+        assert_eq!(
+            serialize(&VarInt(0xF0F0F0F0F0E0)),
+            vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]
+        );
+        assert_eq!(
+            test_varint_encode(0xFF, &u64_to_array_le(0x100000000)).unwrap(),
+            VarInt(0x100000000)
+        );
+        assert_eq!(
+            test_varint_encode(0xFE, &u64_to_array_le(0x10000)).unwrap(),
+            VarInt(0x10000)
+        );
+        assert_eq!(
+            test_varint_encode(0xFD, &u64_to_array_le(0xFD)).unwrap(),
+            VarInt(0xFD)
+        );
+
+        // Test that length calc is working correctly
+        test_varint_len(VarInt(0), 1);
+        test_varint_len(VarInt(0xFC), 1);
+        test_varint_len(VarInt(0xFD), 3);
+        test_varint_len(VarInt(0xFFFF), 3);
+        test_varint_len(VarInt(0x10000), 5);
+        test_varint_len(VarInt(0xFFFFFFFF), 5);
+        test_varint_len(VarInt(0xFFFFFFFF + 1), 9);
+        test_varint_len(VarInt(u64::max_value()), 9);
+    }
+
+    fn test_varint_len(varint: VarInt, expected: usize) {
+        let mut encoder = io::Cursor::new(vec![]);
+        assert_eq!(varint.encode(&mut encoder).unwrap(), expected);
+        assert_eq!(varint.len(), expected);
+    }
+
+    fn test_varint_encode(n: u8, x: &[u8]) -> Result<VarInt> {
+        let mut input = [0u8; 9];
+        input[0] = n;
+        input[1..x.len() + 1].copy_from_slice(x);
+        deserialize_partial::<VarInt>(&input).map(|t| t.0)
+    }
+
+    #[test]
+    fn deserialize_nonminimal_vec() {
+        // Check the edges for variant int
+        assert_eq!(
+            discriminant(&test_varint_encode(0xFF, &u64_to_array_le(0x100000000 - 1)).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(&test_varint_encode(0xFE, &u32_to_array_le(0x10000 - 1)).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(&test_varint_encode(0xFD, &u16_to_array_le(0xFD - 1)).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+
+        assert_eq!(
+            discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]).unwrap_err()),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(
+                &deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+                    .unwrap_err()
+            ),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+        assert_eq!(
+            discriminant(
+                &deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00])
+                    .unwrap_err()
+            ),
+            discriminant(&Error::NonMinimalVarInt)
+        );
+
+        let mut vec_256 = vec![0; 259];
+        vec_256[0] = 0xfd;
+        vec_256[1] = 0x00;
+        vec_256[2] = 0x01;
+        assert!(deserialize::<Vec<u8>>(&vec_256).is_ok());
+
+        let mut vec_253 = vec![0; 256];
+        vec_253[0] = 0xfd;
+        vec_253[1] = 0xfd;
+        vec_253[2] = 0x00;
+        assert!(deserialize::<Vec<u8>>(&vec_253).is_ok());
+    }
+
+    #[test]
+    fn serialize_vector_test() {
+        assert_eq!(serialize(&vec![1u8, 2, 3]), vec![3u8, 1, 2, 3]);
+        // TODO: test vectors of more interesting objects
+    }
+
+    #[test]
+    fn serialize_strbuf_test() {
+        assert_eq!(
+            serialize(&"Andrew".to_string()),
+            vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]
+        );
+    }
+
+    #[test]
+    fn deserialize_int_test() {
+        // bool
+        assert!((deserialize(&[58u8, 0]) as Result<bool>).is_err());
+        assert_eq!(deserialize(&[58u8]).ok(), Some(true));
+        assert_eq!(deserialize(&[1u8]).ok(), Some(true));
+        assert_eq!(deserialize(&[0u8]).ok(), Some(false));
+        assert!((deserialize(&[0u8, 1]) as Result<bool>).is_err());
+
+        // u8
+        assert_eq!(deserialize(&[58u8]).ok(), Some(58u8));
+
+        // u16
+        assert_eq!(deserialize(&[0x01u8, 0x02]).ok(), Some(0x0201u16));
+        assert_eq!(deserialize(&[0xABu8, 0xCD]).ok(), Some(0xCDABu16));
+        assert_eq!(deserialize(&[0xA0u8, 0x0D]).ok(), Some(0xDA0u16));
+        let failure16: Result<u16> = deserialize(&[1u8]);
+        assert!(failure16.is_err());
+
+        // u32
+        assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0]).ok(), Some(0xCDABu32));
+        assert_eq!(
+            deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD]).ok(),
+            Some(0xCDAB0DA0u32)
+        );
+        let failure32: Result<u32> = deserialize(&[1u8, 2, 3]);
+        assert!(failure32.is_err());
+        // TODO: test negative numbers
+        assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0]).ok(), Some(0xCDABi32));
+        assert_eq!(
+            deserialize(&[0xA0u8, 0x0D, 0xAB, 0x2D]).ok(),
+            Some(0x2DAB0DA0i32)
+        );
+        let failurei32: Result<i32> = deserialize(&[1u8, 2, 3]);
+        assert!(failurei32.is_err());
+
+        // u64
+        assert_eq!(
+            deserialize(&[0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]).ok(),
+            Some(0xCDABu64)
+        );
+        assert_eq!(
+            deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]).ok(),
+            Some(0x99000099CDAB0DA0u64)
+        );
+        let failure64: Result<u64> = deserialize(&[1u8, 2, 3, 4, 5, 6, 7]);
+        assert!(failure64.is_err());
+        // TODO: test negative numbers
+        assert_eq!(
+            deserialize(&[0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]).ok(),
+            Some(0xCDABi64)
+        );
+        assert_eq!(
+            deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]).ok(),
+            Some(-0x66ffff663254f260i64)
+        );
+        let failurei64: Result<i64> = deserialize(&[1u8, 2, 3, 4, 5, 6, 7]);
+        assert!(failurei64.is_err());
+    }
+
+    #[test]
+    fn deserialize_vec_test() {
+        assert_eq!(deserialize(&[3u8, 2, 3, 4]).ok(), Some(vec![2u8, 3, 4]));
+        assert!((deserialize(&[4u8, 2, 3, 4, 5, 6]) as Result<Vec<u8>>).is_err());
+    }
+
+    #[test]
+    fn deserialize_strbuf_test() {
+        assert_eq!(
+            deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
+            Some("Andrew".to_string())
+        );
+        assert_eq!(
+            deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
+            Some(::std::borrow::Cow::Borrowed("Andrew"))
+        );
+    }
+}

+ 7 - 5
src/vm.rs

@@ -14,13 +14,15 @@ use std::ops::{Add, AddAssign, MulAssign, Neg, SubAssign};
 use std::time::Instant;
 
 pub struct ZKVirtualMachine {
-    pub ops: Vec<CryptoOperation>,
-    pub aux: Vec<Scalar>,
+    pub constants: Vec<Scalar>,
     pub alloc: Vec<(AllocType, VariableIndex)>,
+    pub ops: Vec<CryptoOperation>,
     pub constraints: Vec<ConstraintInstruction>,
+
+    pub aux: Vec<Scalar>,
+
     pub params: Option<groth16::Parameters<Bls12>>,
     pub verifying_key: Option<groth16::PreparedVerifyingKey<Bls12>>,
-    pub constants: Vec<Scalar>,
 }
 
 pub type VariableIndex = usize;
@@ -202,12 +204,12 @@ impl ZKVirtualMachine {
                     let (self_, start_index, end_index) = match start {
                         VariableRef::Aux(start_index) => match end {
                             VariableRef::Aux(end_index) => (&mut self.aux, start_index, end_index),
-                            VariableRef::Local(end_index) => {
+                            VariableRef::Local(_) => {
                                 return Err(ZKVMError::MalformedRange);
                             }
                         },
                         VariableRef::Local(start_index) => match end {
-                            VariableRef::Aux(end_index) => {
+                            VariableRef::Aux(_) => {
                                 return Err(ZKVMError::MalformedRange);
                             }
                             VariableRef::Local(end_index) => {

+ 214 - 0
src/vm_serial.rs

@@ -0,0 +1,214 @@
+use crate::error::{Error, Result};
+use crate::serial::{Decodable, Encodable, ReadExt, VarInt};
+use crate::vm::{
+    AllocType, ConstraintInstruction, CryptoOperation, VariableIndex, VariableRef, ZKVMCircuit,
+    ZKVirtualMachine,
+};
+use crate::{impl_vec, ZKSupervisor};
+use std::collections::HashMap;
+use std::io;
+
+impl Encodable for ZKSupervisor {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        unimplemented!();
+        Ok(0)
+    }
+}
+
+impl Decodable for ZKSupervisor {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            name: Decodable::decode(&mut d)?,
+            vm: ZKVirtualMachine {
+                constants: Decodable::decode(&mut d)?,
+                alloc: 
+                    Decodable::decode(&mut d)?,
+                ops: 
+                    Decodable::decode(&mut d)?,
+                constraints: 
+                    Decodable::decode(&mut d)?,
+
+                aux: Vec::new(),
+                params: None,
+                verifying_key: None,
+            },
+            params_map: HashMap::new(),
+            params: HashMap::new(),
+            public_map: HashMap::new(),
+        })
+    }
+}
+
+impl Encodable for (AllocType, VariableIndex) {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        //let len = self.x.encode(&mut s)?;
+        //Ok(len + self.y.encode(s)?)
+        unimplemented!();
+        Ok(0)
+    }
+}
+
+impl Decodable for (AllocType, VariableIndex) {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let type_val = ReadExt::read_u8(&mut d)?;
+        assert!(type_val == 0 || type_val == 1);
+        let alloc_type = if type_val == 0 {
+            AllocType::Private
+        } else {
+            AllocType::Public
+        };
+        Ok((alloc_type, ReadExt::read_u32(&mut d)? as usize))
+    }
+}
+
+impl_vec!((AllocType, VariableIndex));
+
+impl Decodable for VariableIndex {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(ReadExt::read_u32(&mut d)? as Self)
+    }
+}
+
+impl Encodable for VariableRef {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        unimplemented!();
+        Ok(0)
+    }
+}
+
+impl Decodable for VariableRef {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let arg_type = ReadExt::read_u8(&mut d)?;
+        match arg_type {
+            0 => Ok(Self::Aux(Decodable::decode(&mut d)?)),
+            1 => Ok(Self::Local(Decodable::decode(&mut d)?)),
+            _ => Err(Error::BadVariableRefType),
+        }
+    }
+}
+
+impl Encodable for CryptoOperation {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        unimplemented!();
+        Ok(0)
+    }
+}
+
+impl Decodable for CryptoOperation {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let op_type = ReadExt::read_u8(&mut d)?;
+        match op_type {
+            0 => Ok(Self::Set(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            1 => Ok(Self::Mul(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            2 => Ok(Self::Add(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            3 => Ok(Self::Sub(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            4 => Ok(Self::Divide(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            5 => Ok(Self::Double(Decodable::decode(&mut d)?)),
+            6 => Ok(Self::Square(Decodable::decode(&mut d)?)),
+            7 => Ok(Self::Invert(Decodable::decode(&mut d)?)),
+            8 => Ok(Self::UnpackBits(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            9 => Ok(Self::Local),
+            10 => Ok(Self::Load(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            11 => Ok(Self::Debug(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            12 => Ok(Self::DumpAlloc),
+            13 => Ok(Self::DumpLocal),
+            i => Err(Error::BadOperationType),
+        }
+    }
+}
+
+impl_vec!(CryptoOperation);
+
+impl Encodable for ConstraintInstruction {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        unimplemented!();
+        Ok(0)
+    }
+}
+
+impl Decodable for ConstraintInstruction {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let constraint_type = ReadExt::read_u8(&mut d)?;
+        match constraint_type {
+            0 =>
+                Ok(Self::Lc0Add(Decodable::decode(&mut d)?)),
+            1 =>
+                Ok(Self::Lc1Add(Decodable::decode(&mut d)?)),
+            2 =>
+                Ok(Self::Lc2Add(Decodable::decode(&mut d)?)),
+            3 =>
+                Ok(Self::Lc0Sub(Decodable::decode(&mut d)?)),
+            4 =>
+                Ok(Self::Lc1Sub(Decodable::decode(&mut d)?)),
+            5 =>
+                Ok(Self::Lc2Sub(Decodable::decode(&mut d)?)),
+            6 =>
+                Ok(Self::Lc0AddOne),
+            7 =>
+                Ok(Self::Lc1AddOne),
+            8 =>
+                Ok(Self::Lc2AddOne),
+            9 =>
+                Ok(Self::Lc0SubOne),
+            10 =>
+                Ok(Self::Lc1SubOne),
+            11 =>
+                Ok(Self::Lc2SubOne),
+            12 =>
+                Ok(Self::Lc0AddCoeff(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            13 =>
+                Ok(Self::Lc1AddCoeff(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            14 =>
+                Ok(Self::Lc2AddCoeff(
+                Decodable::decode(&mut d)?,
+                Decodable::decode(&mut d)?,
+            )),
+            15 =>
+                Ok(Self::Lc0AddOneCoeff(Decodable::decode(&mut d)?)),
+            16 =>
+                Ok(Self::Lc1AddOneCoeff(Decodable::decode(&mut d)?)),
+            17 =>
+                Ok(Self::Lc2AddOneCoeff(Decodable::decode(&mut d)?)),
+            18 =>
+                Ok(Self::Enforce),
+            19 =>
+                Ok(Self::LcCoeffReset),
+            20 =>
+                Ok(Self::LcCoeffDouble),
+            _ => Err(Error::BadConstraintType),
+        }
+    }
+}
+
+impl_vec!(ConstraintInstruction);

+ 2 - 0
src/vmtest.rs

@@ -56,6 +56,8 @@ fn main() {
     assert_eq!(public.len(), 2);
     // 0x66ced46f14e5616d12b993f60a6e66558d6b6afe4c321ed212e0b9cfbd81061a
     // 0x4731570fdd57cf280eadc8946fa00df81112502e44e497e794ab9a221f1bcca
+    println!("u = {:?}", public[0]);
+    println!("v = {:?}", public[1]);
 
     assert!(vm.verify(&proof, &public));
 }