mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
move vm from examples/ to example/
This commit is contained in:
47
example/vm/Cargo.toml
Normal file
47
example/vm/Cargo.toml
Normal 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
9
example/vm/doit.sh
Executable 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
|
||||
|
||||
203
example/vm/src/arith_chip.rs
Normal file
203
example/vm/src/arith_chip.rs
Normal 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
210
example/vm/src/bin/vm2.rs
Normal 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(¶ms, &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(¶ms, &empty_circuit).unwrap();
|
||||
let pk = plonk::keygen_pk(¶ms, 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
137
example/vm/src/endian.rs
Normal 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
113
example/vm/src/error.rs
Normal 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
22
example/vm/src/lib.rs
Normal 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
870
example/vm/src/serial.rs
Normal 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
442
example/vm/src/vm2.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
103
example/vm/src/vm2_serial.rs
Normal file
103
example/vm/src/vm2_serial.rs
Normal 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)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user