Selaa lähdekoodia

move vm from examples/ to example/

narodnik 4 vuotta sitten
vanhempi
sitoutus
1a5c01ff76

+ 47 - 0
example/vm/Cargo.toml

@@ -0,0 +1,47 @@
+[package]
+name = "halo2_examples"
+version = "0.1.0"
+authors = ["narodnik <x@x.org>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+ff = "0.10"
+group = "0.10"
+pasta_curves = "0.1.2"
+bitvec = "0.22"
+rand = "0.8.4"
+arrayvec = "0.7.0"
+lazy_static = "1"
+bigint = "4"
+subtle = "2.3"
+halo2 = "0.0"
+
+[patch.crates-io]
+halo2 = { git = "https://github.com/zcash/halo2.git", rev = "27c4187673a9c6ade13fbdbd4f20955530c22d7f" }
+
+[dependencies.halo2_poseidon]
+git = "https://github.com/parazyd/orchard.git"
+#rev = "0d14f2390734e4710fc24a976f037dae3a6e7ac8"
+rev = "f9cc01c21010b31988129f9cbc2ca8c0bdbf2ee9"
+features = ["halo2"]
+
+[dependencies.halo2_utilities]
+git = "https://github.com/parazyd/orchard.git"
+#rev = "0d14f2390734e4710fc24a976f037dae3a6e7ac8"
+rev = "f9cc01c21010b31988129f9cbc2ca8c0bdbf2ee9"
+
+[dependencies.halo2_ecc]
+git = "https://github.com/parazyd/orchard.git"
+#rev = "0d14f2390734e4710fc24a976f037dae3a6e7ac8"
+rev = "f9cc01c21010b31988129f9cbc2ca8c0bdbf2ee9"
+
+[dependencies.sinsemilla]
+git = "https://github.com/parazyd/orchard.git"
+#rev = "0d14f2390734e4710fc24a976f037dae3a6e7ac8"
+rev = "f9cc01c21010b31988129f9cbc2ca8c0bdbf2ee9"
+
+ [dependencies.orchard]
+git = "https://github.com/parazyd/orchard.git"
+rev = "f9cc01c21010b31988129f9cbc2ca8c0bdbf2ee9"

+ 9 - 0
example/vm/doit.sh

@@ -0,0 +1,9 @@
+#!/bin/bash -x
+cd ../..
+python script/zkas.py proof/mint.zk --bincode
+du -sh proof/mint.zk.bin
+python script/zkas.py proof/mint.zk
+#python script/zkas.py proof/mint.zk
+cd examples/vm/
+cargo run --release --bin vm2
+

+ 203 - 0
example/vm/src/arith_chip.rs

@@ -0,0 +1,203 @@
+use halo2::{
+    circuit::{SimpleFloorPlanner, Cell, Chip, Layouter},
+    pasta::{EqAffine, Fp, pallas},
+    plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Expression, Selector, create_proof, verify_proof, keygen_vk, keygen_pk/*, Permutation*/},
+    poly::{commitment::{Blind, Params}, Rotation},
+    transcript::{Blake2bRead, Blake2bWrite, Challenge255},
+};
+use halo2_utilities::{CellValue, Var};
+use group::Curve;
+use std::time::Instant;
+
+type Variable = CellValue<pallas::Base>;
+
+#[derive(Clone, Debug)]
+pub struct ArithmeticChipConfig {
+    a_col: Column<Advice>,
+    b_col: Column<Advice>,
+    //permute: Permutation,
+    s_add: Selector,
+    s_mul: Selector,
+    //s_pub: Selector,
+}
+
+pub struct ArithmeticChip {
+    config: ArithmeticChipConfig
+}
+
+impl Chip<Fp> for ArithmeticChip {
+    type Config = ArithmeticChipConfig;
+    type Loaded = ();
+
+    fn config(&self) -> &Self::Config {
+        &self.config
+    }
+
+    fn loaded(&self) -> &Self::Loaded {
+        &()
+    }
+}
+
+impl ArithmeticChip {
+    pub fn construct(config: ArithmeticChipConfig) -> Self {
+        Self { config }
+    }
+
+    pub fn configure(cs: &mut ConstraintSystem<Fp>) -> ArithmeticChipConfig {
+        let a_col = cs.advice_column();
+        let b_col = cs.advice_column();
+
+        cs.enable_equality(a_col.into());
+        cs.enable_equality(b_col.into());
+
+        //let instance = cs.instance_column();
+
+        /*let permute = {
+            // Convert advice columns into an "any" columns.
+            let cols: [Column<Any>; 2] = [a_col.into(), b_col.into()];
+            Permutation::new(cs, &cols)
+        };*/
+
+        let s_add = cs.selector();
+        let s_mul = cs.selector();
+        //let s_pub = cs.selector();
+
+        cs.create_gate("add", |cs| {
+            let lhs = cs.query_advice(a_col, Rotation::cur());
+            let rhs = cs.query_advice(b_col, Rotation::cur());
+            let out = cs.query_advice(a_col, Rotation::next());
+            let s_add = cs.query_selector(s_add);
+
+            vec![s_add * (lhs + rhs - out)]
+        });
+
+        cs.create_gate("mul", |cs| {
+            let lhs = cs.query_advice(a_col, Rotation::cur());
+            let rhs = cs.query_advice(b_col, Rotation::cur());
+            let out = cs.query_advice(a_col, Rotation::next());
+            let s_mul = cs.query_selector(s_mul);
+
+            vec![s_mul * (lhs * rhs - out)]
+        });
+
+        /*
+        cs.create_gate("pub", |cs| {
+            let a = cs.query_advice(a_col, Rotation::cur());
+            let p = cs.query_instance(instance, Rotation::cur());
+            let s_pub = cs.query_selector(s_pub);
+
+            vec![s_pub * (p - a)]
+        });
+        */
+
+        ArithmeticChipConfig { a_col, b_col, /*permute,*/ s_add, s_mul /*, s_pub*/ }
+    }
+
+    pub fn add(
+        &self,
+        mut layouter: impl Layouter<Fp>,
+        a: Variable,
+        b: Variable
+    ) -> Result<Variable, Error> {
+        let mut out = None;
+        layouter.assign_region(
+            || "mul",
+            |mut region| {
+                self.config.s_add.enable(&mut region, 0)?;
+
+                let lhs = region.assign_advice(
+                    || "lhs",
+                    self.config.a_col,
+                    0,
+                    || a.value().ok_or(Error::SynthesisError),
+                )?;
+                let rhs = region.assign_advice(
+                    || "rhs",
+                    self.config.b_col,
+                    0,
+                    || b.value().ok_or(Error::SynthesisError),
+                )?;
+                region.constrain_equal(a.cell(), lhs)?;
+                region.constrain_equal(b.cell(), rhs)?;
+
+                let value = a.value().and_then(|a| b.value().map(|b| a + b));
+                let cell = region.assign_advice(
+                    || "lhs + rhs",
+                    self.config.a_col,
+                    1,
+                    || value.ok_or(Error::SynthesisError),
+                )?;
+
+                out = Some(Var::new(cell, value));
+                Ok(())
+            },
+        )?;
+
+        Ok(out.unwrap())
+    }
+
+    pub fn mul(
+        &self,
+        mut layouter: impl Layouter<Fp>,
+        a: Variable,
+        b: Variable
+    ) -> Result<Variable, Error> {
+        let mut out = None;
+        layouter.assign_region(
+            || "mul",
+            |mut region| {
+                self.config.s_mul.enable(&mut region, 0)?;
+
+                let lhs = region.assign_advice(
+                    || "lhs",
+                    self.config.a_col,
+                    0,
+                    || a.value().ok_or(Error::SynthesisError),
+                )?;
+                let rhs = region.assign_advice(
+                    || "rhs",
+                    self.config.b_col,
+                    0,
+                    || b.value().ok_or(Error::SynthesisError),
+                )?;
+                region.constrain_equal(a.cell(), lhs)?;
+                region.constrain_equal(b.cell(), rhs)?;
+
+                let value = a.value().and_then(|a| b.value().map(|b| a * b));
+                let cell = region.assign_advice(
+                    || "lhs * rhs",
+                    self.config.a_col,
+                    1,
+                    || value.ok_or(Error::SynthesisError),
+                )?;
+
+                out = Some(Var::new(cell, value));
+                Ok(())
+            },
+        )?;
+
+        Ok(out.unwrap())
+    }
+
+    /*
+    fn expose_public(&self, layouter: &mut impl Layouter<Fp>, num: Number) -> Result<(), Error> {
+        layouter.assign_region(
+            || "expose public",
+            |mut region| {
+                self.config.s_pub.enable(&mut region, 0)?;
+
+                let out = region.assign_advice(
+                    || "public advice",
+                    self.config.b_col,
+                    0,
+                    || num.value.ok_or(Error::SynthesisError),
+                )?;
+                region.constrain_equal(num.cell, out)?;
+
+                Ok(())
+            },
+        )
+    }
+    */
+}
+

