move vm from examples/ to example/

This commit is contained in:
narodnik
2021-10-30 13:32:35 +02:00
parent 4999914663
commit 1a5c01ff76
10 changed files with 2156 additions and 0 deletions

47
example/vm/Cargo.toml Normal file
View File

@@ -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
example/vm/doit.sh Executable file
View File

@@ -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

View File

@@ -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
example/vm/src/bin/vm2.rs Normal file
View File

@@ -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
example/vm/src/endian.rs Normal file
View File

@@ -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
example/vm/src/error.rs Normal file
View File

@@ -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
example/vm/src/lib.rs Normal file
View File

@@ -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
example/vm/src/serial.rs Normal file
View File

@@ -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
example/vm/src/vm2.rs Normal file
View File

@@ -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(())
}
}

View File

@@ -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)?,
})
}
}