+ 210 - 0
example/vm/src/bin/vm2.rs

@@ -0,0 +1,210 @@
+use group::{Curve, Group};
+use halo2::{
+    arithmetic::{CurveAffine, CurveExt, Field, FieldExt},
+    circuit::{Layouter, SimpleFloorPlanner},
+    dev::MockProver,
+    pasta::{pallas, vesta},
+    plonk,
+    poly::commitment,
+    transcript::{Blake2bRead, Blake2bWrite},
+};
+use halo2_poseidon::{
+    gadget::{Hash as PoseidonHash, Word},
+    pow5t3::{Pow5T3Chip as PoseidonChip, StateWord},
+    primitive::{ConstantLength, Hash, P128Pow5T3 as OrchardNullifier},
+};
+use orchard::constants::fixed_bases::{
+    OrchardFixedBases, VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES,
+    VALUE_COMMITMENT_V_BYTES,
+};
+use rand::rngs::OsRng;
+use std::{collections::HashMap, fs::File, time::Instant};
+
+use drk::serial::Decodable;
+
+// The number of rows in our circuit cannot exceed 2^k
+const K: u32 = 9;
+
+#[allow(non_snake_case)]
+pub fn pedersen_commitment(value: u64, blind: pallas::Scalar) -> pallas::Point {
+    let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
+    let V = hasher(&VALUE_COMMITMENT_V_BYTES);
+    let R = hasher(&VALUE_COMMITMENT_R_BYTES);
+    let value = pallas::Scalar::from_u64(value);
+
+    V * value + R * blind
+}
+
+#[derive(Debug)]
+struct VerifyingKey {
+    params: commitment::Params<vesta::Affine>,
+    vk: plonk::VerifyingKey<vesta::Affine>,
+}
+
+impl VerifyingKey {
+    fn build(empty_circuit: drk::vm2::ZkCircuit) -> Self {
+        let params = commitment::Params::new(K);
+
+        let vk = plonk::keygen_vk(&params, &empty_circuit).unwrap();
+
+        VerifyingKey { params, vk }
+    }
+}
+
+#[derive(Debug)]
+struct ProvingKey {
+    params: commitment::Params<vesta::Affine>,
+    pk: plonk::ProvingKey<vesta::Affine>,
+}
+
+impl ProvingKey {
+    fn build(empty_circuit: drk::vm2::ZkCircuit) -> Self {
+        let params = commitment::Params::new(K);
+
+        let vk = plonk::keygen_vk(&params, &empty_circuit).unwrap();
+        let pk = plonk::keygen_pk(&params, vk, &empty_circuit).unwrap();
+
+        ProvingKey { params, pk }
+    }
+}
+
+#[derive(Clone, Debug)]
+struct Proof(Vec<u8>);
+
+impl AsRef<[u8]> for Proof {
+    fn as_ref(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl Proof {
+    fn create(
+        pk: &ProvingKey,
+        circuits: &[drk::vm2::ZkCircuit],
+        pubinputs: &[pallas::Base],
+    ) -> Result<Self, plonk::Error> {
+        let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]);
+        plonk::create_proof(
+            &pk.params,
+            &pk.pk,
+            circuits,
+            &[&[pubinputs]],
+            &mut transcript,
+        )?;
+        Ok(Proof(transcript.finalize()))
+    }
+
+    fn verify(&self, vk: &VerifyingKey, pubinputs: &[pallas::Base]) -> Result<(), plonk::Error> {
+        let msm = vk.params.empty_msm();
+        let mut transcript = Blake2bRead::init(&self.0[..]);
+        let guard = plonk::verify_proof(&vk.params, &vk.vk, msm, &[&[pubinputs]], &mut transcript)?;
+        let msm = guard.clone().use_challenges();
+        if msm.eval() {
+            Ok(())
+        } else {
+            Err(plonk::Error::ConstraintSystemFailure)
+        }
+    }
+
+    // fn new(bytes: Vec<u8>) -> Self {
+    // Proof(bytes)
+    // }
+}
+
+fn main() -> std::result::Result<(), failure::Error> {
+    let start = Instant::now();
+    let file = File::open("../../proof/mint.zk.bin")?;
+    let zkbin = drk::vm2::ZkBinary::decode(file)?;
+    for contract_name in zkbin.contracts.keys() {
+        println!("Loaded '{}' contract.", contract_name);
+    }
+    println!("Load time: [{:?}]", start.elapsed());
+
+    let contract = &zkbin.contracts["Mint"];
+
+    //contract.witness_base(...);
+    //contract.witness_base(...);
+    //contract.witness_base(...);
+
+    let pubkey = pallas::Point::random(&mut OsRng);
+    let coords = pubkey.to_affine().coordinates().unwrap();
+
+    let value = 110;
+    let asset = 1;
+
+    let value_blind = pallas::Scalar::random(&mut OsRng);
+    let asset_blind = pallas::Scalar::random(&mut OsRng);
+
+    let serial = pallas::Base::random(&mut OsRng);
+    let coin_blind = pallas::Base::random(&mut OsRng);
+
+    let mut coin = pallas::Base::zero();
+
+    let messages = [
+        [*coords.x(), *coords.y()],
+        [pallas::Base::from(value), pallas::Base::from(asset)],
+        [serial, coin_blind],
+    ];
+
+    for msg in messages.iter() {
+        coin += Hash::init(OrchardNullifier, ConstantLength::<2>).hash(*msg);
+    }
+
+    let coin2 = Hash::init(OrchardNullifier, ConstantLength::<2>).hash([*coords.x(), *coords.y()]);
+
+    let value_commit = pedersen_commitment(value, value_blind);
+    let value_coords = value_commit.to_affine().coordinates().unwrap();
+
+    let asset_commit = pedersen_commitment(asset, asset_blind);
+    let asset_coords = asset_commit.to_affine().coordinates().unwrap();
+
+    let mut public_inputs = vec![
+        coin,
+        *value_coords.x(),
+        *value_coords.y(),
+        *asset_coords.x(),
+        *asset_coords.y(),
+    ];
+
+    let mut const_fixed_points = HashMap::new();
+    const_fixed_points.insert(
+        "VALUE_COMMIT_VALUE".to_string(),
+        OrchardFixedBases::ValueCommitV,
+    );
+    const_fixed_points.insert(
+        "VALUE_COMMIT_RANDOM".to_string(),
+        OrchardFixedBases::ValueCommitR,
+    );
+
+    let mut circuit = drk::vm2::ZkCircuit::new(const_fixed_points, &zkbin.constants, contract);
+    let empty_circuit = circuit.clone();
+
+    circuit.witness_base("pub_x", *coords.x())?;
+    circuit.witness_base("pub_y", *coords.y())?;
+    circuit.witness_base("value", pallas::Base::from(value))?;
+    circuit.witness_base("asset", pallas::Base::from(asset))?;
+    circuit.witness_base("serial", serial)?;
+    circuit.witness_base("coin_blind", coin_blind)?;
+    circuit.witness_scalar("value_blind", value_blind)?;
+    circuit.witness_scalar("asset_blind", asset_blind)?;
+
+    // Valid MockProver
+    let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap();
+    assert_eq!(prover.verify(), Ok(()));
+
+    // Actual ZK proof
+    let start = Instant::now();
+    let vk = VerifyingKey::build(empty_circuit.clone());
+    let pk = ProvingKey::build(empty_circuit.clone());
+    println!("\nSetup: [{:?}]", start.elapsed());
+
+    let start = Instant::now();
+    let proof = Proof::create(&pk, &[circuit], &public_inputs).unwrap();
+    println!("Prove: [{:?}]", start.elapsed());
+
+    let start = Instant::now();
+    assert!(proof.verify(&vk, &public_inputs).is_ok());
+    println!("Verify: [{:?}]", start.elapsed());
+
+    Ok(())
+}

+ 137 - 0
example/vm/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]);
+    }
+}

+ 113 - 0
example/vm/src/error.rs

@@ -0,0 +1,113 @@
+use std::fmt;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug, Clone)]
+pub enum Error {
+    Io(std::io::ErrorKind),
+    /// VarInt was encoded in a non-minimal way
+    PathNotFound,
+    NonMinimalVarInt,
+    /// Parsing error
+    ParseFailed(&'static str),
+    ParseIntError,
+    AsyncChannelError,
+    MalformedPacket,
+    AddrParseError,
+    BadVariableRefType,
+    BadOperationType,
+    BadConstraintType,
+    InvalidParamName,
+    InvalidParamType,
+    MissingParams,
+    VmError,
+    BadContract,
+    Groth16Error,
+    RusqliteError(String),
+    OperationFailed,
+    ConnectFailed,
+    ConnectTimeout,
+    ChannelStopped,
+    ChannelTimeout,
+    ServiceStopped,
+    Utf8Error,
+    StrUtf8Error(String),
+    NoteDecryptionFailed,
+    ServicesError(&'static str),
+    ZmqError(String),
+    VerifyFailed,
+    TryIntoError,
+    TryFromError,
+    JsonRpcError(String),
+    RocksdbError(String),
+    TreeFull,
+    SerdeJsonError(String),
+    SurfHttpError(String),
+    EmptyPassword,
+    TomlDeserializeError(String),
+    TomlSerializeError(String),
+    CashierNoReply,
+    Base58EncodeError(String),
+    Base58DecodeError(String),
+    BadBTCAddress(String),
+}
+
+impl std::error::Error for Error {}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
+        match *self {
+            Error::PathNotFound => f.write_str("Cannot find home directory"),
+            Error::Io(ref err) => write!(f, "io error:{:?}", err),
+            Error::NonMinimalVarInt => f.write_str("non-minimal varint"),
+            Error::ParseFailed(ref err) => write!(f, "parse failed: {}", err),
+            Error::ParseIntError => f.write_str("Parse int error"),
+            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"),
+            Error::InvalidParamName => f.write_str("Invalid param name"),
+            Error::InvalidParamType => f.write_str("Invalid param type"),
+            Error::MissingParams => f.write_str("Missing params"),
+            Error::VmError => f.write_str("VM error"),
+            Error::BadContract => f.write_str("Contract is poorly defined"),
+            Error::Groth16Error => f.write_str("Groth16 error"),
+            Error::RusqliteError(ref err) => write!(f, "Rusqlite error {}", err),
+            Error::OperationFailed => f.write_str("Operation failed"),
+
+            Error::ConnectFailed => f.write_str("Connection failed"),
+            Error::ConnectTimeout => f.write_str("Connection timed out"),
+            Error::ChannelStopped => f.write_str("Channel stopped"),
+            Error::ChannelTimeout => f.write_str("Channel timed out"),
+            Error::ServiceStopped => f.write_str("Service stopped"),
+            Error::Utf8Error => f.write_str("Malformed UTF8"),
+            Error::StrUtf8Error(ref err) => write!(f, "Malformed UTF8: {}", err),
+            Error::NoteDecryptionFailed => f.write_str("Unable to decrypt mint note"),
+            Error::ServicesError(ref err) => write!(f, "Services error: {}", err),
+            Error::ZmqError(ref err) => write!(f, "ZmqError: {}", err),
+            Error::VerifyFailed => f.write_str("Verify failed"),
+            Error::TryIntoError => f.write_str("TryInto error"),
+            Error::TryFromError => f.write_str("TryFrom error"),
+            Error::RocksdbError(ref err) => write!(f, "Rocksdb Error: {}", err),
+            Error::JsonRpcError(ref err) => write!(f, "JsonRpc Error: {}", err),
+            Error::TreeFull => f.write_str("MerkleTree is full"),
+            Error::SerdeJsonError(ref err) => write!(f, "Json serialization error: {}", err),
+            Error::SurfHttpError(ref err) => write!(f, "Surf Http error: {}", err),
+            Error::EmptyPassword => f.write_str("Password is empty. Cannot create database"),
+            Error::TomlDeserializeError(ref err) => write!(f, "Toml parsing error: {}", err),
+            Error::TomlSerializeError(ref err) => write!(f, "Toml parsing error: {}", err),
+            Error::Base58EncodeError(ref err) => write!(f, "bs58 encode error: {}", err),
+            Error::Base58DecodeError(ref err) => write!(f, "bs58 decode error: {}", err),
+            Error::CashierNoReply => f.write_str("Cashier did not reply with BTC address"),
+            Error::BadBTCAddress(ref err) => write!(f, "could not parse BTC address: {}", err),
+        }
+    }
+}
+
+impl From<std::io::Error> for Error {
+    fn from(err: std::io::Error) -> Error {
+        Error::Io(err.kind())
+    }
+}

+ 22 - 0
example/vm/src/lib.rs

@@ -0,0 +1,22 @@
+//pub mod async_serial;
+//pub mod blockchain;
+//pub mod bls_extensions;
+//pub mod circuit;
+//pub mod cli;
+//pub mod crypto;
+pub mod arith_chip;
+pub mod endian;
+pub mod error;
+//pub mod net;
+//pub mod rpc;
+pub mod serial;
+//pub mod service;
+//pub mod state;
+//pub mod system;
+//pub mod tx;
+//pub mod util;
+//pub mod vm;
+pub mod vm2;
+pub mod vm2_serial;
+//pub mod vm_serial;
+//pub mod wallet;

+ 870 - 0
example/vm/src/serial.rs

@@ -0,0 +1,870 @@
+use std::borrow::Cow;
+use std::io::{Cursor, Read, Write};
+use std::net::{IpAddr, SocketAddr};
+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(|e| Error::Io(e.kind()))
+        }
+    };
+}
+
+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(|e| Error::Io(e.kind()))?;
+            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(|e| Error::Io(e.kind()))
+    }
+    #[inline]
+    fn write_u8(&mut self, v: u8) -> Result<()> {
+        self.write_all(&[v]).map_err(|e| Error::Io(e.kind()))
+    }
+    #[inline]
+    fn write_bool(&mut self, v: bool) -> Result<()> {
+        self.write_all(&[v as u8]).map_err(|e| Error::Io(e.kind()))
+    }
+    #[inline]
+    fn write_slice(&mut self, v: &[u8]) -> Result<()> {
+        self.write_all(v).map_err(|e| Error::Io(e.kind()))
+    }
+}
+
+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(|e| Error::Io(e.kind()))
+    }
+}
+
+/// 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);
+
+// Options
+impl<T: Encodable> Encodable for Option<T> {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        if let Some(v) = self {
+            len += true.encode(&mut s)?;
+            len += v.encode(&mut s)?;
+        } else {
+            len += false.encode(&mut s)?;
+        }
+        Ok(len)
+    }
+}
+impl<T: Decodable> Decodable for Option<T> {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let valid: bool = Decodable::decode(&mut d)?;
+        let mut val: Option<T> = None;
+
+        if valid {
+            val = Some(Decodable::decode(&mut d)?);
+        }
+
+        Ok(val)
+    }
+}
+
+impl<T: Encodable> Encodable for Vec<Option<T>> {
+    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 val in self {
+            len += val.encode(&mut s)?;
+        }
+        Ok(len)
+    }
+}
+impl<T: Decodable> Decodable for Vec<Option<T>> {
+    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)
+    }
+}
+
+// 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!(SocketAddr);
+impl_vec!([u8; 32]);
+
+impl Encodable for IpAddr {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        match self {
+            IpAddr::V4(ip) => {
+                let version: u8 = 4;
+                len += version.encode(&mut s)?;
+                len += ip.octets().encode(s)?;
+            }
+            IpAddr::V6(ip) => {
+                let version: u8 = 6;
+                len += version.encode(&mut s)?;
+                len += ip.octets().encode(s)?;
+            }
+        }
+        Ok(len)
+    }
+}
+
+impl Decodable for IpAddr {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let version: u8 = Decodable::decode(&mut d)?;
+        match version {
+            4 => {
+                let addr: [u8; 4] = Decodable::decode(&mut d)?;
+                Ok(IpAddr::from(addr))
+            }
+            6 => {
+                let addr: [u8; 16] = Decodable::decode(&mut d)?;
+                Ok(IpAddr::from(addr))
+            }
+            _ => Err(Error::ParseFailed("couldn't decode IpAddr")),
+        }
+    }
+}
+
+impl Encodable for SocketAddr {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.ip().encode(&mut s)?;
+        len += self.port().encode(s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for SocketAddr {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let ip = Decodable::decode(&mut d)?;
+        let port: u16 = Decodable::decode(d)?;
+        Ok(SocketAddr::new(ip, port))
+    }
+}
+
+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"))
+        );
+    }
+}

+ 442 - 0
example/vm/src/vm2.rs

@@ -0,0 +1,442 @@
+use halo2::{
+    circuit::{Layouter, SimpleFloorPlanner},
+    pasta::pallas,
+    plonk,
+    plonk::{
+        Advice, Circuit, Column, ConstraintSystem, Error as PlonkError, Instance as InstanceColumn,
+        Selector,
+    },
+};
+use std::collections::HashMap;
+use std::{convert::TryInto, time::Instant};
+
+use halo2_ecc::{
+    chip::{EccChip, EccConfig},
+    gadget::FixedPoint,
+};
+use halo2_poseidon::{
+    gadget::{Hash as PoseidonHash, Word},
+    pow5t3::{Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig, StateWord},
+    primitive::{ConstantLength, Hash, P128Pow5T3 as OrchardNullifier},
+};
+use halo2_utilities::{
+    lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
+};
+use orchard::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
+use sinsemilla::chip::{SinsemillaChip, SinsemillaConfig};
+
+use crate::arith_chip::{ArithmeticChipConfig, ArithmeticChip};
+use crate::error::{Error, Result};
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum ZkType {
+    Base,
+    Scalar,
+    EcPoint,
+    EcFixedPoint,
+}
+
+type ArgIdx = usize;
+
+#[derive(Clone, Debug)]
+pub enum ZkFunctionCall {
+    PoseidonHash(ArgIdx, ArgIdx),
+    Add(ArgIdx, ArgIdx),
+    ConstrainInstance(ArgIdx),
+    EcMulShort(ArgIdx, ArgIdx),
+    EcMul(ArgIdx, ArgIdx),
+    EcAdd(ArgIdx, ArgIdx),
+    EcGetX(ArgIdx),
+    EcGetY(ArgIdx),
+}
+
+pub struct ZkBinary {
+    pub constants: Vec<(String, ZkType)>,
+    pub contracts: HashMap<String, ZkContract>,
+}
+
+#[derive(Clone, Debug)]
+pub struct ZkContract {
+    pub witness: Vec<(String, ZkType)>,
+    pub code: Vec<ZkFunctionCall>,
+}
+
+// These is the actual structures below which interpret the structures
+// deserialized above.
+
+#[derive(Clone, Debug)]
+pub struct MintConfig {
+    pub primary: Column<InstanceColumn>,
+    pub q_add: Selector,
+    pub advices: [Column<Advice>; 10],
+    pub ecc_config: EccConfig,
+    pub poseidon_config: PoseidonConfig<pallas::Base>,
+    pub arith_config: ArithmeticChipConfig,
+}
+
+impl MintConfig {
+    pub fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
+        EccChip::construct(self.ecc_config.clone())
+    }
+
+    pub fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
+        PoseidonChip::construct(self.poseidon_config.clone())
+    }
+
+    pub fn arithmetic_chip(&self) -> ArithmeticChip {
+        ArithmeticChip::construct(self.arith_config.clone())
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct ZkCircuit<'a> {
+    pub const_fixed_points: HashMap<String, OrchardFixedBases>,
+    pub constants: &'a Vec<(String, ZkType)>,
+    pub contract: &'a ZkContract,
+    // For each type create a separate stack
+    pub witness_base: HashMap<String, Option<pallas::Base>>,
+    pub witness_scalar: HashMap<String, Option<pallas::Scalar>>,
+}
+
+impl<'a> ZkCircuit<'a> {
+    pub fn new(
+        const_fixed_points: HashMap<String, OrchardFixedBases>,
+        constants: &'a Vec<(String, ZkType)>,
+        contract: &'a ZkContract,
+    ) -> Self {
+        let mut witness_base = HashMap::new();
+        let mut witness_scalar = HashMap::new();
+        for (name, type_id) in contract.witness.iter() {
+            match type_id {
+                ZkType::Base => {
+                    witness_base.insert(name.clone(), None);
+                }
+                ZkType::Scalar => {
+                    witness_scalar.insert(name.clone(), None);
+                }
+                _ => {
+                    unimplemented!();
+                }
+            }
+        }
+
+        Self {
+            const_fixed_points,
+            constants,
+            contract,
+            witness_base,
+            witness_scalar,
+        }
+    }
+
+    pub fn witness_base(&mut self, name: &str, value: pallas::Base) -> Result<()> {
+        for (variable, type_id) in self.contract.witness.iter() {
+            if name != variable {
+                continue;
+            }
+            if *type_id != ZkType::Base {
+                return Err(Error::InvalidParamType);
+            }
+            *self.witness_base.get_mut(name).unwrap() = Some(value);
+            return Ok(());
+        }
+        return Err(Error::InvalidParamName);
+    }
+
+    pub fn witness_scalar(&mut self, name: &str, value: pallas::Scalar) -> Result<()> {
+        for (variable, type_id) in self.contract.witness.iter() {
+            if name != variable {
+                continue;
+            }
+            if *type_id != ZkType::Scalar {
+                return Err(Error::InvalidParamType);
+            }
+            *self.witness_scalar.get_mut(name).unwrap() = Some(value);
+            return Ok(());
+        }
+        return Err(Error::InvalidParamName);
+    }
+}
+
+impl<'a> UtilitiesInstructions<pallas::Base> for ZkCircuit<'a> {
+    type Var = CellValue<pallas::Base>;
+}
+
+impl<'a> Circuit<pallas::Base> for ZkCircuit<'a> {
+    type Config = MintConfig;
+    type FloorPlanner = SimpleFloorPlanner;
+
+    fn without_witnesses(&self) -> Self {
+        Self {
+            const_fixed_points: self.const_fixed_points.clone(),
+            constants: self.constants,
+            contract: &self.contract,
+            witness_base: self
+                .witness_base
+                .keys()
+                .map(|key| (key.clone(), None))
+                .collect(),
+            witness_scalar: self
+                .witness_scalar
+                .keys()
+                .map(|key| (key.clone(), None))
+                .collect(),
+        }
+    }
+
+    fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
+        let advices = [
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+        ];
+
+        let q_add = meta.selector();
+
+        let table_idx = meta.lookup_table_column();
+
+        let primary = meta.instance_column();
+
+        meta.enable_equality(primary.into());
+
+        for advice in advices.iter() {
+            meta.enable_equality((*advice).into());
+        }
+
+        let lagrange_coeffs = [
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+        ];
+
+        let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
+        let rc_b = lagrange_coeffs[5..8].try_into().unwrap();
+
+        meta.enable_constant(lagrange_coeffs[0]);
+
+        let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
+
+        let ecc_config = EccChip::<OrchardFixedBases>::configure(
+            meta,
+            advices,
+            lagrange_coeffs,
+            range_check.clone(),
+        );
+
+        let poseidon_config = PoseidonChip::configure(
+            meta,
+            OrchardNullifier,
+            advices[6..9].try_into().unwrap(),
+            advices[5],
+            rc_a,
+            rc_b,
+        );
+
+        let arith_config = ArithmeticChip::configure(meta);
+
+        MintConfig {
+            primary,
+            q_add,
+            advices,
+            ecc_config,
+            poseidon_config,
+            arith_config,
+        }
+    }
+
+    fn synthesize(
+        &self,
+        config: Self::Config,
+        mut layouter: impl Layouter<pallas::Base>,
+    ) -> std::result::Result<(), PlonkError> {
+        let ecc_chip = config.ecc_chip();
+        let arith_chip = config.arithmetic_chip();
+
+        let mut stack_base = Vec::new();
+        let mut stack_scalar = Vec::new();
+        let mut stack_ec_point = Vec::new();
+        let mut stack_ec_fixed_point = Vec::new();
+
+        // Load constants first onto the stacks
+        for (variable, type_id) in self.constants.iter() {
+            match *type_id {
+                ZkType::Base => {
+                    unimplemented!();
+                }
+                ZkType::Scalar => {
+                    unimplemented!();
+                }
+                ZkType::EcPoint => {
+                    unimplemented!();
+                }
+                ZkType::EcFixedPoint => {
+                    let value = self.const_fixed_points[variable];
+                    stack_ec_fixed_point.push(value);
+                }
+            }
+        }
+
+        // Push the witnesses onto the stacks in order
+        for (variable, type_id) in self.contract.witness.iter() {
+            match *type_id {
+                ZkType::Base => {
+                    let value = self.witness_base.get(variable).expect("witness base set");
+                    let value = self.load_private(
+                        layouter.namespace(|| "load pubkey x"),
+                        config.advices[0],
+                        *value,
+                    )?;
+                    stack_base.push(value.clone());
+                }
+                ZkType::Scalar => {
+                    let value = self.witness_scalar.get(variable).expect("witness base set");
+                    stack_scalar.push(value.clone());
+                }
+                ZkType::EcPoint => {
+                    unimplemented!();
+                }
+                ZkType::EcFixedPoint => {
+                    unimplemented!();
+                }
+            }
+        }
+
+        let mut current_instance_offset = 0;
+
+        for func_call in self.contract.code.iter() {
+            match func_call {
+                ZkFunctionCall::PoseidonHash(lhs_idx, rhs_idx) => {
+                    assert!(*lhs_idx < stack_base.len());
+                    assert!(*rhs_idx < stack_base.len());
+                    let messages = [stack_base[*lhs_idx], stack_base[*rhs_idx]];
+                    let poseidon_message = layouter.assign_region(
+                        || "load message",
+                        |mut region| {
+                            let mut message_word = |i: usize| {
+                                let val = messages[i].value();
+                                let var = region.assign_advice(
+                                    || format!("load message_{}", i),
+                                    config.poseidon_config.state()[i],
+                                    0,
+                                    || val.ok_or(plonk::Error::SynthesisError),
+                                )?;
+                                region.constrain_equal(var, messages[i].cell())?;
+                                Ok(Word::<_, _, OrchardNullifier, 3, 2>::from_inner(
+                                    StateWord::new(var, val),
+                                ))
+                            };
+                            Ok([message_word(0)?, message_word(1)?])
+                        },
+                    )?;
+
+                    let poseidon_hasher = PoseidonHash::init(
+                        config.poseidon_chip(),
+                        layouter.namespace(|| "Poseidon init"),
+                        ConstantLength::<2>,
+                    )?;
+
+                    let poseidon_output = poseidon_hasher
+                        .hash(layouter.namespace(|| "poseidon hash"), poseidon_message)?;
+
+                    let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
+                    stack_base.push(poseidon_output);
+                }
+                ZkFunctionCall::Add(lhs_idx, rhs_idx) => {
+                    assert!(*lhs_idx < stack_base.len());
+                    assert!(*rhs_idx < stack_base.len());
+                    let (lhs, rhs) = (stack_base[*lhs_idx], stack_base[*rhs_idx]);
+                    let output = arith_chip.add(
+                        layouter.namespace(|| "arithmetic add"),
+                        lhs, rhs)?;
+                    stack_base.push(output);
+                }
+                ZkFunctionCall::ConstrainInstance(arg_idx) => {
+                    assert!(*arg_idx < stack_base.len());
+                    let arg = stack_base[*arg_idx];
+                    layouter.constrain_instance(
+                        arg.cell(),
+                        config.primary,
+                        current_instance_offset,
+                    )?;
+                    current_instance_offset += 1;
+                }
+                ZkFunctionCall::EcMulShort(value_idx, point_idx) => {
+                    assert!(*value_idx < stack_base.len());
+                    let value = stack_base[*value_idx];
+
+                    assert!(*point_idx < stack_ec_fixed_point.len());
+                    let fixed_point = stack_ec_fixed_point[*point_idx];
+
+                    // This constant one is used for multiplication
+                    let one = self.load_constant(
+                        layouter.namespace(|| "constant one"),
+                        config.advices[0],
+                        pallas::Base::one(),
+                    )?;
+
+                    // v * G_1
+                    let (result, _) = {
+                        let value_commit_v = FixedPoint::from_inner(ecc_chip.clone(), fixed_point);
+                        value_commit_v.mul_short(
+                            layouter.namespace(|| "[value] ValueCommitV"),
+                            (value, one),
+                        )?
+                    };
+
+                    stack_ec_point.push(result);
+                }
+                ZkFunctionCall::EcMul(value_idx, point_idx) => {
+                    assert!(*value_idx < stack_scalar.len());
+                    let value = stack_scalar[*value_idx];
+
+                    assert!(*point_idx < stack_ec_fixed_point.len());
+                    let fixed_point = stack_ec_fixed_point[*point_idx];
+
+                    let (result, _) = {
+                        let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), fixed_point);
+                        value_commit_r
+                            .mul(layouter.namespace(|| "[value_blind] ValueCommitR"), value)?
+                    };
+
+                    stack_ec_point.push(result);
+                }
+                ZkFunctionCall::EcAdd(lhs_idx, rhs_idx) => {
+                    assert!(*lhs_idx < stack_ec_point.len());
+                    assert!(*rhs_idx < stack_ec_point.len());
+                    let lhs = &stack_ec_point[*lhs_idx];
+                    let rhs = &stack_ec_point[*rhs_idx];
+
+                    let result = lhs.add(layouter.namespace(|| "valuecommit"), &rhs)?;
+                    stack_ec_point.push(result);
+                }
+                ZkFunctionCall::EcGetX(arg_idx) => {
+                    assert!(*arg_idx < stack_ec_point.len());
+                    let arg = &stack_ec_point[*arg_idx];
+                    let x = arg.inner().x();
+                    stack_base.push(x);
+                }
+                ZkFunctionCall::EcGetY(arg_idx) => {
+                    assert!(*arg_idx < stack_ec_point.len());
+                    let arg = &stack_ec_point[*arg_idx];
+                    let y = arg.inner().y();
+                    stack_base.push(y);
+                }
+            }
+        }
+
+        // At this point we've enforced all of our public inputs.
+        Ok(())
+    }
+}

+ 103 - 0
example/vm/src/vm2_serial.rs

@@ -0,0 +1,103 @@
+use std::io;
+
+use crate::error::{Error, Result};
+use crate::impl_vec;
+use crate::serial::{Decodable, Encodable, ReadExt, VarInt};
+use crate::vm2::{ZkBinary, ZkContract, ZkFunctionCall, ZkType};
+
+impl_vec!((String, ZkType));
+impl_vec!(ZkFunctionCall);
+impl_vec!((String, ZkContract));
+
+impl Encodable for ZkType {
+    fn encode<S: io::Write>(&self, _s: S) -> Result<usize> {
+        unimplemented!();
+        //Ok(0)
+    }
+}
+
+impl Decodable for ZkType {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let op_type = ReadExt::read_u8(&mut d)?;
+        match op_type {
+            0 => Ok(Self::Base),
+            1 => Ok(Self::Scalar),
+            2 => Ok(Self::EcPoint),
+            3 => Ok(Self::EcFixedPoint),
+            _i => Err(Error::BadOperationType),
+        }
+    }
+}
+
+impl Encodable for ZkFunctionCall {
+    fn encode<S: io::Write>(&self, _s: S) -> Result<usize> {
+        unimplemented!();
+        //Ok(0)
+    }
+}
+
+impl Decodable for ZkFunctionCall {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let func_id = ReadExt::read_u8(&mut d)?;
+        match func_id {
+            0 => Ok(Self::PoseidonHash(
+                ReadExt::read_u32(&mut d)? as usize,
+                ReadExt::read_u32(&mut d)? as usize,
+            )),
+            1 => Ok(Self::Add(
+                ReadExt::read_u32(&mut d)? as usize,
+                ReadExt::read_u32(&mut d)? as usize,
+            )),
+            2 => Ok(Self::ConstrainInstance(ReadExt::read_u32(&mut d)? as usize)),
+            3 => Ok(Self::EcMulShort(
+                ReadExt::read_u32(&mut d)? as usize,
+                ReadExt::read_u32(&mut d)? as usize,
+            )),
+            4 => Ok(Self::EcMul(
+                ReadExt::read_u32(&mut d)? as usize,
+                ReadExt::read_u32(&mut d)? as usize,
+            )),
+            5 => Ok(Self::EcAdd(
+                ReadExt::read_u32(&mut d)? as usize,
+                ReadExt::read_u32(&mut d)? as usize,
+            )),
+            6 => Ok(Self::EcGetX(ReadExt::read_u32(&mut d)? as usize)),
+            7 => Ok(Self::EcGetY(ReadExt::read_u32(&mut d)? as usize)),
+            _i => Err(Error::BadOperationType),
+        }
+    }
+}
+
+impl Encodable for ZkBinary {
+    fn encode<S: io::Write>(&self, _s: S) -> Result<usize> {
+        unimplemented!();
+        //Ok(0)
+    }
+}
+
+impl Decodable for ZkBinary {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            constants: Decodable::decode(&mut d)?,
+            contracts: Vec::<(String, ZkContract)>::decode(&mut d)?
+                .into_iter()
+                .collect(),
+        })
+    }
+}
+
+impl Encodable for ZkContract {
+    fn encode<S: io::Write>(&self, _s: S) -> Result<usize> {
+        unimplemented!();
+        //Ok(0)
+    }
+}
+
+impl Decodable for ZkContract {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            witness: Decodable::decode(&mut d)?,
+            code: Decodable::decode(&mut d)?,
+        })
+    }
+}