Merge branch 'halo2-integration'

This commit is contained in:
lunar-mining
2021-11-27 11:41:12 +01:00
109 changed files with 25913 additions and 6440 deletions

1544
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "darkfi"
version = "0.0.1"
version = "0.2.0"
homepage = "https://dark.fi"
description = "Anonymous. Uncensored. Sovereign."
authors = ["darkfi <dev@dark.fi>"]
@@ -11,77 +11,97 @@ edition = "2021"
[lib]
name = "drk"
[dependencies.halo2]
version = "=0.1.0-beta.1"
features = ["dev-graph", "gadget-traces", "sanity-checks"]
[dependencies.halo2_gadgets]
git = "https://github.com/parazyd/halo2_gadgets.git"
rev = "8238cb3471b798c76dd53b278524fc80685c7d4f"
features = ["dev-graph", "test-dependencies"]
[dependencies.incrementalmerkletree]
git = "https://github.com/zcash/incrementalmerkletree.git"
rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb"
[dependencies.rocksdb]
# TODO: Revert to upstream after bd966750ec861d687913d59a9939a1408ac53131 is merged.
git = "https://github.com/parazyd/rust-rocksdb"
rev = "bd966750ec861d687913d59a9939a1408ac53131"
default-features = false
features = ["lz4"]
[dependencies]
ff = "0.8"
group = "0.8"
bellman = { version = "0.8", default-features = false, features = ["groth16"] }
bls12_381 = "0.3.1"
jubjub = "0.5.1"
zcash_primitives = "0.5.0"
zcash_proofs = "0.5.0"
rand = "0.7.3"
rand_core = "0.5.1"
# Crypto
pasta_curves = "0.2.1"
rand = "0.8.4"
num-bigint = {version = "0.4.2", features = ["rand"]}
blake2b_simd = "0.5.11"
blake2s_simd = "0.5.11"
group = "0.11.0"
crypto_api_chachapoly = "0.5.0"
arrayvec = "0.7.0"
sha2 = "0.9.8"
ripemd160 = "0.9.1"
blake2s_simd = "0.5"
blake2b_simd = "0.5.11"
crypto_api_chachapoly = "0.4"
bitvec = "0.18"
bimap = "0.5.2"
hex = "0.4.2"
# Constant time options which is used for MerkleNode incrementalmerkletree
subtle = "2.3"
# Encoding and parsing
bs58 = "0.4.0"
prettytable-rs = "0.8"
num_cpus = "1.13.0"
num-bigint = {version = "0.3.2", features = ["rand", "serde"]}
smol = "1.2.5"
futures = "0.3.17"
async-channel = "1.6.1"
async-trait = "0.1.51"
async-executor = "1.4.1"
async-std = "1.10.0"
async-native-tls = "0.3.3"
native-tls = "0.2.8"
easy-parallel = "3.1.0"
tungstenite = "0.15.0"
async-tungstenite = "0.15.0"
simple_logger = "1.13.0"
log = "0.4.14"
clap = "2.33.3"
bytes = "1.1.0"
hex = "0.4.3"
toml = "0.5.8"
dirs = "4.0.0"
url = "2.2.2"
serde = {version = "1.0.130", features = ["derive"]}
serde_json = "1.0.68"
bytes = "1.0.1"
serde = {version = "1.0.130", features = ["derive"]}
# Async
async-std = "1.10.0"
async-trait = "0.1.51"
async-channel = "1.6.1"
native-tls = "0.2.8"
async-native-tls = "0.3.3"
async-executor = "1.4.1"
futures = "0.3.17"
smol = "1.2.5"
# Utilities
anyhow = "1.0.44"
dirs = "4.0.0"
failure = "0.1.8"
lazy_static = "1.4.0"
log = "0.4.14"
num_cpus = "1.13.0"
simple_logger = "1.13.0"
signal-hook = "0.3.10"
signal-hook-async-std = "0.2.1"
lazy_static = "1.4.0"
thiserror = "1.0.30"
rocksdb = {version = "0.16.0", default-features = false, features = ["lz4"]}
zeromq = {version = "0.2.2", default-features = false, features = ["async-std-runtime", "all-transport"]}
rusqlite = {version = "0.26.0", features = ["bundled-sqlcipher"]}
# Used for Websockets client implementation.
async-tungstenite = "0.15.0"
tungstenite = "0.15.0"
## Cashier Solana Dependencies
solana-sdk = {version = "1.8.1", optional = true}
solana-client = {version = "1.8.1", optional = true}
spl-token = {version = "3.2.0", features = ["no-entrypoint"], optional = true}
spl-associated-token-account = {version = "1.0.3", features = ["no-entrypoint"], optional = true}
# Used for wallet management.
rusqlite = {version = "0.26.1", features = ["bundled-sqlcipher"]}
## Cashier Bitcoin Dependencies
anyhow = "1.0.44"
# Used for gatewayd network transport.
zeromq = {version = "0.3.0", default-features = false, features = ["async-std-runtime", "all-transport"]}
# Cashier Bitcoin dependencies
bdk = {version = "0.12.0", optional = true}
bitcoin = {version = "0.27.0", optional = true }
bitcoin = {version = "0.27.0", optional = true}
secp256k1 = {version = "0.20.3", default-features = false, features = ["rand-std"], optional = true}
## Cashier Ethereum Dependencies
# Cashier Ethereum dependencies
hash-db = {version = "0.15.2", optional = true}
keccak-hasher = {version = "0.15.3", optional = true}
# Cashier Solana dependencies
solana-client = {version = "1.8.2", optional = true}
solana-sdk = {version = "1.8.2", optional = true}
spl-associated-token-account = {version = "1.0.3", features = ["no-entrypoint"], optional = true}
spl-token = {version = "3.2.0", features = ["no-entrypoint"], optional = true}
[features]
btc = ["bdk", "bitcoin", "secp256k1"]
sol = ["solana-sdk", "solana-client", "spl-token", "spl-associated-token-account"]
eth = ["keccak-hasher", "hash-db"]
sol = ["solana-sdk", "solana-client", "spl-token", "spl-associated-token-account"]

View File

@@ -1,14 +1,13 @@
.POSIX:
# Install prefix
PREFIX = /usr/local
DLURL = https://testnet.cashier.dark.fi
# Cargo binary
CARGO = cargo
DLTOOL = wget -nv --show-progress -O-
#DLTOOL = curl
# Here it's possible to append "cashierd" and "gatewayd".
BINS = drk darkfid
BINS = drk darkfid cashierd gatewayd
# Dependencies which should force the binaries to be rebuilt
BINDEPS = \
@@ -17,23 +16,22 @@ BINDEPS = \
$(shell find token -type f) \
$(shell find sql -type f)
all: $(BINS) mint.params spend.params
#all: $(BINS)
all:
cargo build --release --all-features --lib
$(BINS): $(BINDEPS)
$(CARGO) build --release --all-features --bin $@
cp target/release/$@ $@
%.params:
$(DLTOOL) $(DLURL)/$@ > $@
test:
$(CARGO) test --release --all-features
$(CARGO) test --release --all-features --lib
fix:
$(CARGO) fix --release --all-features --allow-dirty
clippy:
$(CARGO) clippy --release --all-features
$(CARGO) clippy --release --all-features --lib
install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin
@@ -44,7 +42,6 @@ install: all
do \
cp -f example/config/$$i.toml $(DESTDIR)$(PREFIX)/share/doc/darkfi; \
done;
cp -f mint.params spend.params $(DESTDIR)$(PREFIX)/share/darkfi
uninstall:
for i in $(BINS); \
@@ -55,7 +52,7 @@ uninstall:
rm -rf $(DESTDIR)$(PREFIX)/share/darkfi
clean:
rm -f $(BINS) mint.params spend.params
rm -f $(BINS)
distclean: clean
rm -rf target

1213
example/halo2/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,47 +1,22 @@
[package]
name = "halo2_examples"
name = "drk_halo2"
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
authors = ["Ivan Jelincic <parazyd@dyne.org>"]
edition = "2021"
[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"
ff = "0.11.0"
pasta_curves = "0.2.1"
hex = "0.4.3"
failure = "0.1.8"
anyhow = "1.0.44"
[patch.crates-io]
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "27c4187673a9c6ade13fbdbd4f20955530c22d7f" }
[dependencies.halo2]
version = "=0.1.0-beta.1"
features = ["dev-graph", "gadget-traces", "sanity-checks"]
[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"
[dependencies.halo2_gadgets]
git = "https://github.com/parazyd/halo2_gadgets.git"
rev = "b45c527276bb2309f3b256eb5f45ccdcc5bd8c0f"
features = ["dev-graph", "test-dependencies"]

View File

@@ -1,6 +0,0 @@
Always use the --release flag otherwise it's too slow:
```
cargo run --release --bin simple3
```

View File

@@ -0,0 +1,303 @@
// https://docs.vocdoni.io/architecture/protocol/anonymous-voting/zk-census-proof.html#protocol-design
use anyhow::Result;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
};
use halo2_gadgets::{
ecc::chip::{EccChip, EccConfig},
poseidon::{Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig},
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::chip::{MerkleChip, MerkleConfig},
},
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions},
};
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{ff::PrimeFieldBits, Curve},
pallas,
};
use rand::rngs::OsRng;
use drk_halo2::{
constants::{
sinsemilla::{OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION},
OrchardFixedBases,
},
crypto::pedersen_commitment,
spec::i2lebsp,
};
#[derive(Clone, Debug)]
struct VoteConfig {
primary: Column<InstanceColumn>,
advices: [Column<Advice>; 10],
ecc_config: EccConfig,
merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_1:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_2:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
poseidon_config: PoseidonConfig<pallas::Base>,
}
impl VoteConfig {
fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
EccChip::construct(self.ecc_config.clone())
}
fn sinsemilla_chip_1(
&self,
) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
SinsemillaChip::construct(self.sinsemilla_config_1.clone())
}
fn sinsemilla_chip_2(
&self,
) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
SinsemillaChip::construct(self.sinsemilla_config_2.clone())
}
fn merkle_chip_1(
&self,
) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
MerkleChip::construct(self.merkle_config_1.clone())
}
fn merkle_chip_2(
&self,
) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
MerkleChip::construct(self.merkle_config_2.clone())
}
fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}
// The public input array offsets
const VOTE_MERKLE_ROOT_OFFSET: usize = 0;
const VOTE_NULLIFIER_OFFSET: usize = 1;
const VOTE_PROCESS_ID_OFFSET: usize = 2;
const VOTE_COMMITX_OFFSET: usize = 3;
const VOTE_COMMITY_OFFSET: usize = 4;
#[derive(Default, Debug)]
struct VoteCircuit {
index: Option<pallas::Base>,
secret_key: Option<pallas::Base>,
merkle_proof: Option<pallas::Base>,
}
impl UtilitiesInstructions<pallas::Base> for VoteCircuit {
type Var = CellValue<pallas::Base>;
}
impl Circuit<pallas::Base> for VoteCircuit {
type Config = VoteConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
// Advice columns used in the circuit
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(),
];
// Fixed columns for the Sinsemilla generator lookup table
let table_idx = meta.lookup_table_column();
let lookup = (
table_idx,
meta.lookup_table_column(),
meta.lookup_table_column(),
);
// Instance column used for public inputs
let primary = meta.instance_column();
meta.enable_equality(primary.into());
// Permutation over all advice columns
for advice in advices.iter() {
meta.enable_equality((*advice).into());
}
// Poseidon requires four advice columns, while ECC incomplete addition
// requires six. We can reduce the proof size by sharing fixed columns
// between the ECC and Poseidon chips.
// TODO: For multiple invocations they could/should be configured in
// parallel rather than sharing perhaps?
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();
// Also use the first Lagrange coefficient column for loading global constants.
meta.enable_constant(lagrange_coeffs[0]);
// Use one of the right-most advice columns for all of our range checks.
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
// Configuration for curve point operations.
// This uses 10 advice columns and spans the whole circuit.
let ecc_config = EccChip::<OrchardFixedBases>::configure(
meta,
advices,
lagrange_coeffs,
range_check.clone(),
);
// Configuration for the Poseidon hash
let poseidon_config = PoseidonChip::configure(
meta,
P128Pow5T3,
advices[6..9].try_into().unwrap(),
advices[5],
rc_a,
rc_b,
);
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_1, merkle_config_1) = {
let sinsemilla_config_1 = SinsemillaChip::configure(
meta,
advices[..5].try_into().unwrap(),
advices[6],
lagrange_coeffs[0],
lookup,
range_check.clone(),
);
let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
(sinsemilla_config_1, merkle_config_1)
};
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_2, merkle_config_2) = {
let sinsemilla_config_2 = SinsemillaChip::configure(
meta,
advices[5..].try_into().unwrap(),
advices[7],
lagrange_coeffs[1],
lookup,
range_check,
);
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
(sinsemilla_config_2, merkle_config_2)
};
VoteConfig {
primary,
advices,
ecc_config,
merkle_config_1,
merkle_config_2,
sinsemilla_config_1,
sinsemilla_config_2,
poseidon_config,
}
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
// Load the Sinsemilla generator lookup table used by the whole circuit.
SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
Ok(())
}
}
fn root(path: [pallas::Base; 32], leaf_pos: u32, leaf: pallas::Base) -> pallas::Base {
let domain = primitives::sinsemilla::HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let pos_bool = i2lebsp::<32>(leaf_pos as u64);
let mut node = leaf;
for (l, (sibling, pos)) in path.iter().zip(pos_bool.iter()).enumerate() {
let (left, right) = if *pos {
(*sibling, node)
} else {
(node, *sibling)
};
let l_star = i2lebsp::<10>(l as u64);
let left: Vec<_> = left.to_le_bits().iter().by_val().take(255).collect();
let right: Vec<_> = right.to_le_bits().iter().by_val().take(255).collect();
let mut message = l_star.to_vec();
message.extend_from_slice(&left);
message.extend_from_slice(&right);
node = domain.hash(message.into_iter()).unwrap();
}
node
}
fn main() -> Result<()> {
// The number of rows in our circuit cannot exceed 2^k
// let k: u32 = 11;
// Voter is the owner of the secret key corresponding to a certain zkCensusKey.
let secret_key = pallas::Base::random(&mut OsRng);
let zk_census_key =
primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<1>).hash([secret_key]);
// Voter's zkCensusKey is included in the census Merkle Tree
let leaf = zk_census_key.clone();
let pos = rand::random::<u32>();
let path: Vec<_> = (0..32).map(|_| pallas::Base::random(&mut OsRng)).collect();
let merkle_root = root(path.clone().try_into().unwrap(), pos, leaf);
// The nullifier provided by Voter uniquely corresponds to their secret
// key and the process ID for a specific voting process.
let process_id = pallas::Base::from(42);
let nullifier = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>)
.hash([secret_key, process_id]);
// The vote itself
let vote_blind = pallas::Scalar::random(&mut OsRng);
let vote = pedersen_commitment(1, vote_blind);
let vote_coords = vote.to_affine().coordinates().unwrap();
let _public_inputs = [
merkle_root,
nullifier,
process_id,
*vote_coords.x(),
*vote_coords.y(),
];
Ok(())
}

View File

@@ -1,73 +0,0 @@
use std::iter;
use group::{ff::PrimeFieldBits, Curve};
use halo2::{
arithmetic::{CurveAffine, Field, FieldExt},
pasta::{Fp, Fq},
};
use halo2_ecc::gadget::FixedPoints;
use halo2_poseidon::primitive::{ConstantLength, Hash, P128Pow5T3 as OrchardNullifier};
use orchard::constants::{fixed_bases::OrchardFixedBases, sinsemilla::MERKLE_CRH_PERSONALIZATION};
use rand::rngs::OsRng;
use sinsemilla::primitive::{CommitDomain, HashDomain};
use halo2_examples::pedersen_commitment;
fn main() {
let secret_key = Fq::random(&mut OsRng);
let serial = Fp::random(&mut OsRng);
// Sinsemilla hash
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let nullifier = domain
.hash(
iter::empty()
.chain(secret_key.to_le_bits().iter().by_val())
.chain(serial.to_le_bits().iter().by_val()),
)
.unwrap();
let public_key = OrchardFixedBases::SpendAuthG.generator() * secret_key;
let coords = public_key.to_affine().coordinates().unwrap();
let value = 110;
let asset = 1;
let value_blind = Fq::random(&mut OsRng);
let asset_blind = Fq::random(&mut OsRng);
let coin_blind = Fp::random(&mut OsRng);
// FIXME:
let messages = [
[*coords.x(), *coords.y()],
[Fp::from(value), Fp::from(asset)],
[serial, coin_blind],
];
let mut coin = Fp::zero();
for msg in messages.iter() {
coin += Hash::init(OrchardNullifier, ConstantLength::<2>).hash(*msg);
}
// TODO: Merkle
let value_commit = pedersen_commitment(value, value_blind);
let asset_commit = pedersen_commitment(asset, asset_blind);
let value_coords = value_commit.to_affine().coordinates().unwrap();
let asset_coords = value_commit.to_affine().coordinates().unwrap();
let sig_secret = Fq::random(&mut OsRng);
let sig_pubkey = OrchardFixedBases::SpendAuthG.generator() * sig_secret;
let sig_pk_coords = sig_pubkey.to_affine().coordinates().unwrap();
let mut public_inputs = vec![
nullifier,
*value_coords.x(),
*value_coords.y(),
*asset_coords.x(),
*asset_coords.y(),
// merkle_root,
*sig_pk_coords.x(),
*sig_pk_coords.y(),
];
}

View File

@@ -1,58 +1,106 @@
use std::{convert::TryInto, time::Instant};
use std::time::Instant;
use group::{ff::Field, Curve, Group};
use halo2::{
arithmetic::CurveAffine,
circuit::{floor_planner, Layouter},
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
pasta::{vesta, Ep, Fp, Fq},
plonk,
plonk::{Circuit, ConstraintSystem, Error},
poly::commitment,
transcript::{Blake2bRead, Blake2bWrite},
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn, Selector,
},
poly::Rotation,
};
use halo2_ecc::{chip::EccChip, gadget::FixedPoint};
use halo2_poseidon::{
gadget::{Hash as PoseidonHash, Word},
pow5t3::{Pow5T3Chip as PoseidonChip, StateWord},
primitive::{ConstantLength, Hash, P128Pow5T3 as OrchardNullifier},
use halo2_gadgets::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::chip::{MerkleChip, MerkleConfig},
},
utilities::{
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use halo2_utilities::{
lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{Curve, Group},
pallas,
};
use orchard::constants::fixed_bases::OrchardFixedBases;
use rand::rngs::OsRng;
use halo2_examples::{circuit::Config, pedersen_commitment};
use drk_halo2::{
constants::{
sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
OrchardFixedBases,
},
crypto::pedersen_commitment,
proof::{Proof, ProvingKey, VerifyingKey},
};
const K: u32 = 9;
#[derive(Clone, Debug)]
struct MintConfig {
primary: Column<InstanceColumn>,
q_add: Selector,
advices: [Column<Advice>; 10],
ecc_config: EccConfig,
merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_1:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_2:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
poseidon_config: PoseidonConfig<pallas::Base>,
}
impl MintConfig {
fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
EccChip::construct(self.ecc_config.clone())
}
fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}
// The public input array offsets
const MINT_COIN_OFFSET: usize = 0;
const MINT_VALCOMX_OFFSET: usize = 1;
const MINT_VALCOMY_OFFSET: usize = 2;
const MINT_ASSCOMX_OFFSET: usize = 3;
const MINT_ASSCOMY_OFFSET: usize = 4;
#[derive(Default, Debug)]
struct MintCircuit {
pub_x: Option<Fp>, // x coordinate for pubkey
pub_y: Option<Fp>, // y coordinate for pubkey
value: Option<Fp>, // The value of this coin
asset: Option<Fp>, // The asset ID
serial: Option<Fp>, // Unique serial number corresponding to this coin
coin_blind: Option<Fp>, // Random blinding factor for coin
value_blind: Option<Fq>, // Random blinding factor for value commitment
asset_blind: Option<Fq>, // Random blinding factor for the asset ID
pub_x: Option<pallas::Base>, // x coordinate for pubkey
pub_y: Option<pallas::Base>, // y coordinate for pubkey
value: Option<pallas::Base>, // The value of this coin
asset: Option<pallas::Base>, // The asset ID
serial: Option<pallas::Base>, // Unique serial number corresponding to this coin
coin_blind: Option<pallas::Base>, // Random blinding factor for coin
value_blind: Option<pallas::Scalar>, // Random blinding factor for value commitment
asset_blind: Option<pallas::Scalar>, // Random blinding factor for the asset ID
}
impl UtilitiesInstructions<Fp> for MintCircuit {
type Var = CellValue<Fp>;
impl UtilitiesInstructions<pallas::Base> for MintCircuit {
type Var = CellValue<pallas::Base>;
}
impl Circuit<Fp> for MintCircuit {
type Config = Config;
type FloorPlanner = floor_planner::V1;
//type FloorPlanner = SimpleFloorPlanner;
impl Circuit<pallas::Base> for MintCircuit {
type Config = MintConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
// Advice columns used in the circuit
let advices = [
meta.advice_column(),
meta.advice_column(),
@@ -66,24 +114,51 @@ impl Circuit<Fp> for MintCircuit {
meta.advice_column(),
];
// Addition of two field elements
/*
let q_add = meta.selector();
meta.create_gate("poseidon_hash(a, b) + c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[6], Rotation::cur());
let hash = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (hash + c - sum)]
});
*/
let q_add = meta.selector();
meta.create_gate("a+b+c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[5], Rotation::cur());
let a = meta.query_advice(advices[6], Rotation::cur());
let b = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (a + b + c - sum)]
});
// Fixed columns for the Sinsemilla generator lookup table
let table_idx = meta.lookup_table_column();
let lookup = (
table_idx,
meta.lookup_table_column(),
meta.lookup_table_column(),
);
// let lookup = (
// table_idx,
// meta.lookup_table_column(),
// meta.lookup_table_column(),
// );
// Instance column used for public inputs
let primary = meta.instance_column();
meta.enable_equality(primary.into());
// Permutation over all advice columns
for advice in advices.iter() {
meta.enable_equality((*advice).into());
}
// Poseidon requires four advice columns, while ECC incomplete addition
// requires six. We can reduce the proof size by sharing fixed columns
// between the ECC and Poseidon chips.
// TODO: For multiple invocations they could/should be configured in
// parallel rather than sharing perhaps?
let lagrange_coeffs = [
meta.fixed_column(),
meta.fixed_column(),
@@ -94,14 +169,17 @@ impl Circuit<Fp> for MintCircuit {
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();
// Also use the first Lagrange coefficient column for loading global constants.
meta.enable_constant(lagrange_coeffs[0]);
// Use one of the right-most advice columns for all of our range checks.
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
// Configuration for curve point operations.
// This uses 10 advice columns and spans the whole circuit.
let ecc_config = EccChip::<OrchardFixedBases>::configure(
meta,
advices,
@@ -109,20 +187,60 @@ impl Circuit<Fp> for MintCircuit {
range_check.clone(),
);
// Configuration for the Poseidon hash
let poseidon_config = PoseidonChip::configure(
meta,
OrchardNullifier,
P128Pow5T3,
advices[6..9].try_into().unwrap(),
advices[5],
rc_a,
rc_b,
);
Config {
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_1, merkle_config_1) = {
let sinsemilla_config_1 = SinsemillaChip::configure(
meta,
advices[..5].try_into().unwrap(),
advices[6],
lagrange_coeffs[0],
lookup,
range_check.clone(),
);
let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
(sinsemilla_config_1, merkle_config_1)
};
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_2, merkle_config_2) = {
let sinsemilla_config_2 = SinsemillaChip::configure(
meta,
advices[5..].try_into().unwrap(),
advices[7],
lagrange_coeffs[1],
lookup,
range_check,
);
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
(sinsemilla_config_2, merkle_config_2)
};
MintConfig {
primary,
q_add,
advices,
ecc_config,
merkle_config_1,
merkle_config_2,
sinsemilla_config_1,
sinsemilla_config_2,
poseidon_config,
}
}
@@ -130,120 +248,142 @@ impl Circuit<Fp> for MintCircuit {
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<Fp>,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
// Construct the ECC chip.
let ecc_chip = EccChip::construct(config.ecc_config.clone());
// Load the Sinsemilla generator lookup table used by the whole circuit.
SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
let ecc_chip = config.ecc_chip();
let pub_x = self.load_private(
layouter.namespace(|| "load pubkey x"),
config.advices[0],
self.pub_x,
)?;
let pub_y = self.load_private(
layouter.namespace(|| "load pubkey y"),
config.advices[0],
self.pub_y,
)?;
let value = self.load_private(
layouter.namespace(|| "load value"),
config.advices[0],
self.value,
)?;
let asset = self.load_private(
layouter.namespace(|| "load asset"),
config.advices[0],
self.asset,
)?;
let serial = self.load_private(
layouter.namespace(|| "load serial"),
config.advices[0],
self.serial,
)?;
let coin_blind = self.load_private(
layouter.namespace(|| "load coin_blind"),
config.advices[0],
self.coin_blind,
)?;
// =============
// = Coin hash =
// =============
// TODO: This is a hack until issue is resolved in poseidon gadget
let mut coin = Fp::zero();
// =========
// Coin hash
// =========
let messages = [[pub_x, pub_y], [value, asset], [serial, coin_blind]];
//let messages = [[pub_x, pub_y], [value, asset]];
//let messages = [[pub_x, pub_y]];
for msg in messages.iter() {
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let val = msg[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| val.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, msg[i].cell())?;
Ok(Word::<_, _, OrchardNullifier, 3, 2>::from_inner(
StateWord::new(var, val),
))
};
Ok([message_word(0)?, message_word(1)?])
},
)?;
let mut hashes = vec![];
let poseidon_hasher = PoseidonHash::init(
PoseidonChip::construct(config.poseidon_config.clone()),
layouter.namespace(|| "Poseidon init"),
ConstantLength::<2>,
)?;
for message in messages.iter() {
let hash = {
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, P128Pow5T3, 3, 2>::from_inner(StateWord::new(
var, value,
)))
};
Ok([message_word(0)?, message_word(1)?])
},
)?;
let poseidon_output =
poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
let poseidon_hasher = PoseidonHash::init(
config.poseidon_chip(),
layouter.namespace(|| "Poseidon init"),
ConstantLength::<2>,
)?;
let poseidon_output: CellValue<Fp> = poseidon_output.inner().into();
let poseidon_output = poseidon_hasher.hash(
layouter.namespace(|| "Poseidon hash (a, b)"),
poseidon_message,
)?;
if !poseidon_output.value().is_none() {
coin += poseidon_output.value().unwrap();
}
let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
poseidon_output
};
hashes.push(hash);
}
// if coin != Fp::zero() {
// println!("circuit hash: {:?}", coin);
// }
let coin = layouter.assign_region(
|| " `coin` = hash(a,b) + hash(c, d) + hash(e, f)",
|mut region| {
config.q_add.enable(&mut region, 0)?;
let hash = self.load_private(
layouter.namespace(|| "load hash"),
config.advices[0],
Some(coin),
copy(&mut region, || "copy ab", config.advices[6], 0, &hashes[0])?;
copy(&mut region, || "copy cd", config.advices[7], 0, &hashes[1])?;
copy(&mut region, || "copy ef", config.advices[8], 0, &hashes[2])?;
let scalar_val = hashes[0]
.value()
.zip(hashes[1].value())
.zip(hashes[2].value())
.map(|(abcd, ef)| abcd.0 + abcd.1 + ef);
let cell = region.assign_advice(
|| "hash(a,b)+hash(c,d)+hash(e,f)",
config.advices[5],
0,
|| scalar_val.ok_or(Error::SynthesisError),
)?;
Ok(CellValue::new(cell, scalar_val))
},
)?;
// Constrain the coin C; index in public values is 0
layouter.constrain_instance(hash.cell(), config.primary, 0)?;
// Constrain the coin C
layouter.constrain_instance(coin.cell(), config.primary, MINT_COIN_OFFSET)?;
// ====================
// = Value commitment =
// ====================
// ================
// Value commitment
// ================
// This constant one is used for multiplication
let one = self.load_constant(
layouter.namespace(|| "constant one"),
// This constant one is used for short multiplication
let one = self.load_private(
layouter.namespace(|| "load constant one"),
config.advices[0],
Fp::one(),
Some(pallas::Base::one()),
)?;
// v*G_1
// v * G_1
let (commitment, _) = {
let value_commit_v = OrchardFixedBases::ValueCommitV;
let value_commit_v = FixedPoint::from_inner(ecc_chip.clone(), value_commit_v);
value_commit_v.mul_short(layouter.namespace(|| "[value] ValueCommitV"), (value, one))?
};
// r_V*G_2
// r_V * G_2
let (blind, _rcv) = {
let rcv = self.value_blind;
let value_commit_r = OrchardFixedBases::ValueCommitR;
@@ -251,137 +391,83 @@ impl Circuit<Fp> for MintCircuit {
value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
};
// Constrain the x and y; indexes in public values are 1 and 2
// Constrain the value commitment coordinates
let value_commit = commitment.add(layouter.namespace(|| "valuecommit"), &blind)?;
layouter.constrain_instance(value_commit.inner().x().cell(), config.primary, 1)?;
layouter.constrain_instance(value_commit.inner().y().cell(), config.primary, 2)?;
layouter.constrain_instance(
value_commit.inner().x().cell(),
config.primary,
MINT_VALCOMX_OFFSET,
)?;
layouter.constrain_instance(
value_commit.inner().y().cell(),
config.primary,
MINT_VALCOMY_OFFSET,
)?;
// ====================
// = Asset commitment =
// ====================
// a*G_1
// ================
// Asset commitment
// ================
// a * G_1
let (commitment, _) = {
let asset_commit_v = OrchardFixedBases::ValueCommitV;
let asset_commit_v = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_v);
asset_commit_v.mul_short(layouter.namespace(|| "[asset] ValueCommitV"), (asset, one))?
};
// r_A*G_2
// r_A * G_2
let (blind, _rca) = {
let rca = self.asset_blind;
let asset_commit_r = OrchardFixedBases::ValueCommitR;
let asset_commit_r = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_r);
let asset_commit_r = FixedPoint::from_inner(ecc_chip, asset_commit_r);
asset_commit_r.mul(layouter.namespace(|| "[asset_blind] ValueCommitR"), rca)?
};
// Constrain the x and y; indexes in public values are 3 and 4
// Constrain the asset commitment coordinates
let asset_commit = commitment.add(layouter.namespace(|| "assetcommit"), &blind)?;
layouter.constrain_instance(asset_commit.inner().x().cell(), config.primary, 3)?;
layouter.constrain_instance(asset_commit.inner().y().cell(), config.primary, 4)?;
layouter.constrain_instance(
asset_commit.inner().x().cell(),
config.primary,
MINT_ASSCOMX_OFFSET,
)?;
layouter.constrain_instance(
asset_commit.inner().y().cell(),
config.primary,
MINT_ASSCOMY_OFFSET,
)?;
// At this point we've enforced all of our public inputs.
Ok(())
}
}
#[derive(Debug)]
struct VerifyingKey {
params: commitment::Params<vesta::Affine>,
vk: plonk::VerifyingKey<vesta::Affine>,
}
impl VerifyingKey {
fn build() -> Self {
let params = commitment::Params::new(K);
let circuit: MintCircuit = Default::default();
let vk = plonk::keygen_vk(&params, &circuit).unwrap();
VerifyingKey { params, vk }
}
}
#[derive(Debug)]
struct ProvingKey {
params: commitment::Params<vesta::Affine>,
pk: plonk::ProvingKey<vesta::Affine>,
}
impl ProvingKey {
fn build() -> Self {
let params = commitment::Params::new(K);
let circuit: MintCircuit = Default::default();
let vk = plonk::keygen_vk(&params, &circuit).unwrap();
let pk = plonk::keygen_pk(&params, vk, &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: &[MintCircuit], pubinputs: &[Fp]) -> Result<Self, 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: &[Fp]) -> 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(Error::ConstraintSystemFailure)
}
}
// fn new(bytes: Vec<u8>) -> Self {
// Proof(bytes)
// }
}
fn main() {
let pubkey = Ep::random(&mut OsRng);
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 11;
let pubkey = pallas::Point::random(&mut OsRng);
let coords = pubkey.to_affine().coordinates().unwrap();
let value = 110;
let value = 42;
let asset = 1;
let value_blind = Fq::random(&mut OsRng);
let asset_blind = Fq::random(&mut OsRng);
let value_blind = pallas::Scalar::random(&mut OsRng);
let asset_blind = pallas::Scalar::random(&mut OsRng);
let serial = Fp::random(&mut OsRng);
let coin_blind = Fp::random(&mut OsRng);
let serial = pallas::Base::random(&mut OsRng);
let coin_blind = pallas::Base::random(&mut OsRng);
let mut coin = Fp::zero();
// poseidon_hash(x, y) + poseidon_hash(value, asset) + poseidon_hash(serial, coin_blind)
let mut coin = pallas::Base::zero();
let messages = [
[*coords.x(), *coords.y()],
[Fp::from(value), Fp::from(asset)],
[pallas::Base::from(value), pallas::Base::from(asset)],
[serial, coin_blind],
];
// TODO: This is a hack until issue is fixed in poseidon gadget
for msg in messages.iter() {
coin += Hash::init(OrchardNullifier, ConstantLength::<2>).hash(*msg);
let hash = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
coin += hash;
}
let value_commit = pedersen_commitment(value, value_blind);
@@ -390,7 +476,7 @@ fn main() {
let asset_commit = pedersen_commitment(asset, asset_blind);
let asset_coords = asset_commit.to_affine().coordinates().unwrap();
let mut public_inputs = vec![
let public_inputs = vec![
coin,
*value_coords.x(),
*value_coords.y(),
@@ -401,33 +487,22 @@ fn main() {
let circuit = MintCircuit {
pub_x: Some(*coords.x()),
pub_y: Some(*coords.y()),
value: Some(vesta::Scalar::from(value)),
asset: Some(vesta::Scalar::from(asset)),
value: Some(pallas::Base::from(value)),
asset: Some(pallas::Base::from(asset)),
serial: Some(serial),
coin_blind: Some(coin_blind),
value_blind: Some(value_blind),
asset_blind: Some(asset_blind),
};
// Valid MockProver
let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap();
let prover = MockProver::run(k, &circuit, vec![public_inputs.clone()]).unwrap();
assert_eq!(prover.verify(), Ok(()));
// Add 1 to break the public inputs
public_inputs[0] += Fp::from(0xdeadbeef);
// Invalid MockProver
let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap();
assert!(prover.verify().is_err());
// Remove 1 to make the public inputs valid again
public_inputs[0] -= Fp::from(0xdeadbeef);
// Actual ZK proof
let start = Instant::now();
let vk = VerifyingKey::build();
let pk = ProvingKey::build();
println!("\nSetup: [{:?}]", start.elapsed());
let vk = VerifyingKey::build(k, MintCircuit::default());
let pk = ProvingKey::build(k, MintCircuit::default());
println!("Setup: [{:?}]", start.elapsed());
let start = Instant::now();
let proof = Proof::create(&pk, &[circuit], &public_inputs).unwrap();

View File

@@ -1,80 +1,58 @@
use std::convert::TryInto;
use std::time::Instant;
use halo2::{
circuit::{floor_planner, Layouter},
pasta::{vesta, Fp},
plonk,
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn, Selector,
},
poly::{commitment, Rotation},
transcript::{Blake2bRead, Blake2bWrite},
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
};
use halo2_poseidon::{
gadget::{Hash as PoseidonHash, Word},
pow5t3::{Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig, StateWord},
primitive::{ConstantLength, Hash, P128Pow5T3 as OrchardNullifier},
use halo2_gadgets::{
poseidon::{Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig},
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
utilities::{CellValue, UtilitiesInstructions, Var},
};
use halo2_utilities::{copy, CellValue, UtilitiesInstructions, Var};
use pasta_curves::pallas;
const K: u32 = 6;
#[derive(Clone, Debug)]
#[derive(Clone)]
struct Config {
primary: Column<InstanceColumn>,
q_add: Selector,
advices: [Column<Advice>; 10],
poseidon_config: PoseidonConfig<Fp>,
advices: [Column<Advice>; 5],
poseidon_config: PoseidonConfig<pallas::Base>,
}
#[derive(Default, Debug)]
impl Config {
fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}
#[derive(Default)]
struct HashCircuit {
a: Option<Fp>, // First input for hash
b: Option<Fp>, // Second input for hash
c: Option<Fp>, // c is summed with hash
a: Option<pallas::Base>,
b: Option<pallas::Base>,
c: Option<pallas::Base>,
d: Option<pallas::Base>,
}
impl UtilitiesInstructions<Fp> for HashCircuit {
type Var = CellValue<Fp>;
impl UtilitiesInstructions<pallas::Base> for HashCircuit {
type Var = CellValue<pallas::Base>;
}
impl Circuit<Fp> for HashCircuit {
impl Circuit<pallas::Base> for HashCircuit {
type Config = Config;
type FloorPlanner = floor_planner::V1;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
// 10 advice columns
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(),
];
// Addition of two field elements: poseidon_hash(a, b) + c
let q_add = meta.selector();
meta.create_gate("poseidon_hash(a, b) + c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[6], Rotation::cur());
let hash = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (hash + c - sum)]
});
let primary = meta.instance_column();
meta.enable_equality(primary.into());
@@ -100,16 +78,15 @@ impl Circuit<Fp> for HashCircuit {
let poseidon_config = PoseidonChip::configure(
meta,
OrchardNullifier,
advices[6..9].try_into().unwrap(),
advices[5],
P128Pow5T3,
advices[1..4].try_into().unwrap(),
advices[4],
rc_a,
rc_b,
);
Config {
primary,
q_add,
advices,
poseidon_config,
}
@@ -118,40 +95,20 @@ impl Circuit<Fp> for HashCircuit {
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<Fp>,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
let a = self.load_private(layouter.namespace(|| "load a"), config.advices[0], self.a)?;
let b = self.load_private(layouter.namespace(|| "load b"), config.advices[0], self.b)?;
let c = self.load_private(layouter.namespace(|| "load c"), config.advices[0], self.c)?;
let d = self.load_private(layouter.namespace(|| "load d"), config.advices[0], self.d)?;
let hash = {
let message = [a, b];
let poseidon_message = [a, b, c, d];
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, OrchardNullifier, 3, 2>::from_inner(
StateWord::new(var, value),
))
};
Ok([message_word(0)?, message_word(1)?])
},
)?;
let poseidon_hasher = PoseidonHash::init(
//config.poseidon_chip(),
PoseidonChip::construct(config.poseidon_config.clone()),
let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, _, 3, 2>::init(
config.poseidon_chip(),
layouter.namespace(|| "Poseidon init"),
ConstantLength::<2>,
ConstantLength::<4>,
)?;
let poseidon_output = poseidon_hasher.hash(
@@ -159,140 +116,37 @@ impl Circuit<Fp> for HashCircuit {
poseidon_message,
)?;
let poseidon_output: CellValue<Fp> = poseidon_output.inner().into();
let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
poseidon_output
};
// Add hash output to c
let scalar = layouter.assign_region(
|| " `scalar` = poseidon_hash(a, b) + c",
|mut region| {
config.q_add.enable(&mut region, 0)?;
layouter.constrain_instance(hash.cell(), config.primary, 0)?;
copy(&mut region, || "copy hash", config.advices[7], 0, &hash)?;
copy(&mut region, || "copy c", config.advices[8], 0, &c)?;
let scalar_val = hash.value().zip(c.value()).map(|(hash, c)| hash + c);
let cell = region.assign_advice(
|| "poseidon_hash(a, b) + c",
config.advices[6],
0,
|| scalar_val.ok_or(Error::SynthesisError),
)?;
Ok(CellValue::new(cell, scalar_val))
},
)?;
layouter.constrain_instance(scalar.cell(), config.primary, 0)
Ok(())
}
}
#[derive(Debug)]
struct VerifyingKey {
params: commitment::Params<vesta::Affine>,
vk: plonk::VerifyingKey<vesta::Affine>,
}
impl VerifyingKey {
fn build() -> Self {
let params = commitment::Params::new(K);
let circuit: HashCircuit = Default::default();
let vk = plonk::keygen_vk(&params, &circuit).unwrap();
VerifyingKey { params, vk }
}
}
#[derive(Debug)]
struct ProvingKey {
params: commitment::Params<vesta::Affine>,
pk: plonk::ProvingKey<vesta::Affine>,
}
impl ProvingKey {
fn build() -> Self {
let params = commitment::Params::new(K);
let circuit: HashCircuit = Default::default();
let vk = plonk::keygen_vk(&params, &circuit).unwrap();
let pk = plonk::keygen_pk(&params, vk, &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: &[HashCircuit], pubinputs: &[Fp]) -> Result<Self, 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: &[Fp]) -> 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(Error::ConstraintSystemFailure)
}
}
// fn new(bytes: Vec<u8>) -> Self {
// Proof(bytes)
// }
}
fn main() {
let a = Fp::from(13);
let b = Fp::from(69);
let c = Fp::from(42);
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 9;
let message = [a, b];
let output = Hash::init(OrchardNullifier, ConstantLength::<2>).hash(message);
let a = pallas::Base::from(1);
let b = pallas::Base::from(2);
let c = pallas::Base::from(3);
let d = pallas::Base::from(4);
let message = [a, b, c, d];
let hash = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<4>).hash(message);
let circuit = HashCircuit {
a: Some(a),
b: Some(b),
c: Some(c),
d: Some(d),
};
let sum = output + c;
let public_inputs = vec![hash];
// Correct:
let public_inputs = vec![sum];
// Incorrect:
// let public_inputs = vec![sum + Fp::one()];
let start = Instant::now();
let vk = VerifyingKey::build();
let pk = ProvingKey::build();
println!("Setup: [{:?}]", 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());
let prover = MockProver::run(k, &circuit, vec![public_inputs]).unwrap();
assert_eq!(prover.verify(), Ok(()));
}

View File

@@ -9,10 +9,7 @@ use halo2::{
poly::Rotation,
};
use halo2_gadgets::{
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
poseidon::{Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig},
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
utilities::{copy, CellValue, UtilitiesInstructions, Var},
@@ -138,29 +135,9 @@ impl Circuit<pallas::Base> for HashCircuit {
let c = self.load_private(layouter.namespace(|| "load c"), config.advices[0], self.c)?;
let hash = {
let message = [a, b];
let poseidon_message = [a, b];
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, P128Pow5T3, 3, 2>::from_inner(StateWord::new(
var, value,
)))
};
Ok([message_word(0)?, message_word(1)?])
},
)?;
let poseidon_hasher = PoseidonHash::init(
let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, _, 3, 2>::init(
config.poseidon_chip(),
layouter.namespace(|| "Poseidon init"),
ConstantLength::<2>,
@@ -235,14 +212,17 @@ fn main() {
// Actual ZK proof
let start = Instant::now();
let vk = VerifyingKey::build(k, HashCircuit::default());
let pk = ProvingKey::build(k, HashCircuit::default());
println!("Setup: [{:?}]", start.elapsed());
println!("Setup Prover: [{:?}]", start.elapsed());
let start = Instant::now();
let proof = Proof::create(&pk, &[circuit], &public_inputs).unwrap();
println!("Prove: [{:?}]", start.elapsed());
let start = Instant::now();
let vk = VerifyingKey::build(k, HashCircuit::default());
println!("Setup Verifier: [{:?}]", start.elapsed());
let start = Instant::now();
assert!(proof.verify(&vk, &public_inputs).is_ok());
println!("Verify: [{:?}]", start.elapsed());

View File

@@ -1,130 +0,0 @@
use halo2::{
circuit::{SimpleFloorPlanner, Chip, Layouter},
pasta::{EqAffine, Fp},
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Selector, create_proof, verify_proof, keygen_vk, keygen_pk},
poly::{commitment::Params, Rotation},
transcript::{Blake2bRead, Blake2bWrite, Challenge255},
};
use std::time::Instant;
#[derive(Clone, Debug)]
struct CoolConfig {
a_col: Column<Advice>,
s_range: Selector,
}
struct CoolChip {
config: CoolConfig
}
impl Chip<Fp> for CoolChip {
type Config = CoolConfig;
type Loaded = ();
fn config(&self) -> &Self::Config {
&self.config
}
fn loaded(&self) -> &Self::Loaded {
&()
}
}
impl CoolChip {
fn construct(config: CoolConfig) -> Self {
Self { config }
}
fn configure(cs: &mut ConstraintSystem<Fp>) -> CoolConfig {
let a_col = cs.advice_column();
let s_range = cs.selector();
cs.create_gate("check", |cs| {
let a = cs.query_advice(a_col, Rotation::cur());
let s_range = cs.query_selector(s_range);
vec![s_range * (a - Expression::Constant(Fp::from(2)))]
});
CoolConfig { a_col, s_range }
}
fn alloc_and_check(
&self,
layouter: &mut impl Layouter<Fp>,
a: Option<Fp>,
) -> Result<(), Error> {
layouter.assign_region(
|| "load private inputs",
|mut region| {
let row_offset = 0;
self.config.s_range.enable(&mut region, row_offset)?;
region.assign_advice(
|| "private input 'a'",
self.config.a_col,
row_offset,
|| a.ok_or(Error::SynthesisError),
)?;
Ok(())
},
)
}
}
#[derive(Clone)]
struct CoolCircuit {
// Private input.
a: Option<Fp>,
}
impl Circuit<Fp> for CoolCircuit {
type Config = CoolConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self { a: None }
}
fn configure(cs: &mut ConstraintSystem<Fp>) -> Self::Config {
CoolChip::configure(cs)
}
fn synthesize(&self, config: Self::Config, mut layouter: impl Layouter<Fp>) -> Result<(), Error> {
let chip = CoolChip::construct(config);
chip.alloc_and_check(&mut layouter, self.a)
}
}
fn main() {
let start = Instant::now();
let params: Params<EqAffine> = Params::new(4);
let empty_circuit = CoolCircuit { a: None };
let vk = keygen_vk(&params, &empty_circuit).expect("keygen_vk should not fail");
let pk = keygen_pk(&params, vk, &empty_circuit).expect("keygen_pk should not fail");
println!("Setup: [{:?}]", start.elapsed());
let start = Instant::now();
let circuit = CoolCircuit {
a: Some(Fp::from(2)),
};
// Create a proof
let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
create_proof(&params, &pk, &[circuit], &[&[]], &mut transcript)
.expect("proof generation should not fail");
let proof = transcript.finalize();
println!("Prove: [{:?}]", start.elapsed());
let start = Instant::now();
let msm = params.empty_msm();
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
let verification = verify_proof(&params, pk.get_vk(), msm, &[&[]], &mut transcript);
if let Err(err) = verification {
panic!("error {:?}", err);
}
let guard = verification.unwrap();
let msm = guard.clone().use_challenges();
assert!(msm.eval());
println!("Verify: [{:?}]", start.elapsed());
}

View File

@@ -1,267 +0,0 @@
use halo2::{
circuit::{SimpleFloorPlanner, Cell, Chip, Layouter},
pasta::{EqAffine, Fp},
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 group::Curve;
use std::time::Instant;
#[derive(Clone, Debug)]
struct CoolConfig {
a_col: Column<Advice>,
b_col: Column<Advice>,
permute: Permutation,
s_range: Selector,
s_mul: Selector,
s_pub: Selector,
}
struct CoolChip {
config: CoolConfig
}
impl Chip<Fp> for CoolChip {
type Config = CoolConfig;
type Loaded = ();
fn config(&self) -> &Self::Config {
&self.config
}
fn loaded(&self) -> &Self::Loaded {
&()
}
}
#[derive(Clone, Debug)]
struct Number {
cell: Cell,
value: Option<Fp>,
}
impl CoolChip {
fn construct(config: CoolConfig) -> Self {
Self { config }
}
fn configure(cs: &mut ConstraintSystem<Fp>) -> CoolConfig {
let a_col = cs.advice_column();
let b_col = cs.advice_column();
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_range = cs.selector();
let s_mul = cs.selector();
let s_pub = cs.selector();
cs.create_gate("check", |cs| {
let a = cs.query_advice(a_col, Rotation::cur());
let s_range = cs.query_selector(s_range);
vec![s_range * (a - Expression::Constant(Fp::from(2)))]
});
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 * -Fp::one())]
});
cs.create_gate("public input", |cs| {
let a = cs.query_advice(b_col, Rotation::cur());
let p = cs.query_instance(instance, Rotation::cur());
let s = cs.query_selector(s_pub);
vec![s * (p + a * -Fp::one())]
});
CoolConfig { a_col, b_col, permute, s_range, s_mul, s_pub }
}
fn alloc_left(
&self,
layouter: &mut impl Layouter<Fp>,
value: Option<Fp>
) -> Result<Number, Error> {
layouter.assign_region(
|| "load left private input",
|mut region| {
let cell = region.assign_advice(
|| "private input 'a'",
self.config.a_col,
0,
|| value.ok_or(Error::SynthesisError),
)?;
Ok(Number { cell, value })
}
)
}
fn check(
&self,
layouter: &mut impl Layouter<Fp>,
number: Number
) -> Result<(), Error> {
layouter.assign_region(
|| "load private inputs",
|mut region| {
self.config.s_range.enable(&mut region, 0)?;
let a = region.assign_advice(
|| "lhs",
self.config.a_col,
0,
|| number.value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(&self.config.permute, number.cell, a)?;
Ok(())
},
)
}
fn mul(
&self,
layouter: &mut impl Layouter<Fp>,
a: Number,
b: Number
) -> Result<Number, 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(&self.config.permute, a.cell, lhs)?;
region.constrain_equal(&self.config.permute, 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(Number { 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(&self.config.permute, num.cell, out)?;
Ok(())
},
)
}
}
#[derive(Clone)]
struct CoolCircuit {
// Private input.
a: Option<Fp>,
}
impl Circuit<Fp> for CoolCircuit {
type Config = CoolConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self { a: None }
}
fn configure(cs: &mut ConstraintSystem<Fp>) -> Self::Config {
CoolChip::configure(cs)
}
fn synthesize(&self, config: Self::Config, mut layouter: impl Layouter<Fp>) -> Result<(), Error> {
let chip = CoolChip::construct(config);
let a = chip.alloc_left(&mut layouter, self.a)?;
chip.check(&mut layouter, a.clone())?;
let a2 = chip.mul(&mut layouter, a.clone(), a)?;
chip.expose_public(&mut layouter, a2)?;
Ok(())
}
}
fn main() {
let k = 6;
let start = Instant::now();
let params: Params<EqAffine> = Params::new(k);
let empty_circuit = CoolCircuit { a: None };
let vk = keygen_vk(&params, &empty_circuit).expect("keygen_vk should not fail");
let pk = keygen_pk(&params, vk, &empty_circuit).expect("keygen_pk should not fail");
println!("Setup: [{:?}]", start.elapsed());
let start = Instant::now();
let circuit = CoolCircuit {
a: Some(Fp::from(2)),
};
let mut public_inputs = pk.get_vk().get_domain().empty_lagrange();
public_inputs[4] = Fp::from(4);
// Create a proof
let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
create_proof(&params, &pk, &[circuit], &[&[public_inputs.clone()]], &mut transcript)
.expect("proof generation should not fail");
let proof = transcript.finalize();
println!("Prove: [{:?}]", start.elapsed());
let pubinput = params
.commit_lagrange(&public_inputs, Blind::default())
.to_affine();
let pubinput_slice = &[pubinput];
let start = Instant::now();
let msm = params.empty_msm();
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
let verification = verify_proof(&params, pk.get_vk(), msm, &[pubinput_slice], &mut transcript);
if let Err(err) = verification {
panic!("error {:?}", err);
}
let guard = verification.unwrap();
let msm = guard.clone().use_challenges();
assert!(msm.eval());
println!("Verify: [{:?}]", start.elapsed());
}

View File

@@ -1,16 +0,0 @@
use halo2::{
pasta::pallas,
plonk::{Advice, Column, Instance as InstanceColumn, Selector},
};
use halo2_ecc::chip::EccConfig;
use halo2_poseidon::pow5t3::Pow5T3Config as PoseidonConfig;
#[derive(Clone, Debug)]
pub struct Config {
pub primary: Column<InstanceColumn>,
pub q_add: Selector,
pub advices: [Column<Advice>; 10],
pub ecc_config: EccConfig,
pub poseidon_config: PoseidonConfig<pallas::Base>,
}

137
example/halo2/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/halo2/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())
}
}

View File

@@ -1,19 +1,7 @@
pub mod circuit;
use halo2::{
arithmetic::{CurveExt, FieldExt},
pasta::{Ep, Fq},
};
use orchard::constants::fixed_bases::{
VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
};
#[allow(non_snake_case)]
pub fn pedersen_commitment(value: u64, blind: Fq) -> Ep {
let hasher = Ep::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let V = hasher(&VALUE_COMMITMENT_V_BYTES);
let R = hasher(&VALUE_COMMITMENT_R_BYTES);
let value = Fq::from_u64(value);
V * value + R * blind
}
pub mod constants;
pub mod crypto;
pub mod endian;
pub mod error;
pub mod proof;
pub mod serial;
pub mod spec;

870
example/halo2/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"))
);
}
}

View File

@@ -1,14 +1,26 @@
[package]
name = "drk_halo2"
name = "halo2_examples"
version = "0.1.0"
authors = ["Ivan Jelincic <parazyd@dyne.org>"]
authors = ["narodnik <x@x.org>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.4"
ff = "0.11.0"
ff = "0.10"
group = "0.10"
pasta_curves = "0.2.1"
rand = "0.8.4"
hex = "0.4.3"
# Can we delete these?
bitvec = "0.22"
arrayvec = "0.7.0"
lazy_static = "1"
bigint = "4"
subtle = "2.3"
[dependencies.halo2]
version = "=0.1.0-beta.1"
features = ["dev-graph", "gadget-traces", "sanity-checks"]
@@ -17,3 +29,4 @@ features = ["dev-graph", "gadget-traces", "sanity-checks"]
git = "https://github.com/parazyd/halo2_gadgets.git"
rev = "8238cb3471b798c76dd53b278524fc80685c7d4f"
features = ["dev-graph", "test-dependencies"]

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;

1531
examples/halo2/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,455 +0,0 @@
use std::time::Instant;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn, Selector,
},
poly::Rotation,
};
use halo2_gadgets::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
utilities::{
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{Curve, Group},
pallas,
};
use rand::rngs::OsRng;
use drk_halo2::{
constants::OrchardFixedBases,
crypto::pedersen_commitment,
proof::{Proof, ProvingKey, VerifyingKey},
};
#[derive(Clone, Debug)]
struct MintConfig {
primary: Column<InstanceColumn>,
q_add: Selector,
advices: [Column<Advice>; 10],
ecc_config: EccConfig,
poseidon_config: PoseidonConfig<pallas::Base>,
}
impl MintConfig {
fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
EccChip::construct(self.ecc_config.clone())
}
fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}
// The public input array offsets
const MINT_COIN_OFFSET: usize = 0;
const MINT_VALCOMX_OFFSET: usize = 1;
const MINT_VALCOMY_OFFSET: usize = 2;
const MINT_ASSCOMX_OFFSET: usize = 3;
const MINT_ASSCOMY_OFFSET: usize = 4;
#[derive(Default, Debug)]
struct MintCircuit {
pub_x: Option<pallas::Base>, // x coordinate for pubkey
pub_y: Option<pallas::Base>, // y coordinate for pubkey
value: Option<pallas::Base>, // The value of this coin
asset: Option<pallas::Base>, // The asset ID
serial: Option<pallas::Base>, // Unique serial number corresponding to this coin
coin_blind: Option<pallas::Base>, // Random blinding factor for coin
value_blind: Option<pallas::Scalar>, // Random blinding factor for value commitment
asset_blind: Option<pallas::Scalar>, // Random blinding factor for the asset ID
}
impl UtilitiesInstructions<pallas::Base> for MintCircuit {
type Var = CellValue<pallas::Base>;
}
impl Circuit<pallas::Base> for MintCircuit {
type Config = MintConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
// Advice columns used in the circuit
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(),
];
// Addition of two field elements
/*
let q_add = meta.selector();
meta.create_gate("poseidon_hash(a, b) + c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[6], Rotation::cur());
let hash = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (hash + c - sum)]
});
*/
let q_add = meta.selector();
meta.create_gate("a+b+c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[5], Rotation::cur());
let a = meta.query_advice(advices[6], Rotation::cur());
let b = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (a + b + c - sum)]
});
// Fixed columns for the Sinsemilla generator lookup table
let table_idx = meta.lookup_table_column();
let _lookup = (
table_idx,
meta.lookup_table_column(),
meta.lookup_table_column(),
);
// Instance column used for public inputs
let primary = meta.instance_column();
meta.enable_equality(primary.into());
// Permutation over all advice columns
for advice in advices.iter() {
meta.enable_equality((*advice).into());
}
// Poseidon requires four advice columns, while ECC incomplete addition
// requires six. We can reduce the proof size by sharing fixed columns
// between the ECC and Poseidon chips.
// TODO: For multiple invocations they could/should be configured in
// parallel rather than sharing perhaps?
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();
// Also use the first Lagrange coefficient column for loading global constants.
meta.enable_constant(lagrange_coeffs[0]);
// Use one of the right-most advice columns for all of our range checks.
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
// Configuration for curve point operations.
// This uses 10 advice columns and spans the whole circuit.
let ecc_config =
EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check);
// Configuration for the Poseidon hash
let poseidon_config = PoseidonChip::configure(
meta,
P128Pow5T3,
advices[6..9].try_into().unwrap(),
advices[5],
rc_a,
rc_b,
);
MintConfig {
primary,
q_add,
advices,
ecc_config,
poseidon_config,
}
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
let ecc_chip = config.ecc_chip();
let pub_x = self.load_private(
layouter.namespace(|| "load pubkey x"),
config.advices[0],
self.pub_x,
)?;
let pub_y = self.load_private(
layouter.namespace(|| "load pubkey y"),
config.advices[0],
self.pub_y,
)?;
let value = self.load_private(
layouter.namespace(|| "load value"),
config.advices[0],
self.value,
)?;
let asset = self.load_private(
layouter.namespace(|| "load asset"),
config.advices[0],
self.asset,
)?;
let serial = self.load_private(
layouter.namespace(|| "load serial"),
config.advices[0],
self.serial,
)?;
let coin_blind = self.load_private(
layouter.namespace(|| "load coin_blind"),
config.advices[0],
self.coin_blind,
)?;
// =========
// Coin hash
// =========
let messages = [[pub_x, pub_y], [value, asset], [serial, coin_blind]];
let mut hashes = vec![];
for message in messages.iter() {
let hash = {
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, P128Pow5T3, 3, 2>::from_inner(StateWord::new(
var, value,
)))
};
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 (a, b)"),
poseidon_message,
)?;
let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
poseidon_output
};
hashes.push(hash);
}
let coin = layouter.assign_region(
|| " `coin` = hash(a,b) + hash(c, d) + hash(e, f)",
|mut region| {
config.q_add.enable(&mut region, 0)?;
copy(&mut region, || "copy ab", config.advices[6], 0, &hashes[0])?;
copy(&mut region, || "copy cd", config.advices[7], 0, &hashes[1])?;
copy(&mut region, || "copy ef", config.advices[8], 0, &hashes[2])?;
let scalar_val = hashes[0]
.value()
.zip(hashes[1].value())
.zip(hashes[2].value())
.map(|(abcd, ef)| abcd.0 + abcd.1 + ef);
let cell = region.assign_advice(
|| "hash(a,b)+hash(c,d)+hash(e,f)",
config.advices[5],
0,
|| scalar_val.ok_or(Error::SynthesisError),
)?;
Ok(CellValue::new(cell, scalar_val))
},
)?;
// Constrain the coin C
layouter.constrain_instance(coin.cell(), config.primary, MINT_COIN_OFFSET)?;
// ================
// Value commitment
// ================
// This constant one is used for short multiplication
let one = self.load_private(
layouter.namespace(|| "load constant one"),
config.advices[0],
Some(pallas::Base::one()),
)?;
// v * G_1
let (commitment, _) = {
let value_commit_v = OrchardFixedBases::ValueCommitV;
let value_commit_v = FixedPoint::from_inner(ecc_chip.clone(), value_commit_v);
value_commit_v.mul_short(layouter.namespace(|| "[value] ValueCommitV"), (value, one))?
};
// r_V * G_2
let (blind, _rcv) = {
let rcv = self.value_blind;
let value_commit_r = OrchardFixedBases::ValueCommitR;
let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
};
// Constrain the value commitment coordinates
let value_commit = commitment.add(layouter.namespace(|| "valuecommit"), &blind)?;
layouter.constrain_instance(
value_commit.inner().x().cell(),
config.primary,
MINT_VALCOMX_OFFSET,
)?;
layouter.constrain_instance(
value_commit.inner().y().cell(),
config.primary,
MINT_VALCOMY_OFFSET,
)?;
// ================
// Asset commitment
// ================
// a * G_1
let (commitment, _) = {
let asset_commit_v = OrchardFixedBases::ValueCommitV;
let asset_commit_v = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_v);
asset_commit_v.mul_short(layouter.namespace(|| "[asset] ValueCommitV"), (asset, one))?
};
// r_A * G_2
let (blind, _rca) = {
let rca = self.asset_blind;
let asset_commit_r = OrchardFixedBases::ValueCommitR;
let asset_commit_r = FixedPoint::from_inner(ecc_chip, asset_commit_r);
asset_commit_r.mul(layouter.namespace(|| "[asset_blind] ValueCommitR"), rca)?
};
// Constrain the asset commitment coordinates
let asset_commit = commitment.add(layouter.namespace(|| "assetcommit"), &blind)?;
layouter.constrain_instance(
asset_commit.inner().x().cell(),
config.primary,
MINT_ASSCOMX_OFFSET,
)?;
layouter.constrain_instance(
asset_commit.inner().y().cell(),
config.primary,
MINT_ASSCOMY_OFFSET,
)?;
// At this point we've enforced all of our public inputs.
Ok(())
}
}
fn main() {
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 9;
let pubkey = pallas::Point::random(&mut OsRng);
let coords = pubkey.to_affine().coordinates().unwrap();
let value = 42;
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);
// poseidon_hash(x, y) + poseidon_hash(value, asset) + poseidon_hash(serial, coin_blind)
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() {
let hash = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
coin += hash;
}
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 public_inputs = vec![
coin,
*value_coords.x(),
*value_coords.y(),
*asset_coords.x(),
*asset_coords.y(),
];
let circuit = MintCircuit {
pub_x: Some(*coords.x()),
pub_y: Some(*coords.y()),
value: Some(pallas::Base::from(value)),
asset: Some(pallas::Base::from(asset)),
serial: Some(serial),
coin_blind: Some(coin_blind),
value_blind: Some(value_blind),
asset_blind: Some(asset_blind),
};
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(k, MintCircuit::default());
let pk = ProvingKey::build(k, MintCircuit::default());
println!("Setup: [{:?}]", 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());
}

View File

@@ -1,4 +0,0 @@
pub mod constants;
pub mod crypto;
pub mod proof;
pub mod spec;

56
proof/burn.zk Normal file
View File

@@ -0,0 +1,56 @@
# :set syntax=zk
# :source scripts/zk.vim
constant EcFixedPoint VALUE_COMMIT_VALUE
constant EcFixedPoint VALUE_COMMIT_RANDOM
constant EcFixedPoint SPEND_AUTH_G
contract Burn {
Base secret
Base serial
MerklePath path
Base leaf
Base value
Base asset
Scalar value_blind
Scalar asset_blind
Scalar sig_secret
}
circuit Burn {
# nullifier = Hash(secret, serial)
poseidon_hash nullifier secret serial
constrain_instance nullifier
# root = calculate_root(path, leaf)
calculate_merkle_root root path leaf
constrain_instance root
# value_commit = PedersenCommit(value, value_blind);
ec_mul_short vcv value VALUE_COMMIT_VALUE
ec_mul vcr value_blind VALUE_COMMIT_RANDOM
ec_add value_commit vcv vcr
ec_get_x x value_commit
ec_get_y y value_commit
constrain_instance x
constrain_instance y
# asset_commit = PedersenCommit(asset, asset_blind);
ec_mul_short acv asset VALUE_COMMIT_VALUE
ec_mul acr asset_blind VALUE_COMMIT_RANDOM
ec_add asset_commit acv acr
ec_get_x x asset_commit
ec_get_y y asset_commit
constrain_instance x
constrain_instance y
# spend_auth_public = sig_secret * SPEND_AUTH_G
ec_mul spend_auth_public sig_secret SPEND_AUTH_G
ec_get_x sx spend_auth_public
ec_get_y sy spend_auth_public
constrain_instance sx
constrain_instance sy
# return (nullifier, root, value_commit, value_blind);
}

46
proof/mint.zk Normal file
View File

@@ -0,0 +1,46 @@
# :set syntax=zk
# :source scripts/zk.vim
constant EcFixedPoint VALUE_COMMIT_VALUE
constant EcFixedPoint VALUE_COMMIT_RANDOM
contract Mint {
Base pub_x
Base pub_y
Base value
Base asset
Base serial
Base coin_blind
Scalar value_blind
Scalar asset_blind
}
circuit Mint {
# coin = Hash(pub_x, pub_y, value, asset, serial, coin_blind);
poseidon_hash C1 pub_x pub_y
poseidon_hash C2 value asset
poseidon_hash C3 serial coin_blind
add C12 C1 C2
add C C12 C3
constrain_instance C
# value_commit = PedersenCommit(value, value_blind);
ec_mul_short vcv value VALUE_COMMIT_VALUE
ec_mul vcr value_blind VALUE_COMMIT_RANDOM
ec_add value_commit vcv vcr
ec_get_x x value_commit
ec_get_y y value_commit
constrain_instance x
constrain_instance y
# asset_commit = PedersenCommit(asset, asset_blind);
ec_mul_short acv asset VALUE_COMMIT_VALUE
ec_mul acr asset_blind VALUE_COMMIT_RANDOM
ec_add asset_commit acv acr
ec_get_x x asset_commit
ec_get_y y asset_commit
constrain_instance x
constrain_instance y
# return (coin, value_commit, asset_commit);
}

7
run_burn_zk.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash -x
python script/zkas.py proof/burn.zk --bincode
du -sh proof/burn.zk.bin
python script/zkas.py proof/burn.zk
#python script/zkas.py proof/mint.zk
cargo run --release --bin vm2_burn

7
run_mint_zk.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash -x
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
cargo run --release --bin vm2

View File

@@ -0,0 +1,187 @@
# We'll use y^2 = x^3 + 3 for our curve, over F_101
p = 101
F = FiniteField(p)
R.<x_f> = F[]
E = EllipticCurve(F, [0, 3])
print(E)
e_points = E.points()
# Our generator G_1 = E(1, 2)
G_1 = e_points[1]
# Let's find a subgroup with this generator.
print(f"Finding a subgroup with generator {G_1} ...")
r = 1
elem = G_1
while True:
elem += G_1
r += 1
if elem == e_points[0]:
break
print(f"Found subgroup of order r={r} using generator {G_1}")
# Now let's find the embedding degree.
# The embedding degree is the smallest k such that r|p^k - 1
# In other words: p^k == 1 mod r
k = 1
print(f"Finding embedding degree for {p}^k mod {r} ...")
while True:
if p ^ k % r == 1:
break
k += 1
print(f"Found embedding degree: k={k}")
# Our extension field. The polynomial x^2+2 is irreducible in F_101.
F2.<u> = F.extension(x_f^2+2, 'u')
assert u^2 == -2
print(F2)
E2 = EllipticCurve(F2, [0, 3])
print(E2)
# One of the generators for this curve we can use is (36, 31u)
G_2 = E2(36, 31*u)
# Now we build the trusted setup. The SRS is a list of EC points
# parameterized by a randomly generated secret number s.
# According to the PLONK protocol paper, a circuit with n gates requires
# an SRS with at least n+5 elements.
# We choose 2 as our random number for demo purposes.
s = 2
# Our circuit will have 4 gates.
n_gates = 4
SRS = []
for i in range(0, n_gates+3):
SRS.append(s^i * G_1)
for i in range(0, 2):
SRS.append(s^i * G_2)
# Composing our circuit. We'll test a^2 + b^2 = c^2:
# x_1 * x_1 = x_2
# x_3 * x_3 = x_4
# x_5 * x_5 = x_6
# x_2 + x_4 = x_6
#
# In order to satisfy these constraints, we need to supply six numbers
# as wire values that make all of the equations correct.
# e.g. x=(3, 9, 4, 16, 5, 25) would work.
# A full PLONK gate looks like this:
# (q_L)*a + (q_R)*b + (q_O)*c + (q_M)*a*b + q_C = 0
#
# Where a, b, c are the left, right, output wires of the gate.
#
# a + b = c ---> q_L=1 , q_R=1, q_O=-1, q_M=0, q_C = 0
# a * b = c ---> q_O=-1, q_M=1, and the rest = 0
#
# To bind a variable to a public value:
# q_R = q_O = q_M = 0
# q_L = 1
# q_C = public_value
#
# Considering all inputs as private, we get these four PLONK gates
# representing our circuit:
# 0*a_1 + 0*b_1 + (-1)*c_1 + 1*a_1*b_1 + 0 = 0 (a_1 * b_1 = c_1)
# 0*a_2 + 0*b_2 + (-1)*c_2 + 1*a_2*b_2 + 0 = 0 (a_2 * b_2 = c_2)
# 0*a_3 + 0*b_3 + (-1)*c_3 + 1*a_3*b_3 + 0 = 0 (a_3 * b_3 = c_3)
# 1*a_4 + 1*b_4 + (-1)*c_4 + 0*a_4*b_4 + 0 = 0 (a_4 + b_4 = c_4)
#
# So let's test with (3, 4, 5)
# a_i (left) values will be (3, 4, 5, 9)
# b_i (right) values will be (3, 4, 5, 16)
# c_i (output) values will be (9, 16, 25, 25)
# Selectors
q_L = vector([0, 0, 0, 1])
q_R = vector([0, 0, 0, 1])
q_O = vector([-1, -1, -1, -1])
q_M = vector([1, 1, 1, 0])
q_C = vector([0, 0, 0, 0])
# Assignments
a = vector([3, 4, 5, 9])
b = vector([3, 4, 5, 16])
c = vector([9, 16, 25, 25])
# Roots of Unity.
# The vectors for our circuit and assignment are all length 4, so the domain
# for our polynomial interpolation must have at least four elements.
roots_of_unity = []
F_r = FiniteField(r)
for i in F_r:
if i^4 == 1:
roots_of_unity.append(i)
omega_0 = roots_of_unity[0]
omega_1 = roots_of_unity[1]
omega_2 = roots_of_unity[3]
omega_3 = roots_of_unity[2]
# Cosets
# k_1 not in H, k_2 not in H nor k_1H
k_1 = 2
k_2 = 3
H = [omega_0, omega_1, omega_2, omega_3]
k1H = [H[0]*k_1, H[1]*k_1, H[2]*k_1, H[3]*k_1]
k2H = [H[0]*k_2, H[1]*k_2, H[2]*k_2, H[3]*k_2]
print("Polynomial interpolation using roots of unity")
print(f"H: {H}")
print(f"k1H: {k1H}")
print(f"k2H: {k2H}")
# Interpolating using the Roots of Unity
# The interpolated polynomial will be degree-3 and have the form:
# f_a(x) = d + c*x + b*x^2 + a*x^3
# f_a(1) = 3, f_a(4) = 4, f_a(16) = 5, f_a(13) = 9
# Note that the above x is H (the omegas)
#
# This gives a system of equations:
# f(1) = d + c*1 + b*1^2 + a*1^3 = 3
# f(4) = d + c*4 + b*4^2 + a*4^3 = 4
# f(16) = d + c*16 + b*16^2 + a*16^3 = 5
# f(13) = d + c*13 + b*13^2 + a*13^3 = 9
# We can rewrite it as a matrix equation and solve by computing
# an inverse matrix.
def inverse_matrix(c):
return Matrix([
[c[0]^0, c[0]^1, c[0]^2, c[0]^3],
[c[1]^0, c[1]^1, c[1]^2, c[1]^3],
[c[2]^0, c[2]^1, c[2]^2, c[2]^3],
[c[3]^0, c[3]^1, c[3]^2, c[3]^3],
])^-1
# Now we can find polynomial f_a by multiplying the vector a=(3,4,5,9) by
# the interpolation matrix.
f_a_coeffs = inverse_matrix(H) * a
f_b_coeffs = inverse_matrix(H) * b
f_c_coeffs = inverse_matrix(H) * c
q_L_coeffs = inverse_matrix(H) * q_L
q_R_coeffs = inverse_matrix(H) * q_R
q_O_coeffs = inverse_matrix(H) * q_O
q_M_coeffs = inverse_matrix(H) * q_M
q_C_coeffs = inverse_matrix(H) * q_C
# The copy constraints involving left, right, output values are encoded as
# polynomials S_sigma_1, S_sigma_2, S_sigma_3 using the cosets we found
# earlier. The roots of unity H are used to label entries in vector a,
# the elements of k1H are used to label entries in vector b, and vector c is
# labeled by the elements of k2H.
print("Copy constraints:")
print(f"a: {a}")
print(f"b: {b}")
print(f"c: {c}")
# a1 = b1, a2 = b2, a3 = b3, a4 = c1
sigma_1 = vector([k1H[0], k1H[1], k1H[2], k2H[0]])
print(f"sigma_1: {sigma_1}")
# b1 = a1, b2 = a2, b3 = a3, b4 = c2
sigma_2 = vector([H[0], H[1], H[2], k2H[1]])
print(f"sigma_2: {sigma_2}")
# c1 = a4, c2 = b4, c3 = c4, c4 = c3
sigma_3 = vector([H[3], k1H[3], k2H[3], k2H[2]])
print(f"sigma_3: {sigma_3}")
S_sigma_1_coeffs = inverse_matrix(H) * sigma_1
S_sigma_2_coeffs = inverse_matrix(H) * sigma_2
S_sigma_3_coeffs = inverse_matrix(H) * sigma_3

View File

@@ -0,0 +1,33 @@
p = 2^31 - 1
q = 2^61 - 1
assert is_prime(p)
assert is_prime(q)
n = p * q
# Order of the multiplicative group for n
# phi = (p - 1) * (q - 1)
K = IntegerModRing(n)
A_0 = K(5)
c_0 = random_prime(2^12)
A_1 = A_0^c_0
c_1 = random_prime(2^12)
A_2 = A_1^c_1
c_2 = random_prime(2^12)
W_3 = A_2
A_3 = A_2^c_2
c_3 = random_prime(2^12)
W_4 = W_3^c_3
A_4 = A_3^c_3
c_4 = random_prime(2^12)
W_5 = W_4^c_4
A_5 = A_4^c_4
assert W_5^c_2 == A_5
assert A_5 == A_0^(c_0 * c_1 * c_2 * c_3 * c_4)
assert W_5 == A_0^(c_0 * c_1 * c_3 * c_4)

5
script/to_html.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
#nvim -c ":TOhtml" $1
sed -i "s/PreProc { color: #5fd7ff; }/PreProc { color: #8f2722; }/" $1
sed -i "s/Comment { color: #00ffff; }/Comment { color: #0055ff; }/" $1

41
script/zk.vim Normal file
View File

@@ -0,0 +1,41 @@
"For autoload, add this to your VIM config:
" VIM: .vimrc
" NeoVIM: .config/nvim/init.vim
"
"autocmd BufRead *.pism call SetPismOptions()
"function SetPismOptions()
" set syntax=pism
" source /home/narodnik/src/drk/scripts/pism.vim
"endfunction
if exists('b:current_syntax')
finish
endif
syn keyword drkKeyword constant contract circuit
"syn keyword drkAttr
syn keyword drkType EcFixedPoint Base Scalar
"syn keyword drkFunctionKeyword enforce lc0_add_one lc1_add_one lc2_add_one lc_coeff_reset lc_coeff_double lc0_sub_one lc1_sub_one lc2_sub_one dump_alloc dump_local
syn match drkFunction "^[ ]*[a-z_0-9]* "
syn match drkComment "#.*$"
syn match drkNumber ' \zs\d\+\ze'
syn match drkHexNumber ' \zs0x[a-z0-9]\+\ze'
syn match drkConst '[A-Z_]\{2,}[A-Z0-9_]*'
syn keyword drkBoolVal true false
syn match drkPreproc "{%.*%}"
syn match drkPreproc2 "{{.*}}"
hi def link drkKeyword Statement
"hi def link drkAttr StorageClass
hi def link drkPreproc PreProc
hi def link drkPreproc2 PreProc
hi def link drkType Type
hi def link drkFunction Function
hi def link drkFunctionKeyword Function
hi def link drkComment Comment
hi def link drkNumber Constant
hi def link drkHexNumber Constant
hi def link drkConst Constant
hi def link drkBoolVal Constant
let b:current_syntax = "pism"

402
script/zkas.py Normal file
View File

@@ -0,0 +1,402 @@
import argparse
import sys
from zkas.types import *
class CompileException(Exception):
def __init__(self, error_message, line):
super().__init__(error_message)
self.error_message = error_message
self.line = line
class Constants:
def __init__(self):
self.table = []
self.map = {}
def add(self, variable, type_id):
idx = len(self.table)
self.table.append(type_id)
self.map[variable] = idx
def lookup(self, variable):
idx = self.map[variable]
return self.table[idx]
def variables(self):
return self.map.keys()
class SyntaxStruct:
def __init__(self):
self.contracts = {}
self.circuits = {}
self.constants = Constants()
def parse_contract(self, line, it):
assert line.tokens[0] == "contract"
if len(line.tokens) != 3 or line.tokens[2] != "{":
raise CompileException("malformed contract opening", line)
name = line.tokens[1]
if name in self.contracts:
raise CompileException(f"duplicate contract {name}", line)
lines = []
while True:
try:
line = next(it)
except StopIteration:
raise CompileException(
f"premature end of file while parsing {name} contract", line)
assert len(line.tokens) > 0
if line.tokens[0] == "}":
break
lines.append(line)
self.contracts[name] = lines
def parse_circuit(self, line, it):
assert line.tokens[0] == "circuit"
if len(line.tokens) != 3 or line.tokens[2] != "{":
raise CompileException("malformed circuit opening", line)
name = line.tokens[1]
if name in self.circuits:
raise CompileException(f"duplicate contract {name}", line)
lines = []
while True:
try:
line = next(it)
except StopIteration:
raise CompileException(
f"premature end of file while parsing {name} circuit", line)
assert len(line.tokens) > 0
if line.tokens[0] == "}":
break
lines.append(line)
self.circuits[name] = lines
def parse_constant(self, line):
assert line.tokens[0] == "constant"
if len(line.tokens) != 3:
raise CompileException("malformed constant line", line)
_, type_name, variable = line.tokens
if type_name not in allowed_types:
raise CompileException("unknown type '{type}'", line)
type_id = allowed_types[type_name]
self.constants.add(variable, type_id)
def verify(self):
self.static_checks()
schema = self.format_data()
self.trace_circuits(schema)
return schema
def static_checks(self):
for name, lines in self.contracts.items():
for line in lines:
if len(line.tokens) != 2:
raise CompileException("incorrect number of tokens", line)
type, variable = line.tokens
if type not in allowed_types:
raise CompileException(
f"unknown type specifier for variable {variable}", line)
for name, lines in self.circuits.items():
for line in lines:
assert len(line.tokens) > 0
func_name, args = line.tokens[0], line.tokens[1:]
if func_name not in function_formats:
raise CompileException(f"unknown function call {func_name}",
line)
func_format = function_formats[func_name]
if len(args) != func_format.total_arguments():
raise CompileException(
f"incorrect number of arguments for function call {func_name}", line)
# Finally check there are matching circuits and contracts
all_names = set(self.circuits.keys()) | set(self.contracts.keys())
for name in all_names:
if name not in self.contracts:
raise CompileException(f"missing contract for {name}", None)
if name not in self.circuits:
raise CompileException(f"missing circuit for {name}", None)
def format_data(self):
schema = []
for name, circuit in self.circuits.items():
assert name in self.contracts
contract = self.contracts[name]
witness = []
for line in contract:
assert len(line.tokens) == 2
type_name, variable = line.tokens
assert type_name in allowed_types
type_id = allowed_types[type_name]
witness.append((type_id, variable, line))
code = []
for line in circuit:
assert len(line.tokens) > 0
func_name, args = line.tokens[0], line.tokens[1:]
assert func_name in function_formats
func_format = function_formats[func_name]
assert len(args) == func_format.total_arguments()
return_values = []
if func_format.return_type_ids:
rv_len = len(func_format.return_type_ids)
return_values, args = args[:rv_len], args[rv_len:]
func_id = func_format.func_id
code.append((func_format, return_values, args, line))
schema.append((name, witness, code))
return schema
def trace_circuits(self, schema):
for name, witness, code in schema:
tracer = DynamicTracer(name, witness, code, self.constants)
tracer.execute()
class DynamicTracer:
def __init__(self, name, contract_witness, circuit_code, constants):
self.name = name
self.witness = contract_witness
self.code = circuit_code
self.constants = constants
def execute(self):
stack = {}
# Load constants
for variable in self.constants.variables():
stack[variable] = self.constants.lookup(variable)
# Preload stack with our witness values
for type_id, variable, line in self.witness:
stack[variable] = type_id
for i, (func_format, return_values, args, code_line) \
in enumerate(self.code):
assert len(args) == len(func_format.param_types)
for variable, type_id in zip(args, func_format.param_types):
if variable not in stack:
raise CompileException(
f"variable '{variable}' is not defined", code_line)
stack_type_id = stack[variable]
if stack_type_id != type_id:
type_name = type_id_to_name[type_id]
stack_type_name = type_id_to_name[stack_type_id]
raise CompileException(
f"variable '{variable}' has incorrect type. "
f"Found {type_name} but expected variable of "
f"type {stack_type_name}", code_line)
assert len(return_values) == len(func_format.return_type_ids)
for return_variable, return_type_id \
in zip(return_values, func_format.return_type_ids):
# Note that later variables shadow earlier ones.
# We accept this.
stack[return_variable] = return_type_id
class CodeLine:
def __init__(self, func_format, return_values, args, arg_idxs, code_line):
self.func_format = func_format
self.return_values = return_values
self.args = args
self.arg_idxs = arg_idxs
self.code_line = code_line
def func_name(self):
return func_id_to_name[self.func_format.func_id]
class CompiledContract:
def __init__(self, name, witness, code):
self.name = name
self.witness = witness
self.code = code
class Compiler:
def __init__(self, witness, uncompiled_code, constants):
self.witness = witness
self.uncompiled_code = uncompiled_code
self.constants = constants
def compile(self):
code = []
# Each unique type_id has its own stack
stacks = [[] for i in range(TYPE_ID_LAST)]
# Map from variable name to stacks above
stack_vars = {}
def alloc(variable, type_id):
assert type_id <= len(stacks)
idx = len(stacks[type_id])
# Add variable to the stack for its type_id
stacks[type_id].append(variable)
# Create mapping from variable name
stack_vars[variable] = (type_id, idx)
# Load constants
for variable in self.constants.variables():
type_id = self.constants.lookup(variable)
alloc(variable, type_id)
# Preload stack with our witness values
for type_id, variable, line in self.witness:
alloc(variable, type_id)
for i, (func_format, return_values, args, code_line) \
in enumerate(self.uncompiled_code):
assert len(args) == len(func_format.param_types)
arg_idxs = []
# Loop through all arguments
for variable, type_id in zip(args, func_format.param_types):
assert type_id <= len(stacks)
assert variable in stack_vars
# Find the index for the M by N matrix of our variable
loc_type_id, loc_idx = stack_vars[variable]
assert type_id == loc_type_id
assert stacks[loc_type_id][loc_idx] == variable
# This is the info to be serialized, not the variable names
arg_idxs.append(loc_idx)
assert len(return_values) == len(func_format.return_type_ids)
for return_variable, return_type_id \
in zip(return_values, func_format.return_type_ids):
# Allocate returned values so they can be used by
# subsequent function calls.
alloc(return_variable, return_type_id)
code.append(CodeLine(func_format, return_values, args,
arg_idxs, code_line))
return code
class Line:
def __init__(self, tokens, original_line, number):
self.tokens = tokens
self.orig = original_line
self.number = number
def __repr__(self):
return f"Line({self.number}: {str(self.tokens)})"
def load(src_file):
source = []
for i, original_line in enumerate(src_file):
# Remove whitespace on both sides
line = original_line.strip()
# Strip out comments
line = line.split("#")[0]
# Split at whitespace
line = line.split()
if not line:
continue
line_number = i + 1
source.append(Line(line, original_line, line_number))
return source
def parse(source):
syntax = SyntaxStruct()
it = iter(source)
while True:
try:
line = next(it)
except StopIteration:
break
assert len(line.tokens) > 0
if line.tokens[0] == "contract":
syntax.parse_contract(line, it)
elif line.tokens[0] == "circuit":
syntax.parse_circuit(line, it)
elif line.tokens[0] == "constant":
syntax.parse_constant(line)
elif line.tokens[0] == "}":
raise CompileException("unmatched delimiter '}'", line)
return syntax
def main():
parser = argparse.ArgumentParser()
parser.add_argument("SOURCE", help="ZK script to compile")
parser.add_argument("--output", default=None, help="output file")
group = parser.add_mutually_exclusive_group()
group.add_argument('--display', action='store_true',
help="show the compiled code in human readable format")
group.add_argument('--bincode', action='store_true',
help="output compiled code to zkvm supervisor")
args = parser.parse_args()
with open(args.SOURCE, "r") as src_file:
source = load(src_file)
try:
syntax = parse(source)
schema = syntax.verify()
contracts = []
for name, witness, uncompiled_code in schema:
compiler = Compiler(witness, uncompiled_code, syntax.constants)
code = compiler.compile()
contracts.append(CompiledContract(name, witness, code))
constants = syntax.constants
if args.display:
from zkas.text_output import output
if args.output is None:
output(sys.stdout, contracts, constants)
else:
with open(outpath, "w") as file:
output(file, contracts, constants)
elif args.bincode:
from zkas.bincode_output import output
outpath = args.output
if args.output is None:
outpath = args.SOURCE + ".bin"
with open(outpath, "wb") as file:
output(file, contracts, constants)
else:
from zkas.text_output import output
if args.output is None:
output(sys.stdout, contracts, constants)
else:
with open(outpath, "w") as file:
output(file, contracts, constants)
except CompileException as ex:
print(f"Error: {ex.error_message}", file=sys.stderr)
if ex.line is not None:
print(f"Line {ex.line.number}: {ex.line.orig}", file=sys.stderr)
#return -1
raise
return 0
if __name__ == "__main__":
sys.exit(main())
# todo: think about extendable payment scheme which
# is like bitcoin soft forks

0
script/zkas/__init__.py Normal file
View File

View File

@@ -0,0 +1,50 @@
import struct
def varuint(value):
if value <= 0xfc:
return struct.pack("<B", value)
elif value <= 0xffff:
return struct.pack("<BH", 0xfd, value)
elif value <= 0xffffffff:
return struct.pack("<BI", 0xfe, value)
else:
return struct.pack("<BQ", 0xff, value)
def write_len(output, objects):
output.write(varuint(len(objects)))
def write_value(fmt, output, value):
value_bytes = struct.pack("<" + fmt, value)
output.write(value_bytes)
def write_u8(output, value):
write_value("B", output, value)
def write_u32(output, value):
write_value("I", output, value)
def output_contract(output, contract):
write_len(output, contract.name)
output.write(contract.name.encode())
write_len(output, contract.witness)
for type_id, variable, _ in contract.witness:
write_len(output, variable)
output.write(variable.encode())
write_u8(output, type_id)
write_len(output, contract.code)
for code in contract.code:
func_id = code.func_format.func_id
write_u8(output, func_id)
for arg_idx in code.arg_idxs:
write_u32(output, arg_idx)
def output(output, contracts, constants):
write_len(output, constants.variables())
for variable in constants.variables():
write_len(output, variable)
output.write(variable.encode())
type_id = constants.lookup(variable)
write_u8(output, type_id)
write_len(output, contracts)
for contract in contracts:
output_contract(output, contract)

View File

@@ -0,0 +1,21 @@
from .types import type_id_to_name, func_id_to_name
def output(output, contracts, constants):
output.write("Constants:\n")
for variable in constants.variables():
type_id = constants.lookup(variable)
output.write(f" {type_id} {variable}\n")
for contract in contracts:
output.write(f"{contract.name}:\n")
output.write(f" Witness:\n")
for type_id, variable, _ in contract.witness:
type_name = type_id_to_name[type_id]
output.write(f" {type_name} {variable}\n")
output.write(f" Code:\n")
for code in contract.code:
output.write(f" # args = {code.args}\n")
output.write(f" {code.func_name()} {code.return_values} "
f"{code.arg_idxs}\n")

77
script/zkas/types.py Normal file
View File

@@ -0,0 +1,77 @@
TYPE_ID_BASE = 0
TYPE_ID_SCALAR = 1
TYPE_ID_EC_POINT = 2
TYPE_ID_EC_FIXED_POINT = 3
TYPE_ID_MERKLE_PATH = 4
# This is so we know the number of TYPE_ID stacks
TYPE_ID_LAST = 5
allowed_types = {
"Base": TYPE_ID_BASE,
"Scalar": TYPE_ID_SCALAR,
"EcFixedPoint": TYPE_ID_EC_FIXED_POINT,
"MerklePath": TYPE_ID_MERKLE_PATH,
}
# Used for debug and error messages
type_id_to_name = dict((value, key) for key, value in allowed_types.items())
FUNC_ID_POSEIDON_HASH = 0
FUNC_ID_ADD = 1
FUNC_ID_CONSTRAIN_INSTANCE = 2
FUNC_ID_EC_MUL_SHORT = 3
FUNC_ID_EC_MUL = 4
FUNC_ID_EC_ADD = 5
FUNC_ID_EC_GET_X = 6
FUNC_ID_EC_GET_Y = 7
FUNC_ID_CALCULATE_MERKLE_ROOT = 8
class FuncFormat:
def __init__(self, func_id, return_type_ids, param_types):
self.func_id = func_id
self.return_type_ids = return_type_ids
self.param_types = param_types
def total_arguments(self):
return len(self.return_type_ids) + len(self.param_types)
function_formats = {
"poseidon_hash": FuncFormat(
# Funcion ID Type ID Parameter types
FUNC_ID_POSEIDON_HASH, [TYPE_ID_BASE], [TYPE_ID_BASE,
TYPE_ID_BASE]
),
"add": FuncFormat(
FUNC_ID_ADD, [TYPE_ID_BASE], [TYPE_ID_BASE,
TYPE_ID_BASE]
),
"constrain_instance": FuncFormat(
FUNC_ID_CONSTRAIN_INSTANCE, [], [TYPE_ID_BASE]
),
"ec_mul_short": FuncFormat(
FUNC_ID_EC_MUL_SHORT, [TYPE_ID_EC_POINT], [TYPE_ID_BASE,
TYPE_ID_EC_FIXED_POINT]
),
"ec_mul": FuncFormat(
FUNC_ID_EC_MUL, [TYPE_ID_EC_POINT], [TYPE_ID_SCALAR,
TYPE_ID_EC_FIXED_POINT]
),
"ec_add": FuncFormat(
FUNC_ID_EC_ADD, [TYPE_ID_EC_POINT], [TYPE_ID_EC_POINT,
TYPE_ID_EC_POINT]
),
"ec_get_x": FuncFormat(
FUNC_ID_EC_GET_X, [TYPE_ID_BASE], [TYPE_ID_EC_POINT]
),
"ec_get_y": FuncFormat(
FUNC_ID_EC_GET_Y, [TYPE_ID_BASE], [TYPE_ID_EC_POINT]
),
"calculate_merkle_root": FuncFormat(
FUNC_ID_CALCULATE_MERKLE_ROOT,
[TYPE_ID_BASE], [TYPE_ID_MERKLE_PATH,
TYPE_ID_BASE]
),
}
func_id_to_name = dict((fmt.func_id, key) for key, fmt
in function_formats.items())

View File

@@ -1,8 +1,8 @@
use futures::prelude::*;
use crate::endian;
use crate::error::{Error, Result};
use crate::serial::VarInt;
use crate::{Error, Result};
impl VarInt {
pub async fn encode_async<W: AsyncWrite + Unpin>(&self, stream: &mut W) -> Result<usize> {

View File

@@ -29,24 +29,33 @@ use halo2_gadgets::{
merkle::MerklePath,
},
utilities::{
lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use incrementalmerkletree::{
bridgetree::{BridgeTree, Frontier as BridgeFrontier},
Altitude, Frontier, Tree,
};
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{ff::PrimeFieldBits, Curve},
group::{
ff::{PrimeField, PrimeFieldBits},
Curve,
},
pallas,
};
use rand::rngs::OsRng;
use drk_halo2::{
use drk::crypto::{
constants::{
sinsemilla::{OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION},
sinsemilla::{
i2lebsp, OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION,
},
OrchardFixedBases,
},
crypto::pedersen_commitment,
merkle_node2::MerkleNode,
proof::{Proof, ProvingKey, VerifyingKey},
spec::i2lebsp,
util::{pedersen_commitment_scalar, pedersen_commitment_u64},
};
#[derive(Clone, Debug)]
@@ -119,7 +128,6 @@ struct BurnCircuit {
coin_blind: Option<pallas::Base>,
value_blind: Option<pallas::Scalar>,
asset_blind: Option<pallas::Scalar>,
leaf: Option<pallas::Base>,
leaf_pos: Option<u32>,
merkle_path: Option<[pallas::Base; 32]>,
sig_secret: Option<pallas::Scalar>,
@@ -290,7 +298,7 @@ impl Circuit<pallas::Base> for BurnCircuit {
// =========
// Nullifier
// =========
let hashed_secret_key = self.load_private(
let secret_key = self.load_private(
layouter.namespace(|| "load sinsemilla(secret key)"),
config.advices[0],
self.secret_key,
@@ -302,7 +310,7 @@ impl Circuit<pallas::Base> for BurnCircuit {
self.serial,
)?;
let message = [hashed_secret_key, serial];
let message = [secret_key, serial];
let hash = {
let poseidon_message = layouter.assign_region(
|| "load message",
@@ -341,14 +349,117 @@ impl Circuit<pallas::Base> for BurnCircuit {
layouter.constrain_instance(hash.cell(), config.primary, BURN_NULLIFIER_OFFSET)?;
// let nullifier_k = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
// nullifier_k.mul(
// layouter.namespace(|| "[poseidon_output + psi_old] NullifierK"),
// scalar,
// )?
let value = self.load_private(
layouter.namespace(|| "load value"),
config.advices[0],
self.value,
)?;
let asset = self.load_private(
layouter.namespace(|| "load asset"),
config.advices[0],
self.asset,
)?;
let coin_blind = self.load_private(
layouter.namespace(|| "load coin_blind"),
config.advices[0],
self.coin_blind,
)?;
let public_key = {
let nullifier_k = OrchardFixedBases::NullifierK;
let nullifier_k = FixedPoint::from_inner(ecc_chip.clone(), nullifier_k);
nullifier_k.mul_base_field(layouter.namespace(|| "[x_s] Nullifier"), secret_key)?
};
let (pub_x, pub_y) = (public_key.inner().x(), public_key.inner().y());
// =========
// Coin hash
// =========
let messages = [[pub_x, pub_y], [value, asset], [serial, coin_blind]];
let mut hashes = vec![];
for message in messages.iter() {
let hash = {
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, P128Pow5T3, 3, 2>::from_inner(StateWord::new(
var, value,
)))
};
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 (a, b)"),
poseidon_message,
)?;
let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
poseidon_output
};
hashes.push(hash);
}
let coin = layouter.assign_region(
|| " `coin` = hash(a,b) + hash(c, d) + hash(e, f)",
|mut region| {
config.q_add.enable(&mut region, 0)?;
copy(&mut region, || "copy ab", config.advices[6], 0, &hashes[0])?;
copy(&mut region, || "copy cd", config.advices[7], 0, &hashes[1])?;
copy(&mut region, || "copy ef", config.advices[8], 0, &hashes[2])?;
let scalar_val = hashes[0]
.value()
.zip(hashes[1].value())
.zip(hashes[2].value())
.map(|(abcd, ef)| abcd.0 + abcd.1 + ef);
let cell = region.assign_advice(
|| "hash(a,b)+hash(c,d)+hash(e,f)",
config.advices[5],
0,
|| scalar_val.ok_or(Error::SynthesisError),
)?;
Ok(CellValue::new(cell, scalar_val))
},
)?;
// ===========
// Merkle root
// ===========
let leaf = self.load_private(
layouter.namespace(|| "load leaf"),
config.advices[0],
self.leaf,
)?;
//let leaf = self.load_private(
// layouter.namespace(|| "load leaf"),
// config.advices[0],
// self.leaf,
//)?;
let path = MerklePath {
chip_1: merkle_chip_1,
@@ -359,7 +470,7 @@ impl Circuit<pallas::Base> for BurnCircuit {
};
let computed_final_root =
path.calculate_root(layouter.namespace(|| "calculate root"), leaf)?;
path.calculate_root(layouter.namespace(|| "calculate root"), coin)?;
layouter.constrain_instance(
computed_final_root.cell(),
@@ -502,11 +613,15 @@ fn root(path: [pallas::Base; 32], leaf_pos: u32, leaf: pallas::Base) -> pallas::
node
}
fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
pallas::Scalar::from_repr(x.to_repr()).unwrap()
}
fn main() {
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 11;
let secret_key = pallas::Scalar::random(&mut OsRng);
let secret = pallas::Base::random(&mut OsRng);
let serial = pallas::Base::random(&mut OsRng);
let value = 42;
@@ -514,15 +629,15 @@ fn main() {
// Nullifier = poseidon(sinsemilla(secret_key), serial)
let domain = primitives::sinsemilla::HashDomain::new(S_PERSONALIZATION);
let bits_secretkey: Vec<bool> = secret_key.to_le_bits().iter().by_val().collect();
let hashed_secret_key = domain.hash(iter::empty().chain(bits_secretkey)).unwrap();
//let bits_secretkey: Vec<bool> = secret_key.to_le_bits().iter().by_val().collect();
//let hashed_secret_key = domain.hash(iter::empty().chain(bits_secretkey)).unwrap();
let nullifier = [hashed_secret_key, serial];
let nullifier = [secret, serial];
let nullifier =
primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(nullifier);
// Public key derivation
let public_key = OrchardFixedBases::SpendAuthG.generator() * secret_key;
let public_key = OrchardFixedBases::NullifierK.generator() * mod_r_p(secret);
let coords = public_key.to_affine().coordinates().unwrap();
// Construct Coin
@@ -539,17 +654,31 @@ fn main() {
coin += hash;
}
let mut tree = BridgeTree::<MerkleNode, 32>::new(100);
for _ in 0..10 {
let random_node = MerkleNode(pallas::Base::random(&mut OsRng));
tree.append(&random_node);
}
let node = MerkleNode(coin.clone());
tree.append(&node);
tree.witness();
for _ in 0..10 {
let random_node = MerkleNode(pallas::Base::random(&mut OsRng));
tree.append(&random_node);
}
let (merkle_position, merkle_path) = tree.authentication_path(&node).unwrap();
// Merkle root
let leaf = pallas::Base::random(&mut OsRng);
let pos = rand::random::<u32>();
let path: Vec<_> = (0..32).map(|_| pallas::Base::random(&mut OsRng)).collect();
let merkle_root = root(path.clone().try_into().unwrap(), pos, leaf);
let pos: u64 = merkle_position.into();
let path: Vec<pallas::Base> = merkle_path.iter().map(|node| node.0).collect();
let merkle_root = tree.root().0;
// Value and asset commitments
let value_blind = pallas::Scalar::random(&mut OsRng);
let asset_blind = pallas::Scalar::random(&mut OsRng);
let value_commit = pedersen_commitment(value, value_blind);
let asset_commit = pedersen_commitment(asset, asset_blind);
let value_commit = pedersen_commitment_u64(value, value_blind);
let asset_commit = pedersen_commitment_u64(asset, asset_blind);
let value_coords = value_commit.to_affine().coordinates().unwrap();
let asset_coords = asset_commit.to_affine().coordinates().unwrap();
@@ -571,15 +700,14 @@ fn main() {
];
let circuit = BurnCircuit {
secret_key: Some(hashed_secret_key),
secret_key: Some(secret),
serial: Some(serial),
value: Some(pallas::Base::from(value)),
asset: Some(pallas::Base::from(asset)),
coin_blind: Some(coin_blind),
value_blind: Some(value_blind),
asset_blind: Some(asset_blind),
leaf: Some(leaf),
leaf_pos: Some(pos),
leaf_pos: Some(pos as u32),
merkle_path: Some(path.try_into().unwrap()),
sig_secret: Some(sig_secret),
};

View File

@@ -1,6 +1,5 @@
use async_std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::convert::TryInto;
use std::path::PathBuf;
use std::str::FromStr;

View File

@@ -1,89 +1,515 @@
use drk::{Decodable, ZkContract};
use std::fs::File;
use std::time::Instant;
use bls12_381::Scalar;
use ff::{Field, PrimeField};
use group::{Curve, Group};
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn, Selector,
},
poly::Rotation,
};
use halo2_gadgets::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::chip::{MerkleChip, MerkleConfig},
merkle::MerklePath,
},
utilities::{
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{Curve, Group},
pallas,
};
use rand::rngs::OsRng;
//type Result<T> = std::result::Result<T, failure::Error>;
use drk::Result;
use drk::crypto::{
constants::{
sinsemilla::{OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION},
OrchardFixedBases,
},
proof::{Proof, ProvingKey, VerifyingKey},
util::{pedersen_commitment_scalar, pedersen_commitment_u64},
};
// Unpack a value (such as jubjub::Fr) into 256 Scalar binary digits
fn unpack<F: PrimeField>(value: F) -> Vec<Scalar> {
let mut bits = Vec::new();
print!("Unpack: ");
for (_i, bit) in value.to_le_bits().into_iter().cloned().enumerate() {
match bit {
true => bits.push(Scalar::one()),
false => bits.push(Scalar::zero()),
}
print!("{}", if bit { 1 } else { 0 });
}
println!();
bits
#[derive(Clone, Debug)]
struct MintConfig {
primary: Column<InstanceColumn>,
q_add: Selector,
advices: [Column<Advice>; 10],
ecc_config: EccConfig,
merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_1:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_2:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
poseidon_config: PoseidonConfig<pallas::Base>,
}
// Unpack a u64 value in 64 Scalar binary digits
fn _unpack_u64(value: u64) -> Vec<Scalar> {
let mut result = Vec::with_capacity(64);
impl MintConfig {
fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
EccChip::construct(self.ecc_config.clone())
}
for i in 0..64 {
if (value >> i) & 1 == 1 {
result.push(Scalar::one());
} else {
result.push(Scalar::zero());
fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}
// The public input array offsets
const MINT_COIN_OFFSET: usize = 0;
const MINT_VALCOMX_OFFSET: usize = 1;
const MINT_VALCOMY_OFFSET: usize = 2;
const MINT_ASSCOMX_OFFSET: usize = 3;
const MINT_ASSCOMY_OFFSET: usize = 4;
#[derive(Default, Debug)]
struct MintCircuit {
pub_x: Option<pallas::Base>, // x coordinate for pubkey
pub_y: Option<pallas::Base>, // y coordinate for pubkey
value: Option<pallas::Base>, // The value of this coin
asset: Option<pallas::Base>, // The asset ID
serial: Option<pallas::Base>, // Unique serial number corresponding to this coin
coin_blind: Option<pallas::Base>, // Random blinding factor for coin
value_blind: Option<pallas::Scalar>, // Random blinding factor for value commitment
asset_blind: Option<pallas::Scalar>, // Random blinding factor for the asset ID
}
impl UtilitiesInstructions<pallas::Base> for MintCircuit {
type Var = CellValue<pallas::Base>;
}
impl Circuit<pallas::Base> for MintCircuit {
type Config = MintConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
// Advice columns used in the circuit
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(),
];
// Addition of two field elements
/*
let q_add = meta.selector();
meta.create_gate("poseidon_hash(a, b) + c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[6], Rotation::cur());
let hash = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (hash + c - sum)]
});
*/
let q_add = meta.selector();
meta.create_gate("a+b+c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[5], Rotation::cur());
let a = meta.query_advice(advices[6], Rotation::cur());
let b = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (a + b + c - sum)]
});
// Fixed columns for the Sinsemilla generator lookup table
let table_idx = meta.lookup_table_column();
let lookup = (
table_idx,
meta.lookup_table_column(),
meta.lookup_table_column(),
);
// Instance column used for public inputs
let primary = meta.instance_column();
meta.enable_equality(primary.into());
// Permutation over all advice columns
for advice in advices.iter() {
meta.enable_equality((*advice).into());
}
// Poseidon requires four advice columns, while ECC incomplete addition
// requires six. We can reduce the proof size by sharing fixed columns
// between the ECC and Poseidon chips.
// TODO: For multiple invocations they could/should be configured in
// parallel rather than sharing perhaps?
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();
// Also use the first Lagrange coefficient column for loading global constants.
meta.enable_constant(lagrange_coeffs[0]);
// Use one of the right-most advice columns for all of our range checks.
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
// Configuration for curve point operations.
// This uses 10 advice columns and spans the whole circuit.
let ecc_config = EccChip::<OrchardFixedBases>::configure(
meta,
advices,
lagrange_coeffs,
range_check.clone(),
);
// Configuration for the Poseidon hash
let poseidon_config = PoseidonChip::configure(
meta,
P128Pow5T3,
advices[6..9].try_into().unwrap(),
advices[5],
rc_a,
rc_b,
);
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_1, merkle_config_1) = {
let sinsemilla_config_1 = SinsemillaChip::configure(
meta,
advices[..5].try_into().unwrap(),
advices[6],
lagrange_coeffs[0],
lookup,
range_check.clone(),
);
let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
(sinsemilla_config_1, merkle_config_1)
};
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_2, merkle_config_2) = {
let sinsemilla_config_2 = SinsemillaChip::configure(
meta,
advices[5..].try_into().unwrap(),
advices[7],
lagrange_coeffs[1],
lookup,
range_check,
);
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
(sinsemilla_config_2, merkle_config_2)
};
MintConfig {
primary,
q_add,
advices,
ecc_config,
merkle_config_1,
merkle_config_2,
sinsemilla_config_1,
sinsemilla_config_2,
poseidon_config,
}
}
result
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
// Load the Sinsemilla generator lookup table used by the whole circuit.
SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
let ecc_chip = config.ecc_chip();
let pub_x = self.load_private(
layouter.namespace(|| "load pubkey x"),
config.advices[0],
self.pub_x,
)?;
let pub_y = self.load_private(
layouter.namespace(|| "load pubkey y"),
config.advices[0],
self.pub_y,
)?;
let value = self.load_private(
layouter.namespace(|| "load value"),
config.advices[0],
self.value,
)?;
let asset = self.load_private(
layouter.namespace(|| "load asset"),
config.advices[0],
self.asset,
)?;
let serial = self.load_private(
layouter.namespace(|| "load serial"),
config.advices[0],
self.serial,
)?;
let coin_blind = self.load_private(
layouter.namespace(|| "load coin_blind"),
config.advices[0],
self.coin_blind,
)?;
// =========
// Coin hash
// =========
let messages = [[pub_x, pub_y], [value, asset], [serial, coin_blind]];
let mut hashes = vec![];
for message in messages.iter() {
let hash = {
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, P128Pow5T3, 3, 2>::from_inner(StateWord::new(
var, value,
)))
};
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 (a, b)"),
poseidon_message,
)?;
let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
poseidon_output
};
hashes.push(hash);
}
let coin = layouter.assign_region(
|| " `coin` = hash(a,b) + hash(c, d) + hash(e, f)",
|mut region| {
config.q_add.enable(&mut region, 0)?;
copy(&mut region, || "copy ab", config.advices[6], 0, &hashes[0])?;
copy(&mut region, || "copy cd", config.advices[7], 0, &hashes[1])?;
copy(&mut region, || "copy ef", config.advices[8], 0, &hashes[2])?;
let scalar_val = hashes[0]
.value()
.zip(hashes[1].value())
.zip(hashes[2].value())
.map(|(abcd, ef)| abcd.0 + abcd.1 + ef);
let cell = region.assign_advice(
|| "hash(a,b)+hash(c,d)+hash(e,f)",
config.advices[5],
0,
|| scalar_val.ok_or(Error::SynthesisError),
)?;
Ok(CellValue::new(cell, scalar_val))
},
)?;
// Constrain the coin C
layouter.constrain_instance(coin.cell(), config.primary, MINT_COIN_OFFSET)?;
// ================
// Value commitment
// ================
// This constant one is used for short multiplication
let one = self.load_private(
layouter.namespace(|| "load constant one"),
config.advices[0],
Some(pallas::Base::one()),
)?;
// v * G_1
let (commitment, _) = {
let value_commit_v = OrchardFixedBases::ValueCommitV;
let value_commit_v = FixedPoint::from_inner(ecc_chip.clone(), value_commit_v);
value_commit_v.mul_short(layouter.namespace(|| "[value] ValueCommitV"), (value, one))?
};
// r_V * G_2
let (blind, _rcv) = {
let rcv = self.value_blind;
let value_commit_r = OrchardFixedBases::ValueCommitR;
let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
};
// Constrain the value commitment coordinates
let value_commit = commitment.add(layouter.namespace(|| "valuecommit"), &blind)?;
layouter.constrain_instance(
value_commit.inner().x().cell(),
config.primary,
MINT_VALCOMX_OFFSET,
)?;
layouter.constrain_instance(
value_commit.inner().y().cell(),
config.primary,
MINT_VALCOMY_OFFSET,
)?;
// ================
// Asset commitment
// ================
// a * G_1
let (commitment, _) = {
let asset_commit_v = OrchardFixedBases::ValueCommitV;
let asset_commit_v = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_v);
asset_commit_v.mul_short(layouter.namespace(|| "[asset] ValueCommitV"), (asset, one))?
};
// r_A * G_2
let (blind, _rca) = {
let rca = self.asset_blind;
let asset_commit_r = OrchardFixedBases::ValueCommitR;
let asset_commit_r = FixedPoint::from_inner(ecc_chip, asset_commit_r);
asset_commit_r.mul(layouter.namespace(|| "[asset_blind] ValueCommitR"), rca)?
};
// Constrain the asset commitment coordinates
let asset_commit = commitment.add(layouter.namespace(|| "assetcommit"), &blind)?;
layouter.constrain_instance(
asset_commit.inner().x().cell(),
config.primary,
MINT_ASSCOMX_OFFSET,
)?;
layouter.constrain_instance(
asset_commit.inner().y().cell(),
config.primary,
MINT_ASSCOMY_OFFSET,
)?;
// At this point we've enforced all of our public inputs.
Ok(())
}
}
fn main() -> Result<()> {
fn main() {
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 11;
let pubkey = pallas::Point::random(&mut OsRng);
let coords = pubkey.to_affine().coordinates().unwrap();
let value = 42;
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);
// poseidon_hash(x, y) + poseidon_hash(value, asset) + poseidon_hash(serial, coin_blind)
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() {
let hash = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
coin += hash;
}
let value_commit = pedersen_commitment_u64(value, value_blind);
let value_coords = value_commit.to_affine().coordinates().unwrap();
let asset_commit = pedersen_commitment_u64(asset, asset_blind);
let asset_coords = asset_commit.to_affine().coordinates().unwrap();
let public_inputs = vec![
coin,
*value_coords.x(),
*value_coords.y(),
*asset_coords.x(),
*asset_coords.y(),
];
let circuit = MintCircuit {
pub_x: Some(*coords.x()),
pub_y: Some(*coords.y()),
value: Some(pallas::Base::from(value)),
asset: Some(pallas::Base::from(asset)),
serial: Some(serial),
coin_blind: Some(coin_blind),
value_blind: Some(value_blind),
asset_blind: Some(asset_blind),
};
let prover = MockProver::run(k, &circuit, vec![public_inputs.clone()]).unwrap();
assert_eq!(prover.verify(), Ok(()));
// Actual ZK proof
let start = Instant::now();
let file = File::open("mint.zcd")?;
let mut visor = ZkContract::decode(file)?;
println!("{}", visor.name);
//ZkContract::load_contract(bytes);
println!("Loaded contract: [{:?}]", start.elapsed());
let vk = VerifyingKey::build(k, MintCircuit::default());
let pk = ProvingKey::build(k, MintCircuit::default());
println!("Setup: [{:?}]", start.elapsed());
println!("Stats:");
println!(" Constants: {}", visor.vm.constants.len());
println!(" Alloc: {}", visor.vm.alloc.len());
println!(" Operations: {}", visor.vm.ops.len());
println!(
" Constraint Instructions: {}",
visor.vm.constraints.len()
);
let start = Instant::now();
let proof = Proof::create(&pk, &[circuit], &public_inputs).unwrap();
println!("Prove: [{:?}]", start.elapsed());
visor.setup("mint.zts")?;
// We use the ExtendedPoint in calculations because it's faster
let public_point = jubjub::ExtendedPoint::from(jubjub::SubgroupPoint::random(&mut OsRng));
// But to serialize we need to convert to affine (which has the (u, v) values)
let public_affine = public_point.to_affine();
let randomness_value: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
for param in visor.param_names() {
println!("Param name: {}", param);
}
visor.set_param("public_u", public_affine.get_u())?;
visor.set_param("public_v", public_affine.get_v())?;
for (i, param_bit) in unpack(randomness_value).into_iter().enumerate() {
visor.set_param(&format!("vc_randomness_{}", i), param_bit)?;
}
let proof = visor.prove()?;
assert_eq!(proof.public.len(), 2);
for (name, value) in &proof.public {
println!("Public {} = {:?}", name, value);
}
assert!(visor.verify(&proof));
Ok(())
let start = Instant::now();
assert!(proof.verify(&vk, &public_inputs).is_ok());
println!("Verify: [{:?}]", start.elapsed());
}

180
src/bin/tx2.rs Normal file
View File

@@ -0,0 +1,180 @@
use rand::rngs::OsRng;
use std::{fmt, time::Instant};
use halo2_gadgets::ecc::FixedPoints;
use incrementalmerkletree::Hashable;
use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Tree};
use pasta_curves::{
arithmetic::{CurveAffine, Field, FieldExt},
pallas,
};
use drk::{
circuit::{mint_contract::MintContract, spend_contract::SpendContract},
crypto::{
coin::Coin,
constants::OrchardFixedBases,
merkle_node2::MerkleNode,
note::{EncryptedNote, Note},
nullifier::Nullifier,
proof::{Proof, ProvingKey, VerifyingKey},
schnorr,
util::mod_r_p,
util::{pedersen_commitment_scalar, pedersen_commitment_u64},
},
state::{state_transition, ProgramState, StateUpdate},
tx,
};
struct MemoryState {
// The entire merkle tree state
tree: BridgeTree<MerkleNode, 32>,
// List of all previous and the current merkle roots
// This is the hashed value of all the children.
merkle_roots: Vec<MerkleNode>,
// Nullifiers prevent double spending
nullifiers: Vec<Nullifier>,
// All received coins
// NOTE: we need maybe a flag to keep track of which ones are spent
// Maybe the spend field links to a tx hash:input index
// We should also keep track of the tx hash:output index where this
// coin was received
own_coins: Vec<(Coin, Note)>,
mint_vk: VerifyingKey,
spend_vk: VerifyingKey,
// Public key of the cashier
cashier_public: schnorr::PublicKey,
// List of all our secret keys
secrets: Vec<pallas::Base>,
}
impl ProgramState for MemoryState {
fn is_valid_cashier_public_key(&self, public: &schnorr::PublicKey) -> bool {
public == &self.cashier_public
}
fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool {
self.merkle_roots.iter().any(|m| m == merkle_root)
}
fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
self.nullifiers.iter().any(|n| n == nullifier)
}
fn mint_vk(&self) -> &VerifyingKey {
&self.mint_vk
}
fn spend_vk(&self) -> &VerifyingKey {
&self.spend_vk
}
}
impl MemoryState {
fn apply(&mut self, mut update: StateUpdate) {
// Extend our list of nullifiers with the ones from the update
self.nullifiers.append(&mut update.nullifiers);
// Update merkle tree and witnesses
for (coin, enc_note) in update.coins.into_iter().zip(update.enc_notes.into_iter()) {
// Add the new coins to the merkle tree
let node = MerkleNode(coin.0);
self.tree.append(&node);
//// Keep track of all merkle roots that have existed
self.merkle_roots.push(self.tree.root());
if let Some((note, secret)) = self.try_decrypt_note(enc_note) {
self.own_coins.push((coin, note));
self.tree.witness();
}
}
}
fn try_decrypt_note(&self, ciphertext: EncryptedNote) -> Option<(Note, pallas::Base)> {
// Loop through all our secret keys...
for secret in &self.secrets {
// ... attempt to decrypt the note ...
if let Ok(note) = ciphertext.decrypt(secret) {
// ... and return the decrypted note for this coin.
return Some((note, *secret));
}
}
// We weren't able to decrypt the note with any of our keys.
None
}
}
fn main() -> std::result::Result<(), failure::Error> {
let cashier_secret = schnorr::SecretKey::random();
let cashier_public = cashier_secret.public_key();
let secret = pallas::Base::random(&mut OsRng);
let public = OrchardFixedBases::NullifierK.generator() * mod_r_p(secret);
const K: u32 = 11;
let mint_vk = VerifyingKey::build(K, MintContract::default());
let spend_vk = VerifyingKey::build(K, SpendContract::default());
let mut state = MemoryState {
tree: BridgeTree::<MerkleNode, 32>::new(100),
merkle_roots: vec![],
nullifiers: vec![],
own_coins: vec![],
mint_vk,
spend_vk,
cashier_public,
secrets: vec![secret],
};
let token_id = pallas::Base::from(110);
let builder = tx::TransactionBuilder {
clear_inputs: vec![tx::TransactionBuilderClearInputInfo {
value: 110,
token_id,
signature_secret: cashier_secret,
}],
inputs: vec![],
outputs: vec![tx::TransactionBuilderOutputInfo {
value: 110,
token_id,
public,
}],
};
let tx = builder.build()?;
tx.verify(&state.mint_vk, &state.spend_vk)
.expect("tx verify");
let note = tx.outputs[0].enc_note.decrypt(&secret)?;
let update = state_transition(&state, tx)?;
state.apply(update);
// Now spend
let (coin, note) = &state.own_coins[0];
let node = MerkleNode(coin.0.clone());
let (leaf_position, merkle_path) = state.tree.authentication_path(&node).unwrap();
let builder = tx::TransactionBuilder {
clear_inputs: vec![],
inputs: vec![tx::TransactionBuilderInputInfo {
leaf_position,
merkle_path,
secret,
note: note.clone(),
}],
outputs: vec![tx::TransactionBuilderOutputInfo {
value: 110,
token_id,
public,
}],
};
let tx = builder.build()?;
let update = state_transition(&state, tx)?;
state.apply(update);
Ok(())
}

153
src/bin/vm2.rs Normal file
View File

@@ -0,0 +1,153 @@
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn, Selector,
},
poly::Rotation,
};
use halo2_gadgets::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint, FixedPoints,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
primitives,
primitives::{
poseidon::{ConstantLength, P128Pow5T3},
sinsemilla::S_PERSONALIZATION,
},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::chip::{MerkleChip, MerkleConfig},
merkle::MerklePath,
},
utilities::{
lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{ff::PrimeFieldBits, Curve, Group},
pallas,
};
use rand::rngs::OsRng;
use std::{collections::HashMap, fs::File, time::Instant};
use drk::{
crypto::{
constants::{
sinsemilla::{OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION},
OrchardFixedBases,
},
proof::{Proof, ProvingKey, VerifyingKey},
util::{pedersen_commitment_scalar, pedersen_commitment_u64},
},
serial::Decodable,
vm2,
};
fn main() -> std::result::Result<(), failure::Error> {
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 11;
let start = Instant::now();
let file = File::open("proof/mint.zk.bin")?;
let zkbin = 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 += primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
}
let coin2 = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>)
.hash([*coords.x(), *coords.y()]);
let value_commit = pedersen_commitment_u64(value, value_blind);
let value_coords = value_commit.to_affine().coordinates().unwrap();
let asset_commit = pedersen_commitment_u64(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 = 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(k, empty_circuit.clone());
let pk = ProvingKey::build(k, 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(())
}

210
src/bin/vm2_burn.rs Normal file
View File

@@ -0,0 +1,210 @@
use std::iter;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn, Selector,
},
poly::Rotation,
};
use halo2_gadgets::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint, FixedPoints,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
primitives,
primitives::{
poseidon::{ConstantLength, P128Pow5T3},
sinsemilla::S_PERSONALIZATION,
},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::chip::{MerkleChip, MerkleConfig},
merkle::MerklePath,
},
utilities::{
lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use pasta_curves::{
arithmetic::{CurveAffine, Field},
group::{ff::PrimeFieldBits, Curve, Group},
pallas,
};
use rand::rngs::OsRng;
use std::{collections::HashMap, fs::File, time::Instant};
use drk::{
crypto::{
constants::{
sinsemilla::{
i2lebsp, OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION,
},
OrchardFixedBases,
},
proof::{Proof, ProvingKey, VerifyingKey},
util::{pedersen_commitment_scalar, pedersen_commitment_u64},
},
serial::Decodable,
vm2,
};
fn root(path: [pallas::Base; 32], leaf_pos: u32, leaf: pallas::Base) -> pallas::Base {
let domain = primitives::sinsemilla::HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let pos_bool = i2lebsp::<32>(leaf_pos as u64);
let mut node = leaf;
for (l, (sibling, pos)) in path.iter().zip(pos_bool.iter()).enumerate() {
let (left, right) = if *pos {
(*sibling, node)
} else {
(node, *sibling)
};
let l_star = i2lebsp::<10>(l as u64);
let left: Vec<_> = left.to_le_bits().iter().by_val().take(255).collect();
let right: Vec<_> = right.to_le_bits().iter().by_val().take(255).collect();
let mut message = l_star.to_vec();
message.extend_from_slice(&left);
message.extend_from_slice(&right);
node = domain.hash(message.into_iter()).unwrap();
}
node
}
fn main() -> std::result::Result<(), failure::Error> {
// The number of rows in our circuit cannot exceed 2^k
let k: u32 = 11;
let start = Instant::now();
let file = File::open("proof/burn.zk.bin")?;
let zkbin = 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["Burn"];
//contract.witness_base(...);
//contract.witness_base(...);
//contract.witness_base(...);
let secret = pallas::Scalar::random(&mut OsRng);
let serial = pallas::Base::random(&mut OsRng);
let value = 110;
let asset = 1;
// Nullifier = poseidon(sinsemilla(secret_key), serial)
let domain = primitives::sinsemilla::HashDomain::new(S_PERSONALIZATION);
let bits_secretkey: Vec<bool> = secret.to_le_bits().iter().by_val().collect();
let hashed_secret_key = domain.hash(iter::empty().chain(bits_secretkey)).unwrap();
let nullifier = [hashed_secret_key, serial];
let nullifier =
primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(nullifier);
// Public key derivation
let public_key = OrchardFixedBases::SpendAuthG.generator() * secret;
let coords = public_key.to_affine().coordinates().unwrap();
// Construct Coin
let mut coin = pallas::Base::zero();
let coin_blind = pallas::Base::random(&mut OsRng);
let messages = [
[*coords.x(), *coords.y()],
[pallas::Base::from(value), pallas::Base::from(asset)],
[serial, coin_blind],
];
for msg in messages.iter() {
let hash = primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
coin += hash;
}
// Merkle root
let leaf = pallas::Base::random(&mut OsRng);
let leaf_pos = rand::random::<u32>();
let path: Vec<_> = (0..32).map(|_| pallas::Base::random(&mut OsRng)).collect();
let merkle_root = root(path.clone().try_into().unwrap(), leaf_pos, leaf);
// Value and asset commitments
let value_blind = pallas::Scalar::random(&mut OsRng);
let asset_blind = pallas::Scalar::random(&mut OsRng);
let value_commit = pedersen_commitment_u64(value, value_blind);
let asset_commit = pedersen_commitment_u64(asset, asset_blind);
let value_coords = value_commit.to_affine().coordinates().unwrap();
let asset_coords = asset_commit.to_affine().coordinates().unwrap();
// Derive signature public key from signature secret key
let sig_secret = pallas::Scalar::random(&mut OsRng);
let sig_pubkey = OrchardFixedBases::SpendAuthG.generator() * sig_secret;
let sig_coords = sig_pubkey.to_affine().coordinates().unwrap();
let public_inputs = vec![
nullifier,
merkle_root,
*value_coords.x(),
*value_coords.y(),
*asset_coords.x(),
*asset_coords.y(),
*sig_coords.x(),
*sig_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,
);
const_fixed_points.insert("SPEND_AUTH_G".to_string(), OrchardFixedBases::SpendAuthG);
let mut circuit = vm2::ZkCircuit::new(const_fixed_points, &zkbin.constants, contract);
let empty_circuit = circuit.clone();
circuit.witness_base("secret", hashed_secret_key)?;
circuit.witness_base("serial", serial)?;
circuit.witness_merkle_path("path", leaf_pos, path.try_into().unwrap())?;
circuit.witness_base("leaf", leaf)?;
circuit.witness_base("value", pallas::Base::from(value))?;
circuit.witness_base("asset", pallas::Base::from(asset))?;
circuit.witness_scalar("value_blind", value_blind)?;
circuit.witness_scalar("asset_blind", asset_blind)?;
circuit.witness_scalar("sig_secret", sig_secret)?;
// 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(k, empty_circuit.clone());
let pk = ProvingKey::build(k, 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(())
}

View File

@@ -1,7 +1,7 @@
use async_std::sync::Arc;
use std::marker::PhantomData;
use std::path::Path;
use async_std::sync::Arc;
use rocksdb::{ColumnFamily, ColumnFamilyDescriptor, Options, DB};
use crate::serial::{deserialize, serialize, Decodable, Encodable};

View File

@@ -1,179 +1,428 @@
#![allow(unused_imports)]
#![allow(unused_mut)]
use crate::crypto::merkle_node::SAPLING_COMMITMENT_TREE_DEPTH;
use bellman::{
gadgets::{
blake2s, boolean,
boolean::{AllocatedBit, Boolean},
multipack, num, Assignment,
},
groth16, Circuit, ConstraintSystem, SynthesisError,
};
use bls12_381::Bls12;
use ff::{Field, PrimeField};
use group::Curve;
use zcash_proofs::circuit::{ecc, pedersen_hash};
use pasta_curves::pallas;
pub struct MintContract {
pub value: Option<u64>,
pub token_id: Option<jubjub::Fr>,
pub randomness_value: Option<jubjub::Fr>,
pub randomness_token: Option<jubjub::Fr>,
pub serial: Option<jubjub::Fr>,
pub randomness_coin: Option<jubjub::Fr>,
pub public: Option<jubjub::SubgroupPoint>,
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
plonk,
plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn, Selector},
poly::Rotation,
};
use halo2_gadgets::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
StateWord, Word,
},
primitives::poseidon::{ConstantLength, P128Pow5T3},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::chip::{MerkleChip, MerkleConfig},
},
utilities::{
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
};
use crate::crypto::constants::{
sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
OrchardFixedBases,
};
#[derive(Clone, Debug)]
pub struct MintConfig {
primary: Column<InstanceColumn>,
q_add: Selector,
advices: [Column<Advice>; 10],
ecc_config: EccConfig,
merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_1:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
sinsemilla_config_2:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
poseidon_config: PoseidonConfig<pallas::Base>,
}
impl Circuit<bls12_381::Scalar> for MintContract {
fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
// Line 20: u64_as_binary_le value param:value
let value = boolean::u64_into_boolean_vec_le(
cs.namespace(|| "Line 20: u64_as_binary_le value param:value"),
impl MintConfig {
fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
EccChip::construct(self.ecc_config.clone())
}
fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}
// The public input array offsets
const MINT_COIN_OFFSET: usize = 0;
const MINT_VALCOMX_OFFSET: usize = 1;
const MINT_VALCOMY_OFFSET: usize = 2;
const MINT_ASSCOMX_OFFSET: usize = 3;
const MINT_ASSCOMY_OFFSET: usize = 4;
#[derive(Default, Debug)]
pub struct MintContract {
pub pub_x: Option<pallas::Base>, // x coordinate for pubkey
pub pub_y: Option<pallas::Base>, // y coordinate for pubkey
pub value: Option<pallas::Base>, // The value of this coin
pub asset: Option<pallas::Base>, // The asset ID
pub serial: Option<pallas::Base>, // Unique serial number corresponding to this coin
pub coin_blind: Option<pallas::Base>, // Random blinding factor for coin
pub value_blind: Option<pallas::Scalar>, // Random blinding factor for value commitment
pub asset_blind: Option<pallas::Scalar>, // Random blinding factor for the asset ID
}
impl UtilitiesInstructions<pallas::Base> for MintContract {
type Var = CellValue<pallas::Base>;
}
impl Circuit<pallas::Base> for MintContract {
type Config = MintConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
// Advice columns used in the circuit
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(),
];
// Addition of two field elements
/*
let q_add = meta.selector();
meta.create_gate("poseidon_hash(a, b) + c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[6], Rotation::cur());
let hash = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (hash + c - sum)]
});
*/
let q_add = meta.selector();
meta.create_gate("a+b+c", |meta| {
let q_add = meta.query_selector(q_add);
let sum = meta.query_advice(advices[5], Rotation::cur());
let a = meta.query_advice(advices[6], Rotation::cur());
let b = meta.query_advice(advices[7], Rotation::cur());
let c = meta.query_advice(advices[8], Rotation::cur());
vec![q_add * (a + b + c - sum)]
});
// Fixed columns for the Sinsemilla generator lookup table
let table_idx = meta.lookup_table_column();
let lookup = (
table_idx,
meta.lookup_table_column(),
meta.lookup_table_column(),
);
// Instance column used for public inputs
let primary = meta.instance_column();
meta.enable_equality(primary.into());
// Permutation over all advice columns
for advice in advices.iter() {
meta.enable_equality((*advice).into());
}
// Poseidon requires four advice columns, while ECC incomplete addition
// requires six. We can reduce the proof size by sharing fixed columns
// between the ECC and Poseidon chips.
// TODO: For multiple invocations they could/should be configured in
// parallel rather than sharing perhaps?
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();
// Also use the first Lagrange coefficient column for loading global constants.
meta.enable_constant(lagrange_coeffs[0]);
// Use one of the right-most advice columns for all of our range checks.
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
// Configuration for curve point operations.
// This uses 10 advice columns and spans the whole circuit.
let ecc_config = EccChip::<OrchardFixedBases>::configure(
meta,
advices,
lagrange_coeffs,
range_check.clone(),
);
// Configuration for the Poseidon hash
let poseidon_config = PoseidonChip::configure(
meta,
P128Pow5T3,
advices[6..9].try_into().unwrap(),
advices[5],
rc_a,
rc_b,
);
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_1, merkle_config_1) = {
let sinsemilla_config_1 = SinsemillaChip::configure(
meta,
advices[..5].try_into().unwrap(),
advices[6],
lagrange_coeffs[0],
lookup,
range_check.clone(),
);
let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
(sinsemilla_config_1, merkle_config_1)
};
// Configuration for a Sinsemilla hash instantiation and a
// Merkle hash instantiation using this Sinsemilla instance.
// Since the Sinsemilla config uses only 5 advice columns,
// we can fit two instances side-by-side.
let (sinsemilla_config_2, merkle_config_2) = {
let sinsemilla_config_2 = SinsemillaChip::configure(
meta,
advices[5..].try_into().unwrap(),
advices[7],
lagrange_coeffs[1],
lookup,
range_check,
);
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
(sinsemilla_config_2, merkle_config_2)
};
MintConfig {
primary,
q_add,
advices,
ecc_config,
merkle_config_1,
merkle_config_2,
sinsemilla_config_1,
sinsemilla_config_2,
poseidon_config,
}
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), plonk::Error> {
// Load the Sinsemilla generator lookup table used by the whole circuit.
SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
let ecc_chip = config.ecc_chip();
let pub_x = self.load_private(
layouter.namespace(|| "load pubkey x"),
config.advices[0],
self.pub_x,
)?;
let pub_y = self.load_private(
layouter.namespace(|| "load pubkey y"),
config.advices[0],
self.pub_y,
)?;
let value = self.load_private(
layouter.namespace(|| "load value"),
config.advices[0],
self.value,
)?;
// Line 21: fr_as_binary_le token_id param:token_id
let token_id = boolean::field_into_boolean_vec_le(
cs.namespace(|| "Line 21: fr_as_binary_le token_id param:token_id"),
self.token_id,
let asset = self.load_private(
layouter.namespace(|| "load asset"),
config.advices[0],
self.asset,
)?;
// Line 22: fr_as_binary_le randomness_value param:randomness_value
let randomness_value = boolean::field_into_boolean_vec_le(
cs.namespace(|| "Line 22: fr_as_binary_le randomness_value param:randomness_value"),
self.randomness_value,
)?;
// Line 23: fr_as_binary_le randomness_token param:randomness_token
let randomness_token = boolean::field_into_boolean_vec_le(
cs.namespace(|| "Line 23: fr_as_binary_le randomness_token param:randomness_token"),
self.randomness_token,
)?;
// Line 24: fr_as_binary_le serial param:serial
let serial = boolean::field_into_boolean_vec_le(
cs.namespace(|| "Line 24: fr_as_binary_le serial param:serial"),
let serial = self.load_private(
layouter.namespace(|| "load serial"),
config.advices[0],
self.serial,
)?;
// Line 25: fr_as_binary_le randomness_coin param:randomness_coin
let randomness_coin = boolean::field_into_boolean_vec_le(
cs.namespace(|| "Line 25: fr_as_binary_le randomness_coin param:randomness_coin"),
self.randomness_coin,
let coin_blind = self.load_private(
layouter.namespace(|| "load coin_blind"),
config.advices[0],
self.coin_blind,
)?;
// Line 27: witness public param:public
let public = ecc::EdwardsPoint::witness(
cs.namespace(|| "Line 27: witness public param:public"),
self.public.map(jubjub::ExtendedPoint::from),
)?;
// =========
// Coin hash
// =========
let messages = [[pub_x, pub_y], [value, asset], [serial, coin_blind]];
let mut hashes = vec![];
// Line 28: assert_not_small_order public
public.assert_not_small_order(cs.namespace(|| "Line 28: assert_not_small_order public"))?;
for message in messages.iter() {
let hash = {
let poseidon_message = layouter.assign_region(
|| "load message",
|mut region| {
let mut message_word = |i: usize| {
let value = message[i].value();
let var = region.assign_advice(
|| format!("load message_{}", i),
config.poseidon_config.state()[i],
0,
|| value.ok_or(plonk::Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, P128Pow5T3, 3, 2>::from_inner(StateWord::new(
var, value,
)))
};
Ok([message_word(0)?, message_word(1)?])
},
)?;
// Line 33: ec_mul_const vcv value G_VCV
let vcv = ecc::fixed_base_multiplication(
cs.namespace(|| "Line 33: ec_mul_const vcv value G_VCV"),
&zcash_proofs::constants::VALUE_COMMITMENT_VALUE_GENERATOR,
&value,
)?;
let poseidon_hasher = PoseidonHash::init(
config.poseidon_chip(),
layouter.namespace(|| "Poseidon init"),
ConstantLength::<2>,
)?;
// Line 34: ec_mul_const rcv randomness_value G_VCR
let rcv = ecc::fixed_base_multiplication(
cs.namespace(|| "Line 34: ec_mul_const rcv randomness_value G_VCR"),
&zcash_proofs::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
&randomness_value,
)?;
let poseidon_output = poseidon_hasher.hash(
layouter.namespace(|| "Poseidon hash (a, b)"),
poseidon_message,
)?;
// Line 35: ec_add cv vcv rcv
let cv = vcv.add(cs.namespace(|| "Line 35: ec_add cv vcv rcv"), &rcv)?;
let poseidon_output: CellValue<pallas::Base> = poseidon_output.inner().into();
poseidon_output
};
// Line 37: emit_ec cv
cv.inputize(cs.namespace(|| "Line 37: emit_ec cv"))?;
// Line 42: ec_mul_const vca token_id G_VCV
let vca = ecc::fixed_base_multiplication(
cs.namespace(|| "Line 42: ec_mul_const vca token_id G_VCV"),
&zcash_proofs::constants::VALUE_COMMITMENT_VALUE_GENERATOR,
&token_id,
)?;
// Line 43: ec_mul_const rca randomness_token G_VCR
let rca = ecc::fixed_base_multiplication(
cs.namespace(|| "Line 43: ec_mul_const rca randomness_token G_VCR"),
&zcash_proofs::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
&randomness_token,
)?;
// Line 44: ec_add ca vca rca
let ca = vca.add(cs.namespace(|| "Line 44: ec_add ca vca rca"), &rca)?;
// Line 46: emit_ec ca
ca.inputize(cs.namespace(|| "Line 46: emit_ec ca"))?;
// Line 53: alloc_binary preimage
let mut preimage = vec![];
// Line 56: ec_repr repr_public public
let repr_public = public.repr(cs.namespace(|| "Line 56: ec_repr repr_public public"))?;
// Line 57: binary_extend preimage repr_public
preimage.extend(repr_public);
// Line 60: binary_extend preimage value
preimage.extend(value);
// Line 99: binary_extend preimage token_id
preimage.extend(token_id);
// add 4 zero bits
for _ in 0..4 {
// Line 71: alloc_const_bit zero_bit false
let zero_bit = Boolean::constant(false);
// Line 72: binary_push preimage zero_bit
preimage.push(zero_bit);
hashes.push(hash);
}
// Line 67: binary_extend preimage serial
preimage.extend(serial);
let coin = layouter.assign_region(
|| " `coin` = hash(a,b) + hash(c, d) + hash(e, f)",
|mut region| {
config.q_add.enable(&mut region, 0)?;
// add 4 zero bits
for _ in 0..4 {
// Line 71: alloc_const_bit zero_bit false
let zero_bit = Boolean::constant(false);
copy(&mut region, || "copy ab", config.advices[6], 0, &hashes[0])?;
copy(&mut region, || "copy cd", config.advices[7], 0, &hashes[1])?;
copy(&mut region, || "copy ef", config.advices[8], 0, &hashes[2])?;
// Line 72: binary_push preimage zero_bit
preimage.push(zero_bit);
}
let scalar_val = hashes[0]
.value()
.zip(hashes[1].value())
.zip(hashes[2].value())
.map(|(abcd, ef)| abcd.0 + abcd.1 + ef);
// Line 83: binary_extend preimage randomness_coin
preimage.extend(randomness_coin);
// add 4 zero bits
for _ in 0..4 {
// Line 71: alloc_const_bit zero_bit false
let zero_bit = Boolean::constant(false);
// Line 72: binary_push preimage zero_bit
preimage.push(zero_bit);
}
// Line 120: static_assert_binary_size preimage 1088
assert_eq!(preimage.len(), 1088);
// Line 121: blake2s coin preimage CRH_IVK
let mut coin = blake2s::blake2s(
cs.namespace(|| "Line 121: blake2s coin preimage CRH_IVK"),
&preimage,
zcash_primitives::constants::CRH_IVK_PERSONALIZATION,
let cell = region.assign_advice(
|| "hash(a,b)+hash(c,d)+hash(e,f)",
config.advices[5],
0,
|| scalar_val.ok_or(plonk::Error::SynthesisError),
)?;
Ok(CellValue::new(cell, scalar_val))
},
)?;
// Line 122: emit_binary coin
multipack::pack_into_inputs(cs.namespace(|| "Line 122: emit_binary coin"), &coin)?;
// Constrain the coin C
layouter.constrain_instance(coin.cell(), config.primary, MINT_COIN_OFFSET)?;
// ================
// Value commitment
// ================
// This constant one is used for short multiplication
let one = self.load_private(
layouter.namespace(|| "load constant one"),
config.advices[0],
Some(pallas::Base::one()),
)?;
// v * G_1
let (commitment, _) = {
let value_commit_v = OrchardFixedBases::ValueCommitV;
let value_commit_v = FixedPoint::from_inner(ecc_chip.clone(), value_commit_v);
value_commit_v.mul_short(layouter.namespace(|| "[value] ValueCommitV"), (value, one))?
};
// r_V * G_2
let (blind, _rcv) = {
let rcv = self.value_blind;
let value_commit_r = OrchardFixedBases::ValueCommitR;
let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
};
// Constrain the value commitment coordinates
let value_commit = commitment.add(layouter.namespace(|| "valuecommit"), &blind)?;
layouter.constrain_instance(
value_commit.inner().x().cell(),
config.primary,
MINT_VALCOMX_OFFSET,
)?;
layouter.constrain_instance(
value_commit.inner().y().cell(),
config.primary,
MINT_VALCOMY_OFFSET,
)?;
// ================
// Asset commitment
// ================
// a * G_1
let (commitment, _) = {
let asset_commit_v = OrchardFixedBases::ValueCommitV;
let asset_commit_v = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_v);
asset_commit_v.mul_short(layouter.namespace(|| "[asset] ValueCommitV"), (asset, one))?
};
// r_A * G_2
let (blind, _rca) = {
let rca = self.asset_blind;
let asset_commit_r = OrchardFixedBases::ValueCommitR;
let asset_commit_r = FixedPoint::from_inner(ecc_chip, asset_commit_r);
asset_commit_r.mul(layouter.namespace(|| "[asset_blind] ValueCommitR"), rca)?
};
// Constrain the asset commitment coordinates
let asset_commit = commitment.add(layouter.namespace(|| "assetcommit"), &blind)?;
layouter.constrain_instance(
asset_commit.inner().x().cell(),
config.primary,
MINT_ASSCOMX_OFFSET,
)?;
layouter.constrain_instance(
asset_commit.inner().y().cell(),
config.primary,
MINT_ASSCOMY_OFFSET,
)?;
// At this point we've enforced all of our public inputs.
Ok(())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
/*
use async_executor::Executor;
use async_std::sync::{Arc, Mutex};
@@ -24,6 +25,7 @@ use crate::{
wallet::{walletdb::Balances, CashierDbPtr, Keypair, WalletPtr},
Result,
};
*/
#[derive(Debug, Clone, thiserror::Error)]
pub enum ClientFailed {
@@ -53,6 +55,7 @@ pub enum ClientFailed {
VerifyError(String),
}
/*
pub struct Client {
mint_params: bellman::groth16::Parameters<Bls12>,
spend_params: bellman::groth16::Parameters<Bls12>,
@@ -544,6 +547,7 @@ impl State {
}
}
}
*/
impl From<super::error::Error> for ClientFailed {
fn from(err: super::error::Error) -> ClientFailed {

208
src/crypto/arith_chip.rs Normal file
View File

@@ -0,0 +1,208 @@
use halo2::{
circuit::{Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
poly::Rotation,
};
use halo2_gadgets::utilities::{CellValue, Var};
use pasta_curves::pallas;
type Variable = CellValue<pallas::Base>;
// Replace with use pasta::Fp and pasta::Fq
type Fp = pallas::Base;
//type Fq = pallas::Scalar;
#[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(())
},
)
}
*/
}

View File

@@ -1,31 +1,41 @@
use std::io;
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{
error::Result,
serial::{Decodable, Encodable},
serial::{Decodable, Encodable, ReadExt, WriteExt},
Result,
};
#[derive(Clone, Debug, PartialEq)]
pub struct Coin {
pub repr: [u8; 32],
}
#[derive(Clone, Copy, Debug)]
pub struct Coin(pub pallas::Base);
impl Coin {
pub fn new(repr: [u8; 32]) -> Self {
Self { repr }
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
pallas::Base::from_bytes(bytes).map(Coin).unwrap()
}
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_bytes()
}
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
}
impl Encodable for Coin {
fn encode<S: io::Write>(&self, s: S) -> Result<usize> {
self.repr.encode(s)
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
s.write_slice(&self.to_bytes()[..])?;
Ok(32)
}
}
impl Decodable for Coin {
fn decode<D: io::Read>(d: D) -> Result<Self> {
Ok(Self {
repr: Decodable::decode(d)?,
})
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut bytes = [0u8; 32];
d.read_slice(&mut bytes)?;
let result = Self::from_bytes(&bytes);
Ok(result)
}
}

11
src/crypto/constants.rs Normal file
View File

@@ -0,0 +1,11 @@
pub mod fixed_bases;
pub mod sinsemilla;
pub mod util;
pub use fixed_bases::OrchardFixedBases;
pub const DRK_SCHNORR_DOMAIN: &[u8] = b"DarkFi_Schnorr";
pub const MERKLE_DEPTH_ORCHARD: usize = 32;
pub const L_ORCHARD_MERKLE: usize = 255;

View File

@@ -0,0 +1,214 @@
use arrayvec::ArrayVec;
use halo2_gadgets::ecc::{
chip::{compute_lagrange_coeffs, NUM_WINDOWS, NUM_WINDOWS_SHORT},
FixedPoints, H,
};
use pasta_curves::pallas;
use pasta_curves::{
arithmetic::{CurveAffine, Field, FieldExt},
group::Curve,
};
pub mod commit_ivk_r;
pub mod note_commit_r;
pub mod nullifier_k;
pub mod spend_auth_g;
pub mod value_commit_r;
pub mod value_commit_v;
/// SWU hash-to-curve personalization for the value commitment generator
pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv";
/// SWU hash-to-curve value for the value commitment generator
pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r";
/// SWU hash-to-curve value for the value commitment generator
pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v";
/// SWU hash-to-curve personalization for the note commitment generator
pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit";
/// SWU hash-to-curve personalization for the IVK commitment generator
pub const COMMIT_IVK_PERSONALIZATION: &str = "z.cash:Orchard-CommitIvk";
/// SWU hash-to-curve personalization for the spending key base point and
/// the nullifier base point K^Orchard
pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard";
/// Window size for fixed-base scalar multiplication
pub const FIXED_BASE_WINDOW_SIZE: usize = 3;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OrchardFixedBases {
CommitIvkR,
NoteCommitR,
ValueCommitR,
SpendAuthG,
NullifierK,
ValueCommitV,
}
impl FixedPoints<pallas::Affine> for OrchardFixedBases {
fn generator(&self) -> pallas::Affine {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::generator(),
OrchardFixedBases::NoteCommitR => note_commit_r::generator(),
OrchardFixedBases::ValueCommitR => value_commit_r::generator(),
OrchardFixedBases::SpendAuthG => spend_auth_g::generator(),
OrchardFixedBases::NullifierK => nullifier_k::generator(),
OrchardFixedBases::ValueCommitV => value_commit_v::generator(),
}
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::U.to_vec(),
OrchardFixedBases::NoteCommitR => note_commit_r::U.to_vec(),
OrchardFixedBases::ValueCommitR => value_commit_r::U.to_vec(),
OrchardFixedBases::SpendAuthG => spend_auth_g::U.to_vec(),
OrchardFixedBases::NullifierK => nullifier_k::U.to_vec(),
OrchardFixedBases::ValueCommitV => value_commit_v::U_SHORT.to_vec(),
}
}
fn z(&self) -> Vec<u64> {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::Z.to_vec(),
OrchardFixedBases::NoteCommitR => note_commit_r::Z.to_vec(),
OrchardFixedBases::ValueCommitR => value_commit_r::Z.to_vec(),
OrchardFixedBases::SpendAuthG => spend_auth_g::Z.to_vec(),
OrchardFixedBases::NullifierK => nullifier_k::Z.to_vec(),
OrchardFixedBases::ValueCommitV => value_commit_v::Z_SHORT.to_vec(),
}
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
match self {
OrchardFixedBases::ValueCommitV => {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT)
}
_ => compute_lagrange_coeffs(self.generator(), NUM_WINDOWS),
}
}
}
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
/// Each window will have $2^3 = 8$ points.
#[allow(dead_code)]
fn compute_window_table<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows);
// Generate window table entries for all windows but the last.
// For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B.
// Here, w ranges from [0..`num_windows - 1`)
for w in 0..(num_windows - 1) {
window_table.push(
(0..H)
.map(|k| {
// scalar = (k+2)*(8^w)
let scalar = C::ScalarExt::from_u64(k as u64 + 2)
* C::ScalarExt::from_u64(H as u64).pow(&[w as u64, 0, 0, 0]);
(base * scalar).to_affine()
})
.collect::<ArrayVec<C, H>>()
.into_inner()
.unwrap(),
);
}
// Generate window table entries for the last window, w = `num_windows - 1`.
// For the last window, we compute [k * (2^3)^w - sum]B, where sum is defined
// as sum = \sum_{j = 0}^{`num_windows - 2`} 2^{3j+1}
let sum = (0..(num_windows - 1)).fold(C::ScalarExt::zero(), |acc, j| {
acc + C::ScalarExt::from_u64(2).pow(&[
FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1,
0,
0,
0,
])
});
window_table.push(
(0..H)
.map(|k| {
// scalar = k * (2^3)^w - sum, where w = `num_windows - 1`
let scalar = C::ScalarExt::from_u64(k as u64)
* C::ScalarExt::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0])
- sum;
(base * scalar).to_affine()
})
.collect::<ArrayVec<C, H>>()
.into_inner()
.unwrap(),
);
window_table
}
#[cfg(test)]
// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate
// for each fixed-base multiple in each window.
fn test_lagrange_coeffs<C: CurveAffine>(base: C, num_windows: usize) {
let lagrange_coeffs = compute_lagrange_coeffs(base, num_windows);
// Check first 84 windows, i.e. `k_0, k_1, ..., k_83`
for (idx, coeffs) in lagrange_coeffs[0..(num_windows - 1)].iter().enumerate() {
// Test each three-bit chunk in this window.
for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) {
{
// Interpolate the x-coordinate using this window's coefficients
let interpolated_x = super::util::evaluate::<C>(bits, coeffs);
// Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B.
let point = base
* C::Scalar::from_u64(bits as u64 + 2)
* C::Scalar::from_u64(H as u64).pow(&[idx as u64, 0, 0, 0]);
let x = *point.to_affine().coordinates().unwrap().x();
// Check that the interpolated x-coordinate matches the actual one.
assert_eq!(x, interpolated_x);
}
}
}
// Check last window.
for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) {
// Interpolate the x-coordinate using the last window's coefficients
let interpolated_x = super::util::evaluate::<C>(bits, &lagrange_coeffs[num_windows - 1]);
// Compute the actual x-coordinate of the multiple [k * (8^84) - offset]B,
// where offset = \sum_{j = 0}^{83} 2^{3j+1}
let offset = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, w| {
acc + C::Scalar::from_u64(2).pow(&[
FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1,
0,
0,
0,
])
});
let scalar = C::Scalar::from_u64(bits as u64)
* C::Scalar::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0])
- offset;
let point = base * scalar;
let x = *point.to_affine().coordinates().unwrap().x();
// Check that the interpolated x-coordinate matches the actual one.
assert_eq!(x, interpolated_x);
}
}
#[cfg(test)]
// Test that the z-values and u-values satisfy the conditions:
// 1. z + y = u^2,
// 2. z - y is not a square
// for the y-coordinate of each fixed-base multiple in each window.
fn test_zs_and_us<C: CurveAffine>(base: C, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) {
let window_table = compute_window_table(base, num_windows);
for ((u, z), window_points) in u.iter().zip(z.iter()).zip(window_table) {
for (u, point) in u.iter().zip(window_points.iter()) {
let y = *point.coordinates().unwrap().y();
let u = C::Base::from_bytes(u).unwrap();
assert_eq!(C::Base::from_u64(*z) + y, u * u); // allow either square root
assert!(bool::from((C::Base::from_u64(*z) - y).sqrt().is_none()));
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,818 @@
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
/// The value commitment is used to check balance between inputs and outputs. The value is
/// placed over this generator.
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
[
103, 67, 249, 58, 110, 189, 167, 42, 140, 124, 90, 43, 127, 163, 4, 254, 50, 178, 155, 79,
112, 106, 168, 247, 66, 15, 61, 142, 122, 89, 112, 47,
],
[
142, 242, 90, 175, 126, 196, 19, 164, 219, 227, 255, 167, 102, 167, 158, 29, 66, 108, 109,
19, 99, 127, 145, 30, 175, 25, 25, 49, 105, 81, 14, 45,
],
);
/// Short signed z-values for GENERATOR
pub const Z_SHORT: [u64; super::NUM_WINDOWS_SHORT] = [
163547, 76040, 88852, 128479, 54088, 89871, 39598, 144309, 43471, 102492, 741, 55288, 33756,
77312, 12095, 48253, 45718, 202901, 33132, 71081, 152108, 169712,
];
/// Short signed u-values for GENERATOR
pub const U_SHORT: [[[u8; 32]; super::H]; super::NUM_WINDOWS_SHORT] = [
[
[
16, 88, 158, 52, 92, 165, 59, 177, 224, 70, 108, 93, 144, 51, 8, 133, 114, 192, 151,
40, 85, 33, 52, 118, 147, 163, 220, 193, 171, 43, 73, 27,
],
[
201, 84, 190, 165, 219, 228, 63, 155, 182, 74, 179, 74, 230, 96, 89, 168, 110, 162, 83,
218, 185, 137, 194, 92, 244, 85, 1, 60, 59, 16, 119, 14,
],
[
113, 146, 25, 32, 206, 37, 173, 225, 58, 41, 184, 214, 177, 123, 211, 55, 42, 208, 214,
45, 149, 99, 31, 171, 172, 226, 219, 171, 246, 85, 116, 59,
],
[
10, 79, 89, 241, 37, 161, 115, 213, 183, 43, 68, 233, 219, 249, 131, 137, 41, 119, 153,
24, 92, 87, 138, 167, 93, 46, 32, 97, 102, 164, 164, 53,
],
[
50, 252, 2, 254, 234, 133, 190, 34, 244, 192, 31, 96, 156, 152, 150, 88, 154, 224, 223,
144, 223, 113, 203, 226, 214, 44, 69, 198, 171, 60, 26, 45,
],
[
151, 65, 173, 0, 249, 13, 78, 201, 84, 58, 255, 120, 124, 169, 98, 242, 239, 132, 149,
204, 146, 244, 55, 176, 56, 26, 19, 148, 125, 42, 26, 32,
],
[
6, 29, 196, 176, 135, 157, 248, 239, 196, 9, 100, 82, 220, 41, 203, 49, 35, 108, 76,
59, 222, 47, 82, 39, 35, 23, 88, 136, 61, 90, 23, 57,
],
[
169, 203, 101, 116, 220, 68, 34, 248, 199, 107, 202, 59, 111, 171, 90, 121, 119, 80,
146, 214, 191, 197, 220, 212, 125, 139, 12, 140, 198, 191, 110, 31,
],
],
[
[
16, 171, 139, 236, 248, 28, 27, 145, 109, 161, 63, 121, 19, 190, 106, 193, 13, 122,
108, 54, 95, 202, 195, 184, 118, 60, 72, 213, 154, 240, 52, 34,
],
[
131, 218, 16, 142, 97, 153, 190, 249, 118, 171, 187, 254, 21, 17, 202, 48, 107, 23,
103, 72, 159, 238, 228, 38, 183, 166, 74, 136, 77, 51, 85, 18,
],
[
137, 78, 204, 243, 154, 87, 148, 200, 11, 98, 58, 33, 89, 170, 198, 87, 29, 187, 167,
122, 157, 151, 5, 148, 254, 216, 113, 12, 158, 243, 102, 63,
],
[
16, 187, 216, 98, 244, 19, 70, 209, 217, 133, 240, 226, 111, 219, 247, 74, 4, 231, 183,
216, 35, 174, 192, 96, 129, 159, 39, 96, 200, 174, 126, 1,
],
[
185, 2, 174, 129, 110, 163, 212, 152, 214, 139, 105, 150, 23, 209, 185, 245, 71, 8,
171, 142, 43, 57, 167, 172, 233, 77, 194, 166, 118, 232, 127, 35,
],
[
57, 28, 236, 33, 234, 0, 214, 131, 161, 209, 42, 25, 194, 150, 82, 126, 216, 23, 81,
85, 90, 47, 95, 46, 119, 108, 253, 12, 92, 29, 13, 25,
],
[
100, 162, 156, 175, 254, 203, 69, 171, 182, 190, 85, 161, 94, 117, 225, 0, 95, 121,
190, 235, 79, 7, 147, 134, 48, 156, 113, 55, 114, 229, 174, 25,
],
[
243, 93, 223, 98, 132, 0, 171, 125, 135, 53, 111, 49, 49, 173, 69, 211, 62, 106, 168,
221, 115, 208, 2, 20, 14, 150, 124, 130, 189, 48, 16, 15,
],
],
[
[
43, 238, 4, 240, 186, 135, 47, 128, 185, 51, 70, 196, 3, 137, 22, 254, 29, 247, 161,
233, 187, 255, 239, 183, 150, 179, 8, 64, 90, 33, 17, 54,
],
[
20, 226, 19, 60, 92, 195, 1, 255, 100, 188, 89, 54, 98, 225, 138, 133, 26, 140, 202,
76, 107, 199, 46, 54, 167, 23, 119, 80, 19, 25, 34, 5,
],
[
145, 109, 10, 159, 200, 145, 32, 253, 32, 93, 187, 12, 66, 161, 44, 217, 16, 100, 225,
37, 139, 125, 169, 119, 83, 34, 96, 130, 234, 255, 138, 38,
],
[
148, 127, 71, 227, 195, 244, 12, 49, 83, 78, 218, 9, 176, 158, 12, 5, 194, 54, 44, 157,
252, 228, 175, 170, 54, 91, 49, 254, 83, 228, 180, 61,
],
[
36, 96, 238, 61, 227, 144, 153, 81, 121, 197, 74, 190, 35, 216, 255, 92, 70, 7, 168,
219, 130, 255, 172, 71, 200, 31, 142, 232, 255, 117, 96, 15,
],
[
50, 152, 255, 238, 188, 127, 140, 240, 203, 33, 246, 193, 228, 179, 25, 1, 227, 194,
79, 70, 41, 160, 83, 243, 148, 1, 95, 86, 63, 22, 55, 40,
],
[
99, 228, 147, 46, 232, 157, 225, 210, 45, 159, 169, 42, 184, 90, 3, 165, 62, 4, 93,
181, 74, 187, 112, 156, 26, 103, 199, 84, 132, 148, 14, 24,
],
[
120, 163, 182, 125, 197, 141, 122, 21, 166, 103, 203, 57, 160, 228, 242, 192, 203, 40,
233, 179, 8, 173, 199, 21, 213, 215, 222, 129, 23, 153, 25, 29,
],
],
[
[
189, 6, 27, 46, 68, 6, 124, 69, 159, 212, 18, 104, 218, 16, 66, 183, 250, 92, 69, 5,
218, 234, 31, 198, 123, 100, 216, 103, 106, 113, 192, 43,
],
[
226, 152, 83, 159, 143, 155, 164, 90, 171, 100, 22, 240, 187, 26, 55, 172, 170, 133,
128, 192, 29, 33, 35, 8, 59, 142, 20, 204, 151, 172, 29, 28,
],
[
196, 57, 232, 112, 46, 200, 26, 93, 224, 191, 117, 154, 9, 47, 228, 145, 122, 137, 238,
236, 70, 14, 112, 163, 96, 140, 239, 122, 75, 200, 206, 12,
],
[
91, 66, 65, 159, 86, 50, 113, 39, 30, 12, 213, 235, 15, 70, 163, 119, 112, 27, 22, 170,
151, 20, 71, 172, 65, 251, 224, 76, 26, 189, 118, 42,
],
[
195, 200, 87, 207, 125, 122, 254, 50, 24, 40, 189, 169, 237, 228, 134, 66, 37, 220, 23,
98, 202, 193, 152, 184, 171, 63, 105, 11, 70, 94, 154, 58,
],
[
4, 0, 82, 125, 41, 130, 11, 238, 112, 201, 95, 30, 144, 31, 31, 233, 86, 121, 145, 200,
204, 89, 182, 51, 151, 169, 58, 206, 184, 219, 181, 46,
],
[
36, 240, 60, 205, 120, 239, 9, 98, 61, 228, 247, 9, 145, 71, 67, 10, 164, 160, 186,
104, 25, 225, 210, 37, 31, 42, 58, 208, 44, 19, 30, 29,
],
[
163, 214, 140, 64, 48, 239, 54, 175, 108, 32, 187, 248, 142, 77, 244, 117, 10, 236, 39,
80, 158, 44, 98, 44, 248, 24, 208, 60, 173, 247, 115, 28,
],
],
[
[
230, 166, 7, 118, 178, 55, 26, 250, 91, 159, 230, 158, 34, 135, 91, 49, 166, 244, 124,
175, 86, 228, 159, 119, 231, 15, 19, 199, 219, 85, 233, 19,
],
[
65, 240, 77, 9, 197, 255, 96, 35, 239, 161, 177, 115, 83, 114, 180, 179, 118, 158, 220,
61, 177, 3, 175, 70, 132, 51, 87, 79, 36, 104, 129, 15,
],
[
245, 9, 141, 218, 228, 57, 204, 250, 83, 156, 160, 158, 146, 157, 206, 198, 16, 94, 80,
59, 31, 242, 163, 30, 80, 99, 32, 142, 193, 101, 172, 22,
],
[
22, 68, 73, 27, 236, 149, 225, 78, 69, 83, 174, 202, 98, 113, 148, 167, 43, 132, 118,
49, 153, 96, 186, 134, 20, 103, 123, 48, 75, 104, 207, 6,
],
[
186, 5, 66, 183, 166, 255, 222, 14, 34, 0, 105, 167, 36, 11, 210, 228, 91, 103, 65,
248, 3, 48, 117, 240, 180, 214, 201, 222, 5, 202, 103, 42,
],
[
231, 31, 19, 122, 240, 72, 34, 93, 204, 125, 90, 215, 124, 174, 124, 28, 145, 103, 126,
178, 1, 9, 152, 240, 251, 118, 14, 195, 197, 167, 136, 22,
],
[
121, 57, 39, 104, 26, 135, 98, 221, 85, 199, 94, 230, 223, 129, 28, 191, 185, 0, 46,
200, 72, 116, 202, 255, 80, 180, 13, 98, 229, 217, 238, 17,
],
[
228, 158, 59, 241, 50, 224, 177, 78, 8, 121, 211, 157, 95, 196, 88, 59, 85, 141, 134,
50, 123, 168, 175, 115, 240, 153, 223, 61, 71, 229, 77, 10,
],
],
[
[
196, 151, 212, 168, 230, 19, 214, 179, 118, 17, 196, 50, 76, 231, 114, 80, 178, 137,
232, 241, 60, 2, 0, 124, 25, 239, 98, 131, 220, 159, 221, 51,
],
[
207, 216, 50, 53, 106, 200, 85, 166, 137, 33, 29, 239, 97, 46, 104, 218, 177, 150, 178,
64, 232, 238, 208, 98, 103, 58, 233, 97, 1, 243, 73, 5,
],
[
174, 215, 22, 124, 193, 136, 79, 91, 111, 146, 51, 128, 249, 129, 203, 168, 157, 164,
252, 144, 206, 10, 63, 253, 250, 113, 251, 65, 218, 23, 12, 25,
],
[
186, 92, 109, 134, 176, 198, 214, 156, 148, 7, 106, 121, 226, 70, 250, 192, 180, 164,
16, 29, 245, 196, 155, 183, 214, 26, 3, 26, 249, 134, 153, 38,
],
[
244, 56, 170, 227, 236, 145, 153, 150, 166, 77, 201, 48, 100, 130, 204, 194, 108, 207,
3, 243, 227, 41, 45, 8, 136, 106, 58, 70, 126, 3, 132, 59,
],
[
215, 169, 24, 118, 5, 192, 171, 217, 47, 86, 212, 249, 228, 92, 248, 103, 242, 222, 17,
206, 36, 157, 126, 224, 105, 113, 239, 102, 54, 25, 151, 14,
],
[
129, 213, 202, 163, 74, 201, 249, 43, 200, 62, 4, 232, 68, 211, 44, 153, 25, 71, 155,
5, 93, 19, 117, 195, 201, 179, 158, 207, 252, 213, 166, 62,
],
[
86, 75, 236, 222, 217, 118, 49, 0, 141, 130, 201, 248, 34, 225, 69, 91, 186, 90, 246,
36, 141, 152, 166, 216, 118, 7, 38, 133, 147, 190, 79, 3,
],
],
[
[
226, 55, 121, 73, 104, 77, 129, 202, 168, 123, 192, 163, 28, 9, 195, 37, 116, 207, 125,
198, 203, 244, 121, 236, 232, 91, 168, 14, 142, 241, 250, 60,
],
[
222, 86, 183, 37, 132, 107, 210, 125, 127, 46, 94, 81, 18, 91, 72, 160, 16, 193, 239,
114, 238, 186, 186, 203, 96, 169, 87, 155, 108, 36, 97, 6,
],
[
110, 110, 99, 98, 167, 78, 75, 128, 33, 138, 18, 19, 194, 192, 219, 184, 74, 196, 82,
115, 241, 102, 30, 197, 199, 194, 154, 120, 49, 10, 95, 37,
],
[
212, 206, 154, 98, 20, 33, 185, 182, 138, 207, 65, 197, 246, 19, 132, 52, 173, 186, 42,
243, 88, 20, 51, 11, 206, 25, 216, 48, 162, 138, 124, 13,
],
[
32, 161, 64, 72, 1, 32, 243, 175, 251, 37, 86, 248, 136, 187, 181, 55, 39, 255, 98,
228, 189, 235, 194, 2, 228, 39, 92, 104, 245, 17, 117, 7,
],
[
207, 205, 116, 251, 54, 21, 8, 82, 173, 45, 205, 38, 245, 155, 16, 56, 198, 232, 173,
88, 97, 22, 234, 26, 139, 206, 108, 254, 123, 87, 181, 26,
],
[
38, 147, 223, 169, 68, 76, 49, 169, 137, 141, 72, 63, 166, 88, 34, 220, 163, 91, 167,
251, 29, 160, 254, 199, 205, 74, 158, 105, 252, 182, 158, 21,
],
[
221, 44, 183, 72, 226, 191, 226, 165, 162, 153, 186, 190, 97, 53, 19, 115, 215, 71,
155, 33, 79, 120, 197, 228, 216, 212, 249, 15, 179, 11, 216, 32,
],
],
[
[
248, 136, 25, 30, 234, 18, 242, 209, 97, 211, 74, 228, 236, 199, 101, 200, 206, 52,
146, 207, 72, 125, 28, 3, 60, 86, 34, 195, 250, 251, 204, 0,
],
[
204, 182, 197, 171, 247, 159, 161, 27, 18, 146, 249, 99, 198, 138, 25, 61, 119, 232,
160, 152, 18, 149, 7, 67, 125, 231, 237, 3, 68, 190, 137, 0,
],
[
141, 245, 108, 181, 49, 171, 106, 247, 202, 169, 106, 39, 93, 40, 122, 2, 236, 255,
198, 215, 122, 254, 242, 192, 49, 250, 243, 35, 7, 219, 21, 22,
],
[
239, 85, 174, 15, 207, 84, 128, 92, 87, 80, 129, 20, 21, 225, 233, 158, 193, 136, 141,
114, 66, 146, 29, 193, 223, 250, 27, 56, 195, 15, 135, 17,
],
[
231, 242, 76, 43, 57, 10, 41, 166, 32, 254, 129, 47, 147, 118, 189, 200, 44, 102, 204,
116, 96, 82, 186, 150, 106, 27, 30, 73, 237, 94, 36, 44,
],
[
240, 139, 69, 197, 199, 228, 206, 96, 255, 229, 189, 207, 65, 97, 93, 211, 161, 190,
228, 249, 50, 82, 223, 251, 13, 173, 241, 221, 78, 243, 105, 19,
],
[
44, 224, 170, 161, 50, 93, 212, 80, 100, 243, 51, 74, 51, 165, 60, 208, 244, 18, 158,
30, 158, 81, 111, 213, 136, 95, 125, 173, 143, 108, 106, 4,
],
[
134, 244, 131, 92, 152, 118, 30, 139, 153, 128, 62, 115, 88, 25, 58, 29, 205, 101, 47,
208, 93, 89, 222, 17, 122, 112, 71, 56, 147, 68, 92, 22,
],
],
[
[
59, 157, 112, 130, 217, 2, 102, 228, 79, 211, 152, 82, 183, 186, 47, 151, 125, 13, 97,
121, 115, 253, 17, 121, 227, 250, 99, 14, 84, 249, 18, 30,
],
[
80, 180, 155, 59, 6, 182, 136, 39, 134, 168, 238, 138, 129, 174, 195, 206, 210, 167,
214, 167, 35, 139, 130, 27, 21, 59, 7, 200, 165, 37, 91, 29,
],
[
220, 228, 189, 172, 68, 102, 135, 236, 7, 70, 152, 244, 120, 217, 67, 44, 43, 74, 155,
179, 2, 148, 106, 238, 232, 186, 181, 130, 141, 114, 60, 1,
],
[
68, 132, 80, 55, 28, 52, 222, 165, 156, 6, 214, 236, 207, 37, 223, 118, 42, 55, 40,
123, 208, 181, 240, 56, 14, 142, 58, 72, 193, 71, 120, 58,
],
[
93, 114, 68, 232, 179, 37, 202, 74, 41, 64, 245, 112, 233, 162, 231, 19, 223, 207, 232,
213, 178, 60, 106, 26, 35, 191, 108, 19, 243, 220, 40, 41,
],
[
166, 223, 96, 196, 120, 210, 67, 47, 249, 123, 164, 213, 148, 138, 7, 155, 96, 222,
176, 166, 88, 85, 95, 71, 221, 237, 138, 181, 198, 165, 163, 0,
],
[
241, 254, 24, 83, 47, 65, 146, 151, 5, 182, 233, 205, 182, 13, 75, 173, 10, 14, 48,
223, 227, 201, 141, 212, 114, 205, 196, 92, 137, 253, 127, 60,
],
[
20, 41, 204, 77, 168, 230, 68, 202, 73, 251, 254, 88, 95, 80, 130, 216, 122, 75, 173,
105, 236, 192, 177, 209, 26, 66, 205, 127, 154, 188, 245, 17,
],
],
[
[
239, 194, 214, 218, 225, 244, 0, 110, 12, 75, 130, 236, 76, 102, 205, 64, 104, 144,
198, 188, 183, 46, 119, 96, 230, 68, 210, 161, 253, 91, 8, 20,
],
[
84, 32, 226, 77, 213, 16, 207, 156, 234, 224, 147, 173, 186, 249, 186, 155, 90, 255,
34, 55, 48, 108, 76, 214, 254, 66, 95, 200, 174, 191, 52, 43,
],
[
37, 103, 206, 174, 250, 172, 136, 87, 30, 68, 89, 230, 110, 190, 148, 71, 5, 249, 217,
112, 54, 182, 127, 54, 173, 89, 6, 63, 230, 69, 32, 35,
],
[
160, 186, 242, 212, 179, 197, 16, 239, 56, 24, 91, 241, 68, 7, 138, 200, 93, 194, 45,
155, 210, 60, 30, 4, 167, 246, 82, 244, 71, 217, 31, 20,
],
[
182, 132, 62, 134, 4, 186, 95, 160, 230, 255, 125, 156, 5, 134, 66, 99, 83, 182, 156,
207, 98, 84, 197, 48, 160, 47, 126, 2, 253, 64, 69, 25,
],
[
135, 241, 60, 121, 32, 218, 195, 61, 68, 66, 190, 195, 208, 2, 201, 111, 158, 101, 108,
228, 145, 141, 82, 80, 36, 16, 157, 212, 65, 213, 188, 61,
],
[
190, 186, 202, 30, 121, 177, 200, 82, 245, 162, 14, 253, 114, 50, 43, 134, 246, 12,
100, 222, 149, 242, 117, 174, 136, 192, 117, 132, 228, 144, 238, 39,
],
[
160, 120, 19, 13, 34, 38, 71, 236, 116, 162, 150, 254, 247, 252, 222, 198, 196, 59, 98,
165, 54, 33, 22, 120, 58, 73, 225, 42, 37, 211, 88, 21,
],
],
[
[
252, 1, 229, 131, 50, 189, 111, 31, 191, 210, 177, 219, 234, 21, 100, 182, 115, 212,
154, 111, 130, 59, 237, 32, 142, 202, 110, 96, 166, 120, 188, 1,
],
[
247, 244, 137, 120, 38, 62, 94, 38, 17, 38, 102, 240, 225, 129, 15, 214, 213, 142, 79,
176, 156, 118, 85, 80, 167, 47, 122, 152, 206, 19, 67, 40,
],
[
27, 159, 102, 201, 17, 4, 75, 28, 159, 5, 194, 6, 63, 104, 157, 219, 53, 38, 84, 216,
73, 181, 11, 118, 29, 177, 147, 135, 150, 5, 58, 10,
],
[
97, 168, 102, 245, 40, 187, 155, 99, 147, 65, 114, 119, 191, 225, 196, 34, 117, 134,
116, 162, 73, 69, 158, 103, 144, 16, 22, 216, 146, 38, 10, 41,
],
[
149, 231, 10, 10, 17, 16, 88, 231, 24, 215, 115, 237, 123, 68, 9, 209, 24, 141, 150,
207, 109, 56, 107, 192, 252, 112, 156, 0, 65, 234, 86, 10,
],
[
201, 24, 6, 113, 122, 123, 58, 3, 233, 141, 78, 228, 137, 112, 71, 121, 200, 171, 158,
233, 87, 171, 121, 118, 205, 98, 38, 24, 176, 153, 170, 25,
],
[
6, 114, 137, 241, 204, 203, 173, 160, 14, 124, 220, 164, 166, 224, 0, 253, 255, 68, 40,
182, 248, 135, 226, 25, 213, 247, 45, 116, 94, 147, 107, 3,
],
[
73, 103, 138, 222, 168, 203, 85, 216, 242, 63, 127, 158, 153, 60, 168, 180, 234, 71,
27, 10, 38, 161, 207, 26, 81, 150, 195, 37, 91, 228, 57, 46,
],
],
[
[
188, 220, 107, 162, 250, 116, 137, 134, 75, 73, 102, 28, 11, 158, 166, 162, 77, 99,
159, 21, 166, 195, 208, 99, 28, 0, 51, 64, 126, 222, 203, 28,
],
[
115, 93, 10, 209, 3, 81, 82, 191, 158, 74, 26, 242, 145, 24, 85, 106, 28, 36, 54, 17,
216, 109, 58, 102, 221, 11, 10, 157, 226, 90, 53, 3,
],
[
197, 172, 174, 245, 150, 142, 92, 221, 45, 118, 174, 8, 83, 195, 45, 83, 221, 212, 122,
239, 218, 103, 89, 56, 184, 102, 73, 70, 1, 40, 246, 54,
],
[
131, 77, 239, 236, 59, 58, 35, 163, 25, 57, 251, 93, 224, 202, 225, 84, 189, 195, 1,
234, 156, 138, 3, 2, 102, 170, 173, 235, 97, 41, 224, 0,
],
[
251, 165, 141, 221, 2, 154, 174, 224, 120, 187, 163, 188, 37, 146, 49, 193, 150, 241,
183, 33, 12, 228, 96, 92, 105, 198, 238, 59, 247, 172, 247, 54,
],
[
31, 84, 10, 130, 68, 107, 203, 153, 201, 34, 69, 151, 1, 180, 37, 198, 113, 64, 82,
116, 116, 142, 251, 62, 22, 122, 138, 130, 200, 159, 145, 2,
],
[
229, 126, 102, 192, 242, 5, 109, 247, 248, 70, 34, 78, 35, 23, 81, 67, 34, 226, 133,
119, 200, 242, 142, 111, 223, 102, 159, 61, 162, 226, 222, 11,
],
[
171, 0, 253, 102, 188, 223, 208, 250, 186, 183, 127, 172, 10, 41, 201, 173, 242, 156,
106, 219, 236, 139, 76, 115, 200, 123, 176, 228, 181, 248, 121, 38,
],
],
[
[
187, 71, 125, 130, 250, 45, 125, 44, 56, 31, 103, 55, 71, 87, 166, 228, 184, 12, 252,
79, 26, 221, 65, 188, 62, 254, 222, 87, 189, 71, 43, 0,
],
[
248, 127, 55, 175, 11, 237, 134, 201, 211, 212, 93, 115, 63, 118, 15, 121, 71, 55, 176,
74, 3, 75, 20, 100, 177, 194, 39, 92, 67, 109, 243, 38,
],
[
147, 188, 248, 11, 127, 3, 176, 153, 109, 5, 65, 101, 2, 46, 70, 203, 246, 245, 254,
67, 193, 214, 156, 21, 116, 165, 60, 79, 219, 45, 180, 47,
],
[
78, 126, 47, 15, 17, 83, 240, 144, 40, 174, 95, 250, 144, 43, 132, 67, 241, 189, 140,
244, 41, 221, 164, 186, 104, 156, 223, 233, 160, 99, 190, 39,
],
[
29, 119, 16, 42, 190, 69, 200, 191, 3, 160, 164, 28, 189, 135, 85, 63, 59, 121, 213,
143, 9, 96, 150, 14, 21, 93, 132, 57, 4, 165, 174, 12,
],
[
54, 200, 34, 46, 89, 210, 152, 121, 245, 147, 150, 48, 193, 246, 108, 154, 243, 12, 10,
10, 97, 83, 225, 116, 187, 177, 176, 80, 248, 185, 5, 38,
],
[
245, 84, 103, 49, 77, 27, 84, 143, 30, 40, 54, 249, 178, 71, 191, 135, 199, 72, 204,
162, 75, 110, 203, 246, 193, 61, 70, 158, 74, 154, 13, 45,
],
[
123, 98, 28, 217, 129, 160, 71, 205, 19, 41, 168, 124, 76, 145, 108, 71, 57, 60, 26,
154, 163, 64, 250, 13, 52, 179, 197, 193, 54, 184, 29, 32,
],
],
[
[
103, 140, 102, 88, 162, 193, 224, 59, 243, 31, 145, 100, 116, 71, 36, 129, 94, 248, 33,
0, 102, 46, 146, 206, 22, 255, 216, 58, 61, 118, 226, 47,
],
[
21, 127, 228, 231, 155, 190, 28, 145, 48, 160, 35, 104, 47, 120, 243, 107, 145, 118,
199, 126, 138, 164, 246, 143, 153, 59, 153, 209, 81, 118, 167, 9,
],
[
4, 84, 44, 30, 90, 253, 226, 166, 218, 12, 39, 214, 231, 241, 223, 87, 87, 82, 93, 220,
65, 132, 166, 75, 221, 33, 236, 113, 198, 43, 210, 39,
],
[
243, 54, 41, 143, 244, 171, 75, 158, 218, 230, 55, 35, 236, 18, 40, 55, 157, 139, 180,
29, 58, 159, 88, 208, 214, 87, 168, 227, 93, 211, 194, 17,
],
[
97, 131, 219, 190, 19, 178, 244, 173, 141, 143, 113, 3, 27, 63, 35, 185, 170, 43, 75,
64, 75, 38, 5, 13, 123, 39, 147, 243, 141, 122, 217, 39,
],
[
3, 24, 126, 200, 122, 92, 125, 221, 95, 205, 139, 145, 231, 77, 223, 96, 84, 39, 33,
66, 139, 41, 82, 182, 22, 102, 95, 173, 66, 125, 77, 21,
],
[
27, 50, 52, 183, 190, 198, 236, 248, 71, 251, 120, 132, 192, 227, 113, 36, 155, 81,
225, 48, 72, 17, 246, 99, 208, 242, 236, 93, 2, 19, 53, 31,
],
[
99, 18, 31, 165, 229, 52, 216, 52, 162, 62, 66, 1, 190, 22, 69, 133, 11, 126, 106, 165,
131, 180, 218, 253, 238, 124, 3, 16, 42, 196, 148, 57,
],
],
[
[
0, 209, 105, 72, 69, 130, 81, 154, 136, 174, 169, 182, 42, 150, 112, 115, 234, 136, 47,
170, 158, 213, 211, 65, 178, 62, 18, 172, 135, 59, 253, 19,
],
[
145, 192, 219, 168, 214, 190, 54, 248, 68, 248, 196, 148, 4, 254, 61, 193, 67, 218,
131, 110, 235, 60, 159, 101, 200, 218, 208, 195, 30, 249, 163, 32,
],
[
21, 246, 3, 74, 137, 246, 202, 207, 71, 59, 198, 73, 117, 224, 124, 57, 2, 82, 110, 6,
190, 80, 143, 143, 113, 62, 127, 122, 164, 202, 6, 54,
],
[
252, 245, 11, 63, 63, 70, 60, 82, 15, 154, 188, 35, 211, 222, 252, 180, 109, 109, 98,
69, 197, 240, 137, 46, 189, 8, 167, 87, 15, 179, 18, 12,
],
[
125, 206, 204, 128, 43, 62, 39, 36, 246, 164, 44, 6, 250, 83, 14, 207, 53, 201, 166,
231, 175, 110, 140, 200, 48, 239, 20, 171, 46, 80, 115, 54,
],
[
167, 7, 74, 225, 61, 229, 21, 154, 196, 11, 247, 27, 158, 112, 217, 238, 57, 53, 63,
251, 162, 91, 168, 86, 37, 203, 207, 119, 68, 135, 205, 9,
],
[
84, 187, 71, 200, 46, 254, 136, 13, 25, 137, 121, 128, 232, 221, 40, 0, 175, 232, 153,
227, 181, 162, 29, 67, 225, 234, 249, 102, 82, 171, 226, 1,
],
[
24, 185, 170, 6, 35, 57, 108, 85, 245, 134, 216, 239, 33, 12, 223, 38, 227, 73, 145,
100, 25, 14, 244, 177, 84, 38, 101, 67, 21, 96, 249, 61,
],
],
[
[
57, 9, 82, 174, 160, 195, 27, 106, 241, 225, 207, 16, 11, 131, 29, 63, 187, 187, 5, 76,
34, 39, 136, 124, 56, 25, 58, 99, 70, 116, 170, 19,
],
[
143, 6, 32, 114, 74, 44, 29, 53, 226, 34, 62, 232, 111, 63, 201, 203, 46, 115, 209,
118, 31, 27, 1, 120, 254, 70, 252, 80, 5, 111, 123, 55,
],
[
62, 18, 214, 41, 0, 12, 4, 12, 145, 201, 12, 6, 179, 4, 20, 84, 36, 155, 8, 99, 181,
18, 150, 144, 203, 228, 172, 135, 166, 152, 214, 8,
],
[
49, 93, 249, 139, 121, 113, 205, 158, 145, 118, 40, 96, 206, 154, 71, 190, 146, 65,
233, 104, 83, 91, 25, 118, 176, 14, 149, 115, 137, 27, 223, 41,
],
[
116, 160, 29, 244, 254, 193, 228, 122, 194, 168, 126, 1, 222, 247, 90, 191, 253, 101,
123, 197, 178, 127, 30, 113, 38, 73, 48, 240, 82, 52, 161, 12,
],
[
156, 145, 203, 40, 113, 83, 199, 161, 230, 196, 203, 227, 217, 212, 254, 139, 37, 215,
39, 230, 190, 141, 119, 120, 87, 23, 61, 21, 3, 209, 179, 47,
],
[
179, 114, 238, 159, 43, 22, 64, 61, 207, 56, 101, 90, 62, 245, 27, 21, 165, 0, 205, 34,
104, 32, 170, 75, 215, 255, 83, 74, 123, 73, 159, 19,
],
[
14, 208, 162, 223, 209, 5, 175, 15, 1, 78, 222, 82, 21, 113, 25, 129, 103, 64, 139, 21,
226, 245, 199, 114, 252, 69, 133, 254, 128, 63, 61, 13,
],
],
[
[
255, 187, 20, 3, 51, 61, 230, 80, 83, 233, 71, 190, 94, 131, 225, 143, 139, 246, 196,
161, 165, 85, 92, 167, 71, 198, 83, 10, 164, 120, 89, 26,
],
[
250, 108, 167, 151, 249, 92, 38, 36, 21, 96, 210, 31, 41, 91, 113, 183, 104, 192, 3,
45, 165, 253, 37, 75, 239, 245, 28, 148, 5, 255, 134, 60,
],
[
59, 154, 220, 255, 37, 98, 169, 60, 50, 196, 202, 240, 225, 57, 165, 129, 255, 66, 169,
162, 7, 30, 198, 27, 160, 208, 193, 106, 29, 119, 104, 48,
],
[
137, 180, 21, 151, 27, 173, 213, 11, 238, 163, 104, 192, 171, 59, 79, 249, 123, 55,
183, 8, 94, 117, 32, 48, 41, 141, 231, 207, 61, 135, 104, 2,
],
[
242, 254, 15, 0, 58, 49, 204, 28, 27, 56, 2, 67, 248, 104, 160, 32, 214, 242, 10, 206,
233, 61, 23, 103, 180, 53, 179, 198, 56, 254, 65, 6,
],
[
136, 214, 253, 248, 156, 140, 42, 172, 221, 187, 160, 233, 86, 213, 239, 5, 110, 252,
70, 18, 193, 29, 156, 156, 136, 70, 167, 59, 98, 223, 7, 30,
],
[
84, 25, 227, 152, 61, 51, 53, 59, 135, 229, 159, 248, 6, 39, 151, 139, 121, 149, 226,
142, 126, 136, 248, 196, 93, 176, 131, 254, 221, 204, 179, 36,
],
[
198, 74, 99, 58, 59, 34, 82, 94, 95, 64, 17, 241, 173, 114, 211, 57, 124, 181, 140,
102, 105, 79, 13, 1, 60, 121, 143, 88, 192, 253, 159, 47,
],
],
[
[
90, 115, 165, 218, 163, 197, 210, 143, 213, 125, 1, 77, 74, 165, 200, 244, 80, 39, 20,
247, 86, 120, 109, 109, 93, 7, 209, 199, 109, 12, 144, 46,
],
[
231, 44, 48, 128, 109, 202, 114, 192, 218, 67, 233, 141, 64, 251, 104, 41, 58, 212, 60,
65, 93, 58, 34, 149, 128, 90, 30, 197, 191, 244, 8, 37,
],
[
56, 47, 18, 80, 195, 143, 175, 35, 183, 225, 201, 236, 138, 29, 26, 229, 194, 202, 13,
43, 71, 188, 3, 204, 12, 15, 218, 207, 15, 83, 219, 39,
],
[
50, 71, 182, 171, 33, 129, 211, 168, 40, 85, 193, 218, 165, 54, 220, 203, 164, 124, 8,
37, 19, 210, 8, 253, 120, 158, 239, 239, 28, 195, 253, 37,
],
[
245, 191, 155, 103, 118, 221, 209, 204, 89, 48, 249, 160, 180, 1, 114, 3, 254, 220, 94,
244, 221, 122, 224, 55, 184, 106, 99, 11, 236, 89, 211, 38,
],
[
182, 208, 168, 152, 15, 192, 45, 31, 93, 181, 13, 203, 128, 82, 126, 145, 129, 220, 19,
252, 188, 247, 49, 216, 218, 198, 178, 70, 180, 209, 175, 22,
],
[
72, 71, 200, 22, 21, 120, 50, 111, 112, 195, 141, 79, 49, 52, 98, 8, 37, 130, 142, 13,
78, 197, 15, 92, 203, 50, 108, 82, 109, 254, 158, 12,
],
[
71, 44, 114, 76, 152, 26, 79, 25, 44, 244, 191, 178, 150, 102, 34, 230, 54, 251, 209,
155, 90, 28, 81, 49, 127, 246, 116, 238, 106, 105, 196, 29,
],
],
[
[
208, 87, 78, 186, 184, 128, 38, 190, 131, 156, 221, 119, 87, 12, 144, 4, 240, 77, 118,
209, 74, 131, 37, 155, 247, 155, 206, 167, 80, 71, 127, 18,
],
[
190, 54, 229, 228, 15, 167, 185, 240, 161, 238, 216, 88, 210, 31, 242, 20, 81, 147, 48,
54, 38, 226, 251, 64, 69, 196, 67, 166, 242, 34, 118, 39,
],
[
58, 171, 187, 174, 13, 247, 253, 15, 102, 171, 48, 63, 136, 157, 55, 28, 117, 130, 104,
23, 145, 203, 155, 105, 121, 249, 115, 106, 88, 114, 86, 11,
],
[
105, 30, 100, 75, 20, 206, 29, 147, 150, 37, 48, 216, 33, 147, 61, 193, 82, 230, 205,
122, 142, 65, 148, 102, 47, 185, 182, 147, 185, 31, 29, 54,
],
[
158, 245, 169, 236, 26, 185, 17, 174, 156, 69, 81, 196, 60, 109, 99, 91, 19, 208, 93,
58, 9, 109, 228, 186, 109, 127, 171, 156, 229, 215, 195, 59,
],
[
23, 42, 4, 183, 91, 177, 2, 172, 168, 182, 158, 185, 157, 118, 199, 184, 237, 203, 60,
170, 35, 121, 162, 7, 130, 171, 121, 207, 32, 2, 227, 62,
],
[
0, 139, 174, 217, 13, 116, 28, 230, 238, 117, 190, 91, 86, 105, 38, 231, 147, 100, 233,
187, 70, 128, 111, 82, 184, 113, 154, 136, 59, 27, 21, 10,
],
[
4, 208, 53, 136, 59, 196, 102, 52, 69, 1, 231, 8, 254, 19, 67, 134, 251, 73, 157, 156,
30, 94, 170, 147, 185, 72, 11, 143, 226, 255, 0, 60,
],
],
[
[
214, 131, 68, 196, 131, 169, 22, 250, 29, 101, 142, 26, 106, 96, 18, 190, 18, 15, 19,
59, 203, 203, 119, 251, 61, 221, 198, 116, 24, 178, 61, 42,
],
[
101, 161, 133, 103, 0, 112, 204, 255, 98, 240, 20, 161, 242, 253, 216, 204, 83, 96, 93,
228, 77, 76, 63, 70, 116, 156, 69, 253, 121, 189, 2, 36,
],
[
156, 83, 226, 206, 4, 35, 12, 137, 209, 181, 109, 81, 194, 119, 188, 216, 30, 233, 135,
220, 213, 40, 74, 152, 49, 14, 0, 3, 223, 41, 238, 54,
],
[
47, 25, 110, 4, 111, 57, 200, 91, 168, 73, 47, 175, 189, 60, 49, 243, 128, 11, 63, 17,
151, 123, 80, 140, 139, 202, 93, 104, 190, 32, 67, 54,
],
[
90, 100, 132, 107, 167, 162, 164, 62, 239, 68, 20, 223, 157, 1, 90, 95, 248, 82, 65,
61, 241, 63, 238, 10, 2, 160, 230, 104, 101, 197, 60, 52,
],
[
41, 144, 80, 156, 134, 224, 6, 48, 188, 57, 30, 205, 84, 135, 190, 75, 213, 94, 16, 72,
11, 96, 41, 117, 75, 60, 62, 133, 29, 133, 105, 15,
],
[
219, 66, 247, 117, 3, 137, 38, 43, 131, 177, 137, 150, 9, 65, 160, 206, 235, 121, 121,
245, 205, 233, 229, 78, 72, 200, 171, 149, 240, 64, 184, 5,
],
[
22, 179, 118, 116, 100, 222, 159, 96, 236, 247, 38, 23, 224, 103, 6, 5, 42, 95, 161, 4,
128, 2, 240, 122, 117, 247, 127, 207, 76, 205, 137, 31,
],
],
[
[
255, 68, 73, 184, 204, 219, 231, 9, 237, 101, 142, 55, 146, 252, 138, 14, 186, 62, 32,
108, 79, 130, 251, 188, 101, 134, 179, 162, 172, 160, 149, 22,
],
[
93, 226, 69, 177, 229, 17, 78, 185, 6, 206, 195, 246, 145, 189, 141, 7, 197, 148, 166,
43, 203, 235, 170, 119, 102, 76, 108, 98, 216, 237, 121, 34,
],
[
211, 167, 46, 90, 228, 111, 217, 129, 255, 3, 113, 207, 200, 221, 28, 48, 33, 62, 31,
245, 116, 175, 130, 128, 180, 252, 132, 178, 56, 58, 16, 2,
],
[
159, 176, 149, 39, 220, 58, 146, 80, 175, 91, 125, 15, 166, 114, 133, 117, 52, 243,
219, 221, 223, 114, 140, 236, 106, 39, 65, 168, 43, 244, 140, 57,
],
[
144, 68, 49, 189, 208, 94, 145, 108, 143, 62, 16, 188, 15, 110, 23, 239, 71, 48, 32,
238, 96, 19, 43, 91, 231, 90, 77, 162, 159, 162, 71, 15,
],
[
103, 8, 114, 153, 156, 97, 188, 167, 128, 217, 58, 42, 208, 82, 234, 142, 53, 71, 10,
38, 177, 2, 13, 35, 8, 49, 196, 134, 215, 255, 42, 54,
],
[
229, 29, 149, 199, 252, 232, 6, 148, 31, 243, 79, 192, 221, 191, 136, 186, 249, 198,
35, 155, 198, 198, 19, 183, 159, 123, 65, 127, 169, 3, 156, 59,
],
[
2, 244, 213, 144, 80, 83, 125, 211, 252, 98, 209, 105, 104, 213, 143, 183, 164, 199,
103, 53, 110, 48, 230, 35, 34, 129, 221, 255, 225, 224, 42, 42,
],
],
[
[
145, 41, 77, 21, 230, 237, 146, 98, 160, 218, 242, 227, 198, 83, 11, 39, 148, 69, 31,
185, 143, 52, 71, 75, 157, 26, 157, 188, 179, 27, 114, 24,
],
[
160, 247, 33, 120, 242, 78, 125, 237, 149, 68, 194, 190, 248, 145, 93, 23, 171, 167,
181, 242, 226, 41, 104, 67, 0, 116, 81, 246, 87, 82, 103, 51,
],
[
84, 35, 131, 165, 134, 206, 147, 191, 7, 3, 253, 142, 49, 128, 111, 47, 53, 169, 88,
17, 31, 193, 20, 98, 19, 173, 111, 175, 134, 186, 166, 27,
],
[
49, 150, 139, 110, 180, 138, 202, 107, 41, 238, 123, 185, 17, 161, 67, 30, 2, 2, 39,
91, 7, 35, 69, 121, 34, 12, 247, 78, 138, 39, 59, 8,
],
[
64, 14, 249, 58, 50, 65, 122, 135, 174, 11, 102, 220, 221, 64, 29, 66, 24, 169, 57,
114, 140, 176, 7, 149, 78, 15, 211, 255, 101, 244, 151, 46,
],
[
127, 185, 215, 42, 158, 164, 234, 37, 140, 239, 228, 75, 189, 8, 197, 4, 206, 24, 136,
191, 73, 206, 141, 195, 85, 123, 141, 189, 82, 250, 65, 21,
],
[
49, 239, 163, 97, 219, 143, 242, 84, 53, 166, 149, 155, 243, 11, 207, 69, 250, 25, 159,
142, 240, 8, 72, 229, 91, 179, 218, 39, 128, 133, 201, 6,
],
[
96, 144, 236, 91, 71, 246, 217, 36, 27, 102, 209, 14, 75, 249, 185, 211, 2, 97, 216,
204, 141, 6, 234, 251, 183, 215, 152, 151, 125, 210, 121, 14,
],
],
];
pub fn generator() -> pallas::Affine {
pallas::Affine::from_xy(
pallas::Base::from_bytes(&GENERATOR.0).unwrap(),
pallas::Base::from_bytes(&GENERATOR.1).unwrap(),
)
.unwrap()
}
#[cfg(test)]
mod tests {
use super::super::{
test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION,
};
use super::*;
use group::Curve;
use pasta_curves::{
arithmetic::{CurveAffine, CurveExt, FieldExt},
pallas,
};
#[test]
fn generator() {
let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let point = hasher(b"v");
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap());
assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap());
}
#[test]
fn lagrange_coeffs_short() {
let base = super::generator();
test_lagrange_coeffs(base, NUM_WINDOWS_SHORT);
}
#[test]
fn z_short() {
let base = super::generator();
test_zs_and_us(base, &Z_SHORT, &U_SHORT, NUM_WINDOWS_SHORT);
}
}

View File

@@ -0,0 +1,261 @@
//! Sinsemilla generators
use super::OrchardFixedBases;
//use crate::spec::i2lebsp;
use halo2_gadgets::sinsemilla::{CommitDomains, HashDomains};
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
pub const K: usize = 10;
/// $\frac{1}{2^K}$
pub const INV_TWO_POW_K: [u8; 32] = [
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 240, 63,
];
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
/// of Pallas.
pub const C: usize = 253;
/// SWU hash-to-curve personalization for the Merkle CRH generator
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
// Sinsemilla Q generators
/// SWU hash-to-curve personalization for Sinsemilla $Q$ generators.
pub const Q_PERSONALIZATION: &str = "z.cash:SinsemillaQ";
// Sinsemilla S generators
/// SWU hash-to-curve personalization for Sinsemilla $S$ generators.
pub const S_PERSONALIZATION: &str = "z.cash:SinsemillaS";
/// Generator used in SinsemillaHashToPoint for note commitment
pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
93, 116, 168, 64, 9, 186, 14, 50, 42, 221, 70, 253, 90, 15, 150, 197, 93, 237, 176, 121,
180, 242, 159, 247, 13, 205, 251, 86, 160, 7, 128, 23,
],
[
99, 172, 73, 115, 90, 10, 39, 135, 158, 94, 219, 129, 136, 18, 34, 136, 44, 201, 244, 110,
217, 194, 190, 78, 131, 112, 198, 138, 147, 88, 160, 50,
],
);
/// Generator used in SinsemillaHashToPoint for IVK commitment
pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
242, 130, 15, 121, 146, 47, 203, 107, 50, 162, 40, 81, 36, 204, 27, 66, 250, 65, 162, 90,
184, 129, 204, 125, 17, 200, 169, 74, 241, 12, 188, 5,
],
[
190, 222, 173, 207, 206, 229, 90, 190, 241, 165, 109, 201, 29, 53, 196, 70, 75, 5, 222, 32,
70, 7, 89, 239, 230, 190, 26, 212, 246, 76, 1, 27,
],
);
/// Generator used in SinsemillaHashToPoint for Merkle collision-resistant hash
pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
[
160, 198, 41, 127, 249, 199, 185, 248, 112, 16, 141, 192, 85, 185, 190, 201, 153, 14, 137,
239, 90, 54, 15, 160, 185, 24, 168, 99, 150, 210, 22, 22,
],
[
98, 234, 242, 37, 206, 174, 233, 134, 150, 21, 116, 5, 234, 150, 28, 226, 121, 89, 163, 79,
62, 242, 196, 45, 153, 32, 175, 227, 163, 66, 134, 53,
],
);
pub fn i2lebsp<const NUM_BITS: usize>(int: u64) -> [bool; NUM_BITS] {
assert!(NUM_BITS <= 64);
super::util::gen_const_array(|mask: usize| (int & (1 << mask)) != 0)
}
#[allow(dead_code)]
fn lebs2ip_k(bits: &[bool]) -> u32 {
assert!(bits.len() == K);
bits.iter()
.enumerate()
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
}
/// The sequence of K bits in little-endian order representing an integer
/// up to `2^K` - 1.
pub fn i2lebsp_k(int: usize) -> [bool; K] {
assert!(int < (1 << K));
i2lebsp(int as u64)
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardHashDomains {
NoteCommit,
CommitIvk,
MerkleCrh,
}
#[allow(non_snake_case)]
impl HashDomains<pallas::Affine> for OrchardHashDomains {
fn Q(&self) -> pallas::Affine {
match self {
OrchardHashDomains::CommitIvk => pallas::Affine::from_xy(
pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap(),
pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap(),
)
.unwrap(),
OrchardHashDomains::NoteCommit => pallas::Affine::from_xy(
pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap(),
pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(),
)
.unwrap(),
OrchardHashDomains::MerkleCrh => pallas::Affine::from_xy(
pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap(),
pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap(),
)
.unwrap(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardCommitDomains {
NoteCommit,
CommitIvk,
}
impl CommitDomains<pallas::Affine, OrchardFixedBases, OrchardHashDomains> for OrchardCommitDomains {
fn r(&self) -> OrchardFixedBases {
match self {
Self::NoteCommit => OrchardFixedBases::NoteCommitR,
Self::CommitIvk => OrchardFixedBases::CommitIvkR,
}
}
fn hash_domain(&self) -> OrchardHashDomains {
match self {
Self::NoteCommit => OrchardHashDomains::NoteCommit,
Self::CommitIvk => OrchardHashDomains::CommitIvk,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::constants::{
fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION},
sinsemilla::MERKLE_CRH_PERSONALIZATION,
};
use halo2_gadgets::primitives::sinsemilla::{CommitDomain, HashDomain};
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
group::{ff::PrimeField, Curve},
pallas,
};
use rand::{self, rngs::OsRng, Rng};
#[test]
// Nodes in the Merkle tree are Pallas base field elements.
fn l_orchard_merkle() {
assert_eq!(255, pallas::Base::NUM_BITS as usize);
}
#[test]
fn lebs2ip_k_round_trip() {
let mut rng = OsRng;
{
let int = rng.gen_range(0..(1 << K));
assert_eq!(lebs2ip_k(&i2lebsp_k(int)) as usize, int);
}
assert_eq!(lebs2ip_k(&i2lebsp_k(0)) as usize, 0);
assert_eq!(lebs2ip_k(&i2lebsp_k((1 << K) - 1)) as usize, (1 << K) - 1);
}
#[test]
fn i2lebsp_k_round_trip() {
{
let bitstring = (0..K).map(|_| rand::random()).collect::<Vec<_>>();
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [false; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [true; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
}
#[test]
fn q_note_commitment_m() {
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
*coords.x(),
pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap()
);
assert_eq!(
*coords.y(),
pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap()
);
}
#[test]
fn q_commit_ivk_m() {
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
*coords.x(),
pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap()
);
assert_eq!(
*coords.y(),
pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap()
);
}
#[test]
fn q_merkle_crh() {
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
*coords.x(),
pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap()
);
assert_eq!(
*coords.y(),
pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap()
);
}
#[test]
fn inv_two_pow_k() {
let two_pow_k = pallas::Base::from_u64(1u64 << K);
let inv_two_pow_k = pallas::Base::from_bytes(&INV_TWO_POW_K).unwrap();
assert_eq!(two_pow_k * inv_two_pow_k, pallas::Base::one());
}
}

View File

@@ -0,0 +1,31 @@
use pasta_curves::arithmetic::{CurveAffine, Field, FieldExt};
/// Evaluate y = f(x) given the coefficients of f(x)
pub fn evaluate<C: CurveAffine>(x: u8, coeffs: &[C::Base]) -> C::Base {
let x = C::Base::from_u64(x as u64);
coeffs
.iter()
.rev()
.cloned()
.reduce(|acc, coeff| acc * x + coeff)
.unwrap_or_else(C::Base::zero)
}
/// Takes in an FnMut closure and returns a constant-length array with elements of
/// type `Output`.
pub fn gen_const_array<Output: Copy + Default, const LEN: usize>(
closure: impl FnMut(usize) -> Output,
) -> [Output; LEN] {
gen_const_array_with_default(Default::default(), closure)
}
pub(crate) fn gen_const_array_with_default<Output: Copy, const LEN: usize>(
default_value: Output,
mut closure: impl FnMut(usize) -> Output,
) -> [Output; LEN] {
let mut ret: [Output; LEN] = [default_value; LEN];
for (bit, val) in ret.iter_mut().zip((0..LEN).map(|idx| closure(idx))) {
*bit = val;
}
ret
}

View File

@@ -1,5 +1,6 @@
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use group::{cofactor::CofactorGroup, GroupEncoding};
use pasta_curves as pasta;
use pasta_curves::group::{cofactor::CofactorGroup, GroupEncoding};
pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"DarkFiSaplingKDF";
@@ -8,7 +9,7 @@ pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"DarkFiSaplingKDF";
/// Sapling key agreement for note encryption.
///
/// Implements section 5.4.4.3 of the Zcash Protocol Specification.
pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint {
pub fn sapling_ka_agree(esk: &pasta::Fq, pk_d: &pasta::Ep) -> pasta::Ep {
// [8 esk] pk_d
// <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
// ExtendedPoint::mul_by_cofactor in the jubjub crate.
@@ -25,7 +26,7 @@ pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubju
/// Sapling KDF for note encryption.
///
/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
pub fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::ExtendedPoint) -> Blake2bHash {
pub fn kdf_sapling(dhsecret: pasta::Ep, epk: &pasta::Ep) -> Blake2bHash {
Blake2bParams::new()
.hash_length(32)
.personal(KDF_SAPLING_PERSONALIZATION)

View File

@@ -1,455 +1,147 @@
//! Implementation of a Merkle tree of commitments used to prove the existence
//! of notes.
use std::iter;
//use byteorder::{LittleEndian, ReadBytesExt};
use crate::serial::{Decodable, Encodable, VarInt};
use crate::{Error, Result};
use std::collections::VecDeque;
use std::io;
use std::io::{Read, Write};
use halo2_gadgets::primitives::sinsemilla::HashDomain;
use incrementalmerkletree::{Altitude, Hashable};
use lazy_static::lazy_static;
use pasta_curves::{
arithmetic::FieldExt,
group::ff::{PrimeField, PrimeFieldBits},
pallas,
};
//use super::serialize::{Optional, Vector};
use super::merkle_node::SAPLING_COMMITMENT_TREE_DEPTH;
use super::{
coin::Coin,
constants::{
sinsemilla::{i2lebsp_k, MERKLE_CRH_PERSONALIZATION},
util::gen_const_array_with_default,
},
};
/// A hashable node within a Merkle tree.
pub trait Hashable: Clone + Copy + Encodable + Decodable {
/// Parses a node from the given byte source.
fn read<R: Read>(reader: R) -> Result<Self>;
// TODO: to constants
const MERKLE_DEPTH_ORCHARD: usize = 32;
/// Serializes this node.
fn write<W: Write>(&self, writer: W) -> Result<()>;
/// Returns the parent node within the tree of the two given nodes.
fn combine(_: usize, _: &Self, _: &Self) -> Self;
/// Returns a blank leaf node.
fn blank() -> Self;
/// Returns the empty root for the given depth.
fn empty_root(_: usize) -> Self;
lazy_static! {
static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from_u64(2);
pub(crate) static ref EMPTY_ROOTS: Vec<MerkleHash> = {
iter::empty()
.chain(Some(MerkleHash::empty_leaf()))
.chain(
(0..MERKLE_DEPTH_ORCHARD).scan(MerkleHash::empty_leaf(), |state, l| {
let l = l as u8;
*state = MerkleHash::combine(l.into(), state, state);
Some(*state)
}),
)
.collect()
};
}
struct PathFiller<Node: Hashable> {
queue: VecDeque<Node>,
}
#[derive(Copy, Clone, Debug)]
pub struct MerkleHash(pallas::Base);
impl<Node: Hashable> PathFiller<Node> {
fn empty() -> Self {
PathFiller {
queue: VecDeque::new(),
}
impl MerkleHash {
pub fn from_coin(value: &Coin) -> Self {
MerkleHash(value.inner())
}
fn next(&mut self, depth: usize) -> Node {
self.queue
.pop_front()
.unwrap_or_else(|| Node::empty_root(depth))
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
pallas::Base::from_bytes(bytes).map(MerkleHash).unwrap()
}
}
/// A Merkle tree of note commitments.
///
/// The depth of the Merkle tree is fixed at 32, equal to the depth of the
/// Sapling commitment tree.
#[derive(Clone)]
pub struct CommitmentTree<Node: Hashable> {
left: Option<Node>,
right: Option<Node>,
parents: Vec<Option<Node>>,
}
impl<Node: Hashable> CommitmentTree<Node> {
/// Creates an empty tree.
pub fn empty() -> Self {
CommitmentTree {
left: None,
right: None,
parents: vec![],
}
impl Hashable for MerkleHash {
fn empty_leaf() -> Self {
MerkleHash(*UNCOMMITTED_ORCHARD)
}
/// Returns the number of leaf nodes in the tree.
pub fn size(&self) -> usize {
self.parents.iter().enumerate().fold(
match (self.left, self.right) {
(None, None) => 0,
(Some(_), None) => 1,
(Some(_), Some(_)) => 2,
(None, Some(_)) => unreachable!(),
},
|acc, (i, p)| {
// Treat occupation of parents array as a binary number
// (right-shifted by 1)
acc + if p.is_some() { 1 << (i + 1) } else { 0 }
},
fn combine(altitude: Altitude, left: &Self, right: &Self) -> Self {
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
MerkleHash(
domain
.hash(
iter::empty()
.chain(i2lebsp_k(altitude.into()).iter().copied())
.chain(left.0.to_le_bits().iter().by_val().take(255))
.chain(right.0.to_le_bits().iter().by_val().take(255)),
)
.unwrap_or(pallas::Base::zero()),
)
}
fn is_complete(&self, depth: usize) -> bool {
self.left.is_some()
&& self.right.is_some()
&& self.parents.len() == depth - 1
&& self.parents.iter().all(|p| p.is_some())
}
/// Adds a leaf node to the tree.
///
/// Returns an error if the tree is full.
pub fn append(&mut self, node: Node) -> Result<()> {
self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH)
}
fn append_inner(&mut self, node: Node, depth: usize) -> Result<()> {
if self.is_complete(depth) {
return Err(Error::TreeFull);
}
match (self.left, self.right) {
(None, _) => self.left = Some(node),
(_, None) => self.right = Some(node),
(Some(l), Some(r)) => {
let mut combined = Node::combine(0, &l, &r);
self.left = Some(node);
self.right = None;
for i in 0..depth {
if i < self.parents.len() {
if let Some(p) = self.parents[i] {
combined = Node::combine(i + 1, &p, &combined);
self.parents[i] = None;
} else {
self.parents[i] = Some(combined);
break;
}
} else {
self.parents.push(Some(combined));
break;
}
}
}
}
Ok(())
}
/// Returns the current root of the tree.
pub fn root(&self) -> Node {
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH, PathFiller::empty())
}
fn root_inner(&self, depth: usize, mut filler: PathFiller<Node>) -> Node {
assert!(depth > 0);
// 1) Hash left and right leaves together.
// - Empty leaves are used as needed.
let leaf_root = Node::combine(
0,
&self.left.unwrap_or_else(|| filler.next(0)),
&self.right.unwrap_or_else(|| filler.next(0)),
);
// 2) Hash in parents up to the currently-filled depth.
// - Roots of the empty subtrees are used as needed.
let mid_root = self
.parents
.iter()
.enumerate()
.fold(leaf_root, |root, (i, p)| match p {
Some(node) => Node::combine(i + 1, node, &root),
None => Node::combine(i + 1, &root, &filler.next(i + 1)),
});
// 3) Hash in roots of the empty subtrees up to the final depth.
((self.parents.len() + 1)..depth)
.fold(mid_root, |root, d| Node::combine(d, &root, &filler.next(d)))
fn empty_root(altitude: Altitude) -> Self {
EMPTY_ROOTS[<usize>::from(altitude)]
}
}
impl<Node: Hashable> Encodable for CommitmentTree<Node> {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
let mut len = 0;
len += self.left.encode(&mut s)?;
len += self.right.encode(&mut s)?;
len += self.parents.encode(&mut s)?;
Ok(len)
pub struct Anchor(pallas::Base);
impl From<pallas::Base> for Anchor {
fn from(anchor_field: pallas::Base) -> Anchor {
Anchor(anchor_field)
}
}
impl<Node: Hashable> Decodable for CommitmentTree<Node> {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
Ok(Self {
left: Decodable::decode(&mut d)?,
right: Decodable::decode(&mut d)?,
parents: Decodable::decode(&mut d)?,
})
impl From<MerkleHash> for Anchor {
fn from(anchor: MerkleHash) -> Anchor {
Anchor(anchor.0)
}
}
/*
/// An updatable witness to a path from a position in a particular
/// [`CommitmentTree`].
///
/// Appending the same commitments in the same order to both the original
/// [`CommitmentTree`] and this `IncrementalWitness` will result in a witness to
/// the path from the target position to the root of the updated tree.
///
/// # Examples
///
/// ```
/// use ff::{Field, PrimeField};
/// use rand_core::OsRng;
/// use zcash_primitives::{
/// merkle_tree::{CommitmentTree, IncrementalWitness},
/// sapling::Node,
/// };
///
/// let mut rng = OsRng;
///
/// let mut tree = CommitmentTree::<Node>::empty();
///
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
/// let mut witness = IncrementalWitness::from_tree(&tree);
/// assert_eq!(witness.position(), 1);
/// assert_eq!(tree.root(), witness.root());
///
/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr());
/// tree.append(cmu);
/// witness.append(cmu);
/// assert_eq!(tree.root(), witness.root());
/// ```
///
*/
impl Anchor {
pub fn from_bytes(bytes: [u8; 32]) -> Anchor {
pallas::Base::from_repr(bytes).map(Anchor).unwrap()
}
#[derive(Clone)]
pub struct IncrementalWitness<Node: Hashable> {
tree: CommitmentTree<Node>,
filled: Vec<Node>,
cursor_depth: usize,
cursor: Option<CommitmentTree<Node>>,
}
impl<Node: Hashable> Encodable for Vec<Node> {
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)
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_repr()
}
}
impl<Node: Hashable> Decodable for Vec<Node> {
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)
}
#[derive(Debug)]
pub struct MerklePath {
position: u32,
auth_path: [MerkleHash; MERKLE_DEPTH_ORCHARD],
}
impl<Node: Hashable> Encodable for IncrementalWitness<Node> {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
let mut len = 0;
len += self.tree.encode(&mut s)?;
len += self.filled.encode(&mut s)?;
len += self.cursor_depth.encode(&mut s)?;
len += self.cursor.encode(&mut s)?;
Ok(len)
}
}
impl<Node: Hashable> Decodable for IncrementalWitness<Node> {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
Ok(Self {
tree: Decodable::decode(&mut d)?,
filled: Decodable::decode(&mut d)?,
cursor_depth: Decodable::decode(&mut d)?,
cursor: Decodable::decode(d)?,
})
}
}
impl<Node: Hashable> IncrementalWitness<Node> {
/// Creates an `IncrementalWitness` for the most recent commitment added to
/// the given [`CommitmentTree`].
pub fn from_tree(tree: &CommitmentTree<Node>) -> IncrementalWitness<Node> {
IncrementalWitness {
tree: tree.clone(),
filled: vec![],
cursor_depth: 0,
cursor: None,
}
}
/// Returns the position of the witnessed leaf node in the commitment tree.
pub fn position(&self) -> usize {
self.tree.size() - 1
}
fn filler(&self) -> PathFiller<Node> {
let cursor_root = self
.cursor
.as_ref()
.map(|c| c.root_inner(self.cursor_depth, PathFiller::empty()));
PathFiller {
queue: self.filled.iter().cloned().chain(cursor_root).collect(),
}
}
/// Finds the next "depth" of an unfilled subtree.
fn next_depth(&self) -> usize {
let mut skip = self.filled.len();
if self.tree.left.is_none() {
if skip > 0 {
skip -= 1;
} else {
return 0;
}
}
if self.tree.right.is_none() {
if skip > 0 {
skip -= 1;
} else {
return 0;
}
}
let mut d = 1;
for p in &self.tree.parents {
if p.is_none() {
if skip > 0 {
skip -= 1;
} else {
return d;
}
}
d += 1;
}
d + skip
}
/// Tracks a leaf node that has been added to the underlying tree.
///
/// Returns an error if the tree is full.
pub fn append(&mut self, node: Node) -> Result<()> {
self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH)
}
fn append_inner(&mut self, node: Node, depth: usize) -> Result<()> {
if let Some(mut cursor) = self.cursor.take() {
cursor
.append_inner(node, depth)
.expect("cursor should not be full");
if cursor.is_complete(self.cursor_depth) {
self.filled
.push(cursor.root_inner(self.cursor_depth, PathFiller::empty()));
} else {
self.cursor = Some(cursor);
}
} else {
self.cursor_depth = self.next_depth();
if self.cursor_depth >= depth {
return Err(Error::TreeFull);
}
if self.cursor_depth == 0 {
self.filled.push(node);
} else {
let mut cursor = CommitmentTree::empty();
cursor
.append_inner(node, depth)
.expect("cursor should not be full");
self.cursor = Some(cursor);
}
}
Ok(())
}
/// Returns the current root of the tree corresponding to the witness.
pub fn root(&self) -> Node {
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH)
}
fn root_inner(&self, depth: usize) -> Node {
self.tree.root_inner(depth, self.filler())
}
/// Returns the current witness, or None if the tree is empty.
pub fn path(&self) -> Option<MerklePath<Node>> {
self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH)
}
fn path_inner(&self, depth: usize) -> Option<MerklePath<Node>> {
let mut filler = self.filler();
let mut auth_path = Vec::new();
if let Some(node) = self.tree.left {
if self.tree.right.is_some() {
auth_path.push((node, true));
} else {
auth_path.push((filler.next(0), false));
}
} else {
// Can't create an authentication path for the beginning of the tree
return None;
}
for (i, p) in self.tree.parents.iter().enumerate() {
auth_path.push(match p {
Some(node) => (*node, true),
None => (filler.next(i + 1), false),
});
}
for i in self.tree.parents.len()..(depth - 1) {
auth_path.push((filler.next(i + 1), false));
}
assert_eq!(auth_path.len(), depth);
Some(MerklePath::from_path(auth_path, self.position() as u64))
}
}
/// A path from a position in a particular commitment tree to the root of that
/// tree.
#[derive(Clone, Debug, PartialEq)]
pub struct MerklePath<Node: Hashable> {
pub auth_path: Vec<(Node, bool)>,
pub position: u64,
}
impl<Node: Hashable> MerklePath<Node> {
/// Constructs a Merkle path directly from a path and position.
pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self {
MerklePath {
auth_path,
impl MerklePath {
pub fn new(position: u32, auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD]) -> Self {
Self {
position,
auth_path: gen_const_array_with_default(MerkleHash::empty_leaf(), |i| {
MerkleHash(auth_path[i])
}),
}
}
/// Returns the root of the tree corresponding to this path applied to
/// `leaf`.
pub fn root(&self, leaf: Node) -> Node {
pub fn root(&self, coin: Coin) -> Anchor {
self.auth_path
.iter()
.enumerate()
.fold(
leaf,
|root, (i, (p, leaf_is_on_right))| match leaf_is_on_right {
false => Node::combine(i, &root, p),
true => Node::combine(i, p, &root),
},
)
.fold(MerkleHash::from_coin(&coin), |node, (l, sibling)| {
let l = l as u8;
if self.position & (1 << l) == 0 {
MerkleHash::combine(l.into(), &node, sibling)
} else {
MerkleHash::combine(l.into(), sibling, &node)
}
})
.into()
}
pub fn position(&self) -> u32 {
self.position
}
pub fn auth_path(&self) -> [MerkleHash; MERKLE_DEPTH_ORCHARD] {
self.auth_path
}
}

453
src/crypto/merkle_old.rs Normal file
View File

@@ -0,0 +1,453 @@
//! Implementation of a Merkle tree of commitments used to prove the existence
//! of notes.
//use byteorder::{LittleEndian, ReadBytesExt};
use crate::serial::{Decodable, Encodable, VarInt};
use crate::{Error, Result};
use std::collections::VecDeque;
use std::io;
use std::io::{Read, Write};
//use super::serialize::{Optional, Vector};
use super::merkle_node::SAPLING_COMMITMENT_TREE_DEPTH;
/// A hashable node within a Merkle tree.
pub trait Hashable: Clone + Copy + Encodable + Decodable {
/// Parses a node from the given byte source.
fn read<R: Read>(reader: R) -> Result<Self>;
/// Serializes this node.
fn write<W: Write>(&self, writer: W) -> Result<()>;
/// Returns the parent node within the tree of the two given nodes.
fn combine(_: usize, _: &Self, _: &Self) -> Self;
/// Returns a blank leaf node.
fn blank() -> Self;
/// Returns the empty root for the given depth.
fn empty_root(_: usize) -> Self;
}
struct PathFiller<Node: Hashable> {
queue: VecDeque<Node>,
}
impl<Node: Hashable> PathFiller<Node> {
fn empty() -> Self {
PathFiller {
queue: VecDeque::new(),
}
}
fn next(&mut self, depth: usize) -> Node {
self.queue
.pop_front()
.unwrap_or_else(|| Node::empty_root(depth))
}
}
/// A Merkle tree of note commitments.
///
/// The depth of the Merkle tree is fixed at 32, equal to the depth of the
/// Sapling commitment tree.
#[derive(Clone)]
pub struct CommitmentTree<Node: Hashable> {
left: Option<Node>,
right: Option<Node>,
parents: Vec<Option<Node>>,
}
impl<Node: Hashable> CommitmentTree<Node> {
/// Creates an empty tree.
pub fn empty() -> Self {
CommitmentTree {
left: None,
right: None,
parents: vec![],
}
}
/// Returns the number of leaf nodes in the tree.
pub fn size(&self) -> usize {
self.parents.iter().enumerate().fold(
match (self.left, self.right) {
(None, None) => 0,
(Some(_), None) => 1,
(Some(_), Some(_)) => 2,
(None, Some(_)) => unreachable!(),
},
|acc, (i, p)| {
// Treat occupation of parents array as a binary number
// (right-shifted by 1)
acc + if p.is_some() { 1 << (i + 1) } else { 0 }
},
)
}
fn is_complete(&self, depth: usize) -> bool {
self.left.is_some()
&& self.right.is_some()
&& self.parents.len() == depth - 1
&& self.parents.iter().all(|p| p.is_some())
}
/// Adds a leaf node to the tree.
///
/// Returns an error if the tree is full.
pub fn append(&mut self, node: Node) -> Result<()> {
self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH)
}
fn append_inner(&mut self, node: Node, depth: usize) -> Result<()> {
if self.is_complete(depth) {
return Err(Error::TreeFull);
}
match (self.left, self.right) {
(None, _) => self.left = Some(node),
(_, None) => self.right = Some(node),
(Some(l), Some(r)) => {
let mut combined = Node::combine(0, &l, &r);
self.left = Some(node);
self.right = None;
for i in 0..depth {
if i < self.parents.len() {
if let Some(p) = self.parents[i] {
combined = Node::combine(i + 1, &p, &combined);
self.parents[i] = None;
} else {
self.parents[i] = Some(combined);
break;
}
} else {
self.parents.push(Some(combined));
break;
}
}
}
}
Ok(())
}
/// Returns the current root of the tree.
pub fn root(&self) -> Node {
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH, PathFiller::empty())
}
fn root_inner(&self, depth: usize, mut filler: PathFiller<Node>) -> Node {
assert!(depth > 0);
// 1) Hash left and right leaves together.
// - Empty leaves are used as needed.
let leaf_root = Node::combine(
0,
&self.left.unwrap_or_else(|| filler.next(0)),
&self.right.unwrap_or_else(|| filler.next(0)),
);
// 2) Hash in parents up to the currently-filled depth.
// - Roots of the empty subtrees are used as needed.
let mid_root = self
.parents
.iter()
.enumerate()
.fold(leaf_root, |root, (i, p)| match p {
Some(node) => Node::combine(i + 1, node, &root),
None => Node::combine(i + 1, &root, &filler.next(i + 1)),
});
// 3) Hash in roots of the empty subtrees up to the final depth.
((self.parents.len() + 1)..depth)
.fold(mid_root, |root, d| Node::combine(d, &root, &filler.next(d)))
}
}
impl<Node: Hashable> Encodable for CommitmentTree<Node> {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
let mut len = 0;
len += self.left.encode(&mut s)?;
len += self.right.encode(&mut s)?;
len += self.parents.encode(&mut s)?;
Ok(len)
}
}
impl<Node: Hashable> Decodable for CommitmentTree<Node> {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
Ok(Self {
left: Decodable::decode(&mut d)?,
right: Decodable::decode(&mut d)?,
parents: Decodable::decode(&mut d)?,
})
}
}
/// An updatable witness to a path from a position in a particular
/// [`CommitmentTree`].
///
/// Appending the same commitments in the same order to both the original
/// [`CommitmentTree`] and this `IncrementalWitness` will result in a witness to
/// the path from the target position to the root of the updated tree.
///
/// # Examples
///
/// ```
/// use ff::{Field, PrimeField};
/// use rand_core::OsRng;
/// use zcash_primitives::{
/// merkle_tree::{CommitmentTree, IncrementalWitness},
/// sapling::Node,
/// };
///
/// let mut rng = OsRng;
///
/// let mut tree = CommitmentTree::<Node>::empty();
///
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
/// let mut witness = IncrementalWitness::from_tree(&tree);
/// assert_eq!(witness.position(), 1);
/// assert_eq!(tree.root(), witness.root());
///
/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr());
/// tree.append(cmu);
/// witness.append(cmu);
/// assert_eq!(tree.root(), witness.root());
/// ```
///
#[derive(Clone)]
pub struct IncrementalWitness<Node: Hashable> {
tree: CommitmentTree<Node>,
filled: Vec<Node>,
cursor_depth: usize,
cursor: Option<CommitmentTree<Node>>,
}
impl<Node: Hashable> Encodable for Vec<Node> {
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<Node: Hashable> Decodable for Vec<Node> {
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<Node: Hashable> Encodable for IncrementalWitness<Node> {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
let mut len = 0;
len += self.tree.encode(&mut s)?;
len += self.filled.encode(&mut s)?;
len += self.cursor_depth.encode(&mut s)?;
len += self.cursor.encode(&mut s)?;
Ok(len)
}
}
impl<Node: Hashable> Decodable for IncrementalWitness<Node> {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
Ok(Self {
tree: Decodable::decode(&mut d)?,
filled: Decodable::decode(&mut d)?,
cursor_depth: Decodable::decode(&mut d)?,
cursor: Decodable::decode(d)?,
})
}
}
impl<Node: Hashable> IncrementalWitness<Node> {
/// Creates an `IncrementalWitness` for the most recent commitment added to
/// the given [`CommitmentTree`].
pub fn from_tree(tree: &CommitmentTree<Node>) -> IncrementalWitness<Node> {
IncrementalWitness {
tree: tree.clone(),
filled: vec![],
cursor_depth: 0,
cursor: None,
}
}
/// Returns the position of the witnessed leaf node in the commitment tree.
pub fn position(&self) -> usize {
self.tree.size() - 1
}
fn filler(&self) -> PathFiller<Node> {
let cursor_root = self
.cursor
.as_ref()
.map(|c| c.root_inner(self.cursor_depth, PathFiller::empty()));
PathFiller {
queue: self.filled.iter().cloned().chain(cursor_root).collect(),
}
}
/// Finds the next "depth" of an unfilled subtree.
fn next_depth(&self) -> usize {
let mut skip = self.filled.len();
if self.tree.left.is_none() {
if skip > 0 {
skip -= 1;
} else {
return 0;
}
}
if self.tree.right.is_none() {
if skip > 0 {
skip -= 1;
} else {
return 0;
}
}
let mut d = 1;
for p in &self.tree.parents {
if p.is_none() {
if skip > 0 {
skip -= 1;
} else {
return d;
}
}
d += 1;
}
d + skip
}
/// Tracks a leaf node that has been added to the underlying tree.
///
/// Returns an error if the tree is full.
pub fn append(&mut self, node: Node) -> Result<()> {
self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH)
}
fn append_inner(&mut self, node: Node, depth: usize) -> Result<()> {
if let Some(mut cursor) = self.cursor.take() {
cursor
.append_inner(node, depth)
.expect("cursor should not be full");
if cursor.is_complete(self.cursor_depth) {
self.filled
.push(cursor.root_inner(self.cursor_depth, PathFiller::empty()));
} else {
self.cursor = Some(cursor);
}
} else {
self.cursor_depth = self.next_depth();
if self.cursor_depth >= depth {
return Err(Error::TreeFull);
}
if self.cursor_depth == 0 {
self.filled.push(node);
} else {
let mut cursor = CommitmentTree::empty();
cursor
.append_inner(node, depth)
.expect("cursor should not be full");
self.cursor = Some(cursor);
}
}
Ok(())
}
/// Returns the current root of the tree corresponding to the witness.
pub fn root(&self) -> Node {
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH)
}
fn root_inner(&self, depth: usize) -> Node {
self.tree.root_inner(depth, self.filler())
}
/// Returns the current witness, or None if the tree is empty.
pub fn path(&self) -> Option<MerklePath<Node>> {
self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH)
}
fn path_inner(&self, depth: usize) -> Option<MerklePath<Node>> {
let mut filler = self.filler();
let mut auth_path = Vec::new();
if let Some(node) = self.tree.left {
if self.tree.right.is_some() {
auth_path.push((node, true));
} else {
auth_path.push((filler.next(0), false));
}
} else {
// Can't create an authentication path for the beginning of the tree
return None;
}
for (i, p) in self.tree.parents.iter().enumerate() {
auth_path.push(match p {
Some(node) => (*node, true),
None => (filler.next(i + 1), false),
});
}
for i in self.tree.parents.len()..(depth - 1) {
auth_path.push((filler.next(i + 1), false));
}
assert_eq!(auth_path.len(), depth);
Some(MerklePath::from_path(auth_path, self.position() as u64))
}
}
/// A path from a position in a particular commitment tree to the root of that
/// tree.
#[derive(Clone, Debug, PartialEq)]
pub struct MerklePath<Node: Hashable> {
pub auth_path: Vec<(Node, bool)>,
pub position: u64,
}
impl<Node: Hashable> MerklePath<Node> {
/// Constructs a Merkle path directly from a path and position.
pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self {
MerklePath {
auth_path,
position,
}
}
/// Returns the root of the tree corresponding to this path applied to
/// `leaf`.
pub fn root(&self, leaf: Node) -> Node {
self.auth_path
.iter()
.enumerate()
.fold(
leaf,
|root, (i, (p, leaf_is_on_right))| match leaf_is_on_right {
false => Node::combine(i, &root, p),
true => Node::combine(i, p, &root),
},
)
}
}

View File

@@ -1,56 +1,59 @@
use bellman::gadgets::multipack;
use bellman::groth16;
use blake2s_simd::Params as Blake2sParams;
use bls12_381::Bls12;
use group::{Curve, GroupEncoding};
use rand::rngs::OsRng;
use std::io;
use std::time::Instant;
use crate::circuit::mint_contract::MintContract;
use crate::error::Result;
use crate::serial::{Decodable, Encodable};
use halo2_gadgets::primitives;
use halo2_gadgets::primitives::poseidon::{ConstantLength, P128Pow5T3};
use log::debug;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
group::Curve,
pallas,
};
use super::{
proof::{Proof, ProvingKey, VerifyingKey},
util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
};
use crate::{
circuit::mint_contract::MintContract,
serial::{Decodable, Encodable},
types::*,
Result,
};
pub struct MintRevealedValues {
pub value_commit: jubjub::SubgroupPoint,
pub token_commit: jubjub::SubgroupPoint,
pub coin: [u8; 32],
pub value_commit: DrkValueCommit,
pub token_commit: DrkValueCommit,
//pub coin: [u8; 32],
pub coin: pallas::Base,
}
impl MintRevealedValues {
fn compute(
value: u64,
token_id: jubjub::Fr,
randomness_value: &jubjub::Fr,
randomness_token: &jubjub::Fr,
serial: &jubjub::Fr,
randomness_coin: &jubjub::Fr,
public: &jubjub::SubgroupPoint,
token_id: DrkTokenId,
value_blind: DrkValueBlind,
token_blind: DrkValueBlind,
serial: DrkSerial,
coin_blind: DrkCoinBlind,
public_key: DrkPublicKey,
) -> Self {
let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
* jubjub::Fr::from(value))
+ (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
* randomness_value);
let value_commit = pedersen_commitment_u64(value, value_blind);
let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
let token_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
* token_id)
+ (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
* randomness_token);
let coords = public_key.to_affine().coordinates().unwrap();
let messages = [
[*coords.x(), *coords.y()],
[DrkValue::from_u64(value), token_id],
[serial, coin_blind],
];
let mut coin = [0; 32];
coin.copy_from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(zcash_primitives::constants::CRH_IVK_PERSONALIZATION)
.to_state()
.update(&public.to_bytes())
.update(&value.to_le_bytes())
.update(&token_id.to_bytes())
.update(&serial.to_bytes())
.update(&randomness_coin.to_bytes())
.finalize()
.as_bytes(),
);
let mut coin = DrkCoin::zero();
for msg in messages.iter() {
coin += primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
}
//let coin = hash.to_bytes();
MintRevealedValues {
value_commit,
@@ -59,41 +62,20 @@ impl MintRevealedValues {
}
}
fn make_outputs(&self) -> [bls12_381::Scalar; 6] {
let mut public_input = [bls12_381::Scalar::zero(); 6];
fn make_outputs(&self) -> [DrkCircuitField; 5] {
let value_coords = self.value_commit.to_affine().coordinates().unwrap();
let token_coords = self.token_commit.to_affine().coordinates().unwrap();
{
let result = jubjub::ExtendedPoint::from(self.value_commit);
let affine = result.to_affine();
//let (u, v) = (affine.get_u(), affine.get_v());
let u = affine.get_u();
let v = affine.get_v();
public_input[0] = u;
public_input[1] = v;
}
{
let result = jubjub::ExtendedPoint::from(self.token_commit);
let affine = result.to_affine();
let u = affine.get_u();
let v = affine.get_v();
public_input[2] = u;
public_input[3] = v;
}
{
// Pack the hash as inputs for proof verification.
let hash = multipack::bytes_to_bits_le(&self.coin);
let hash = multipack::compute_multipacking(&hash);
// There are 2 chunks for a blake hash
assert_eq!(hash.len(), 2);
public_input[4] = hash[0];
public_input[5] = hash[1];
}
public_input
vec![
//DrkCircuitField::from_bytes(&self.coin).unwrap(),
self.coin.clone(),
*value_coords.x(),
*value_coords.y(),
*token_coords.x(),
*token_coords.y(),
]
.try_into()
.unwrap()
}
}
@@ -117,72 +99,59 @@ impl Decodable for MintRevealedValues {
}
}
pub fn setup_mint_prover() -> groth16::Parameters<Bls12> {
println!("Mint: Making random params...");
let start = Instant::now();
let params = {
let c = MintContract {
value: None,
token_id: None,
randomness_value: None,
randomness_token: None,
serial: None,
randomness_coin: None,
public: None,
};
groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
};
println!("Setup: [{:?}]", start.elapsed());
params
}
#[allow(clippy::too_many_arguments)]
pub fn create_mint_proof(
params: &groth16::Parameters<Bls12>,
value: u64,
token_id: jubjub::Fr,
randomness_value: jubjub::Fr,
randomness_token: jubjub::Fr,
serial: jubjub::Fr,
randomness_coin: jubjub::Fr,
public: jubjub::SubgroupPoint,
) -> (groth16::Proof<Bls12>, MintRevealedValues) {
token_id: DrkTokenId,
value_blind: DrkValueBlind,
token_blind: DrkValueBlind,
serial: DrkSerial,
coin_blind: DrkCoinBlind,
public_key: DrkPublicKey,
) -> Result<(Proof, MintRevealedValues)> {
const K: u32 = 11;
let revealed = MintRevealedValues::compute(
value,
token_id,
&randomness_value,
&randomness_token,
&serial,
&randomness_coin,
&public,
value_blind,
token_blind,
serial,
coin_blind,
public_key,
);
let coords = public_key.to_affine().coordinates().unwrap();
let c = MintContract {
value: Some(value),
token_id: Some(token_id),
randomness_value: Some(randomness_value),
randomness_token: Some(randomness_token),
pub_x: Some(*coords.x()),
pub_y: Some(*coords.y()),
value: Some(DrkValue::from_u64(value)),
asset: Some(token_id),
serial: Some(serial),
randomness_coin: Some(randomness_coin),
public: Some(public),
coin_blind: Some(coin_blind),
value_blind: Some(value_blind),
asset_blind: Some(token_blind),
};
let start = Instant::now();
let proof = groth16::create_random_proof(c, params, &mut OsRng).unwrap();
println!("Prove: [{:?}]", start.elapsed());
// TODO: Don't always build this
let pk = ProvingKey::build(K, MintContract::default());
debug!("Setup: [{:?}]", start.elapsed());
(proof, revealed)
let start = Instant::now();
let public_inputs = revealed.make_outputs();
let proof = Proof::create(&pk, &[c], &public_inputs)?;
debug!("Prove: [{:?}]", start.elapsed());
Ok((proof, revealed))
}
pub fn verify_mint_proof(
pvk: &groth16::PreparedVerifyingKey<Bls12>,
proof: &groth16::Proof<Bls12>,
vk: &VerifyingKey,
proof: &Proof,
revealed: &MintRevealedValues,
) -> bool {
let public_input = revealed.make_outputs();
let start = Instant::now();
let result = groth16::verify_proof(pvk, proof, &public_input).is_ok();
println!("Verify: [{:?}]", start.elapsed());
result
) -> Result<()> {
let public_inputs = revealed.make_outputs();
Ok(proof.verify(vk, &public_inputs)?)
}

View File

@@ -1,49 +1,36 @@
Warning: can't set `wrap_comments = true`, unstable features are only available in nightly channel.
Warning: can't set `comment_width = 100`, unstable features are only available in nightly channel.
Warning: can't set `imports_granularity = Crate`, unstable features are only available in nightly channel.
Warning: can't set `binop_separator = Back`, unstable features are only available in nightly channel.
Warning: can't set `trailing_semicolon = false`, unstable features are only available in nightly channel.
Warning: can't set `trailing_comma = Vertical`, unstable features are only available in nightly channel.
pub mod arith_chip;
pub mod coin;
pub mod constants;
pub mod diffie_hellman;
pub mod fr_serial;
pub mod merkle;
pub mod merkle_node;
pub mod merkle_node2;
pub mod mint_proof;
pub mod note;
pub mod nullifier;
pub mod pasta_serial;
pub mod proof;
pub mod schnorr;
pub mod spend_proof;
pub mod util;
use bellman::groth16;
use bls12_381::Bls12;
pub(crate) use {mint_proof::MintRevealedValues, proof::Proof, spend_proof::SpendRevealedValues};
use crate::error::Result;
pub use mint_proof::{create_mint_proof, setup_mint_prover, verify_mint_proof, MintRevealedValues};
pub use spend_proof::{
create_spend_proof, setup_spend_prover, verify_spend_proof, SpendRevealedValues,
};
use crate::types::DrkSecretKey;
#[derive(Clone)]
pub struct OwnCoin {
pub coin: coin::Coin,
pub note: note::Note,
pub secret: jubjub::Fr,
pub witness: merkle::IncrementalWitness<merkle_node::MerkleNode>,
pub secret: DrkSecretKey,
//pub witness: merkle::IncrementalWitness<merkle_node::MerkleNode>,
//pub witness: BridgeFrontier<merkle::MerkleHash, 32>,
pub nullifier: nullifier::Nullifier,
}
pub type OwnCoins = Vec<OwnCoin>;
pub fn save_params(filename: &str, params: &groth16::Parameters<Bls12>) -> Result<()> {
let buffer = std::fs::File::create(filename)?;
params.write(buffer)?;
Ok(())
}
pub fn load_params(
filename: &str,
) -> Result<(
groth16::Parameters<Bls12>,
groth16::PreparedVerifyingKey<Bls12>,
)> {
let buffer = std::fs::File::open(filename)?;
let params = groth16::Parameters::<Bls12>::read(buffer, false)?;
let pvk = groth16::prepare_verifying_key(&params.vk);
Ok((params, pvk))
}

View File

@@ -1,27 +1,34 @@
use crypto_api_chachapoly::ChachaPolyIetf;
use ff::Field;
use rand::rngs::OsRng;
use std::io;
use super::diffie_hellman::{kdf_sapling, sapling_ka_agree};
use crate::error::{Error, Result};
use crate::serial::{Decodable, Encodable, ReadExt, WriteExt};
use crypto_api_chachapoly::ChachaPolyIetf;
use pasta_curves::arithmetic::Field;
use rand::rngs::OsRng;
use super::{
diffie_hellman::{kdf_sapling, sapling_ka_agree},
util::mod_r_p,
};
use crate::{
serial::{Decodable, Encodable, ReadExt, WriteExt},
types::*,
Error, Result,
};
pub const NOTE_PLAINTEXT_SIZE: usize = 32 + // serial
8 + // value
32 + // token_id
32 + // coin_blind
32; // valcom_blind
32; // value_blind
pub const AEAD_TAG_SIZE: usize = 16;
pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
#[derive(Clone)]
pub struct Note {
pub serial: jubjub::Fr,
pub serial: DrkSerial,
pub value: u64,
pub token_id: jubjub::Fr,
pub coin_blind: jubjub::Fr,
pub valcom_blind: jubjub::Fr,
pub token_id: DrkTokenId,
pub coin_blind: DrkCoinBlind,
pub value_blind: DrkValueBlind,
}
impl Encodable for Note {
@@ -31,7 +38,7 @@ impl Encodable for Note {
len += self.value.encode(&mut s)?;
len += self.token_id.encode(&mut s)?;
len += self.coin_blind.encode(&mut s)?;
len += self.valcom_blind.encode(&mut s)?;
len += self.value_blind.encode(&mut s)?;
Ok(len)
}
}
@@ -43,17 +50,17 @@ impl Decodable for Note {
value: Decodable::decode(&mut d)?,
token_id: Decodable::decode(&mut d)?,
coin_blind: Decodable::decode(&mut d)?,
valcom_blind: Decodable::decode(d)?,
value_blind: Decodable::decode(d)?,
})
}
}
impl Note {
pub fn encrypt(&self, public: &jubjub::SubgroupPoint) -> Result<EncryptedNote> {
let ephem_secret = jubjub::Fr::random(&mut OsRng);
let ephem_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * ephem_secret;
let shared_secret = sapling_ka_agree(&ephem_secret, public.into());
let key = kdf_sapling(shared_secret, &ephem_public.into());
pub fn encrypt(&self, public: &DrkPublicKey) -> Result<EncryptedNote> {
let ephem_secret = DrkSecretKey::random(&mut OsRng);
let ephem_public = derive_public_key(ephem_secret);
let shared_secret = sapling_ka_agree(&mod_r_p(ephem_secret), public);
let key = kdf_sapling(shared_secret, &ephem_public);
let mut input = Vec::new();
self.encode(&mut input)?;
@@ -75,7 +82,7 @@ impl Note {
pub struct EncryptedNote {
ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
ephem_public: jubjub::SubgroupPoint,
ephem_public: DrkPublicKey,
}
impl Encodable for EncryptedNote {
@@ -100,9 +107,9 @@ impl Decodable for EncryptedNote {
}
impl EncryptedNote {
pub fn decrypt(&self, secret: &jubjub::Fr) -> Result<Note> {
let shared_secret = sapling_ka_agree(secret, &self.ephem_public.into());
let key = kdf_sapling(shared_secret, &self.ephem_public.into());
pub fn decrypt(&self, secret: &DrkSecretKey) -> Result<Note> {
let shared_secret = sapling_ka_agree(&mod_r_p(*secret), &self.ephem_public);
let key = kdf_sapling(shared_secret, &self.ephem_public);
let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
assert_eq!(
@@ -124,16 +131,18 @@ impl EncryptedNote {
#[test]
fn test_note_encdec() {
use crate::types::*;
let note = Note {
serial: jubjub::Fr::random(&mut OsRng),
serial: DrkSerial::random(&mut OsRng),
value: 110,
token_id: jubjub::Fr::random(&mut OsRng),
coin_blind: jubjub::Fr::random(&mut OsRng),
valcom_blind: jubjub::Fr::random(&mut OsRng),
token_id: DrkTokenId::random(&mut OsRng),
coin_blind: DrkCoinBlind::random(&mut OsRng),
value_blind: DrkValueBlind::random(&mut OsRng),
};
let secret = jubjub::Fr::random(&mut OsRng);
let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
let secret = DrkSecretKey::random(&mut OsRng);
let public = derive_public_key(secret);
let encrypted_note = note.encrypt(&public).unwrap();
let note2 = encrypted_note.decrypt(&secret).unwrap();

View File

@@ -1,31 +1,47 @@
Warning: can't set `wrap_comments = true`, unstable features are only available in nightly channel.
Warning: can't set `comment_width = 100`, unstable features are only available in nightly channel.
Warning: can't set `imports_granularity = Crate`, unstable features are only available in nightly channel.
Warning: can't set `binop_separator = Back`, unstable features are only available in nightly channel.
Warning: can't set `trailing_semicolon = false`, unstable features are only available in nightly channel.
Warning: can't set `trailing_comma = Vertical`, unstable features are only available in nightly channel.
use std::io;
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{
error::Result,
serial::{Decodable, Encodable},
serial::{Decodable, Encodable, ReadExt, WriteExt},
Result,
};
#[derive(Debug, Clone, PartialEq)]
pub struct Nullifier {
pub repr: [u8; 32],
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Nullifier(pub(crate) pallas::Base);
impl Nullifier {
pub fn new(repr: [u8; 32]) -> Self {
Self { repr }
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
pallas::Base::from_bytes(bytes).map(Nullifier).unwrap()
}
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_bytes()
}
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
}
impl Encodable for Nullifier {
fn encode<S: io::Write>(&self, s: S) -> Result<usize> {
self.repr.encode(s)
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
s.write_slice(&self.to_bytes()[..])?;
Ok(32)
}
}
impl Decodable for Nullifier {
fn decode<D: io::Read>(d: D) -> Result<Self> {
Ok(Self {
repr: Decodable::decode(d)?,
})
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut bytes = [0u8; 32];
d.read_slice(&mut bytes)?;
let result = Self::from_bytes(&bytes);
Ok(result)
}
}

View File

@@ -1,17 +1,21 @@
use group::GroupEncoding;
use std::io;
use crate::error::{Error, Result};
use crate::serial::{Decodable, Encodable, ReadExt, WriteExt};
use pasta_curves as pasta;
use pasta_curves::{arithmetic::FieldExt, group::GroupEncoding};
impl Encodable for jubjub::Fr {
use crate::{
serial::{Decodable, Encodable, ReadExt, WriteExt},
Error, Result,
};
impl Encodable for pasta::Fp {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
s.write_slice(&self.to_bytes()[..])?;
Ok(32)
}
}
impl Decodable for jubjub::Fr {
impl Decodable for pasta::Fp {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut bytes = [0u8; 32];
d.read_slice(&mut bytes)?;
@@ -24,14 +28,34 @@ impl Decodable for jubjub::Fr {
}
}
impl Encodable for jubjub::SubgroupPoint {
impl Encodable for pasta::Fq {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
s.write_slice(&self.to_bytes()[..])?;
Ok(32)
}
}
impl Decodable for jubjub::SubgroupPoint {
impl Decodable for pasta::Fq {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut bytes = [0u8; 32];
d.read_slice(&mut bytes)?;
let result = Self::from_bytes(&bytes);
if result.is_some().into() {
Ok(result.unwrap())
} else {
Err(Error::BadOperationType)
}
}
}
impl Encodable for pasta::Ep {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
s.write_slice(&self.to_bytes()[..])?;
Ok(32)
}
}
impl Decodable for pasta::Ep {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut bytes = [0u8; 32];
d.read_slice(&mut bytes)?;

109
src/crypto/proof.rs Normal file
View File

@@ -0,0 +1,109 @@
use std::io;
// TODO: Alias vesta::Affine to something
use halo2::{
plonk,
plonk::Circuit,
poly::commitment,
transcript::{Blake2bRead, Blake2bWrite},
};
use pasta_curves::vesta;
use crate::serial::{Decodable, Encodable, ReadExt, VarInt, WriteExt};
use crate::types::*;
use crate::Result;
#[derive(Debug)]
pub struct VerifyingKey {
pub params: commitment::Params<vesta::Affine>,
pub vk: plonk::VerifyingKey<vesta::Affine>,
}
impl VerifyingKey {
pub fn build(k: u32, c: impl Circuit<DrkCircuitField>) -> Self {
let params = commitment::Params::new(k);
let vk = plonk::keygen_vk(&params, &c).unwrap();
VerifyingKey { params, vk }
}
}
#[derive(Debug)]
pub struct ProvingKey {
pub params: commitment::Params<vesta::Affine>,
pub pk: plonk::ProvingKey<vesta::Affine>,
}
impl ProvingKey {
pub fn build(k: u32, c: impl Circuit<DrkCircuitField>) -> Self {
let params = commitment::Params::new(k);
let vk = plonk::keygen_vk(&params, &c).unwrap();
let pk = plonk::keygen_pk(&params, vk, &c).unwrap();
ProvingKey { params, pk }
}
}
#[derive(Clone, Debug)]
pub struct Proof(Vec<u8>);
impl AsRef<[u8]> for Proof {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Proof {
pub fn create(
pk: &ProvingKey,
circuits: &[impl Circuit<DrkCircuitField>],
pubinputs: &[DrkCircuitField],
) -> std::result::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()))
}
pub fn verify(
&self,
vk: &VerifyingKey,
pubinputs: &[DrkCircuitField],
) -> std::result::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)
}
}
pub fn new(bytes: Vec<u8>) -> Self {
Proof(bytes)
}
}
impl Encodable for Proof {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
s.write_slice(self.as_ref())?;
Ok(self.as_ref().len())
}
}
impl Decodable for Proof {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let len = VarInt::decode(&mut d)?.0 as usize;
let mut r = vec![0u8; len];
d.read_slice(&mut r)?;
Ok(Proof::new(r))
}
}

View File

@@ -1,41 +1,52 @@
use ff::Field;
use group::GroupEncoding;
use rand::rngs::OsRng;
use std::io;
use super::util::hash_to_scalar;
use crate::error::Result;
use crate::serial::{Decodable, Encodable};
use halo2_gadgets::ecc::FixedPoints;
use pasta_curves::{arithmetic::Field, group::GroupEncoding, pallas};
use rand::rngs::OsRng;
pub struct SecretKey(pub jubjub::Fr);
use super::{
constants::{OrchardFixedBases, DRK_SCHNORR_DOMAIN},
util::{hash_to_scalar, mod_r_p},
};
use crate::{
error::Result,
serial::{Decodable, Encodable},
types::{
derive_public_key, DrkCoinBlind, DrkPublicKey, DrkSecretKey, DrkSerial, DrkTokenId,
DrkValueBlind, DrkValueCommit,
},
};
#[derive(Clone)]
pub struct SecretKey(pub pallas::Scalar);
impl SecretKey {
pub fn random() -> Self {
Self(jubjub::Fr::random(&mut OsRng))
Self(pallas::Scalar::random(&mut OsRng))
}
pub fn sign(&self, message: &[u8]) -> Signature {
let mask = jubjub::Fr::random(&mut OsRng);
let commit = zcash_primitives::constants::SPENDING_KEY_GENERATOR * mask;
let challenge = hash_to_scalar(b"DarkFi_Schnorr", &commit.to_bytes(), message);
let mask = DrkValueBlind::random(&mut OsRng);
let commit = OrchardFixedBases::SpendAuthG.generator() * mask;
let challenge = hash_to_scalar(DRK_SCHNORR_DOMAIN, &commit.to_bytes(), message);
let response = mask + challenge * self.0;
Signature { commit, response }
}
pub fn public_key(&self) -> PublicKey {
let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * self.0;
PublicKey(public)
let public_key = OrchardFixedBases::SpendAuthG.generator() * self.0;
PublicKey(public_key)
}
}
pub struct PublicKey(pub jubjub::SubgroupPoint);
#[derive(PartialEq)]
pub struct PublicKey(pub DrkPublicKey);
pub struct Signature {
commit: jubjub::SubgroupPoint,
response: jubjub::Fr,
commit: DrkValueCommit,
response: DrkValueBlind,
}
impl Encodable for Signature {
@@ -58,13 +69,24 @@ impl Decodable for Signature {
impl PublicKey {
pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
let challenge = hash_to_scalar(b"DarkFi_Schnorr", &signature.commit.to_bytes(), message);
zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature.response
- self.0 * challenge
let challenge = hash_to_scalar(DRK_SCHNORR_DOMAIN, &signature.commit.to_bytes(), message);
OrchardFixedBases::SpendAuthG.generator() * signature.response - self.0 * challenge
== signature.commit
}
}
impl Encodable for PublicKey {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
Ok(self.0.encode(s)?)
}
}
impl Decodable for PublicKey {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
Ok(Self(Decodable::decode(&mut d)?))
}
}
#[test]
fn test_schnorr() {
let secret = SecretKey::random();

View File

@@ -1,176 +1,114 @@
use bellman::gadgets::multipack;
use bellman::groth16;
use blake2s_simd::Params as Blake2sParams;
use bls12_381::Bls12;
use ff::PrimeField;
use group::{Curve, GroupEncoding};
use rand::rngs::OsRng;
use std::io;
use std::time::Instant;
use super::merkle_node::{merkle_hash, MerkleNode, SAPLING_COMMITMENT_TREE_DEPTH};
use super::nullifier::Nullifier;
use crate::circuit::spend_contract::SpendContract;
use crate::error::Result;
use crate::serial::{Decodable, Encodable};
use halo2_gadgets::{
primitives,
primitives::poseidon::{ConstantLength, P128Pow5T3},
};
use incrementalmerkletree::Hashable;
use log::debug;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
group::Curve,
pallas,
};
use super::{
nullifier::Nullifier,
proof::{Proof, ProvingKey, VerifyingKey},
util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
};
use crate::{
circuit::spend_contract::SpendContract,
crypto::{merkle_node2::MerkleNode, schnorr},
serial::{Decodable, Encodable},
types::*,
Result,
};
pub struct SpendRevealedValues {
pub value_commit: jubjub::SubgroupPoint,
pub token_commit: jubjub::SubgroupPoint,
pub value_commit: DrkValueCommit,
pub token_commit: DrkValueCommit,
pub nullifier: Nullifier,
// This should not be here, we just have it for debugging
//coin: [u8; 32],
pub merkle_root: MerkleNode,
pub signature_public: jubjub::SubgroupPoint,
pub signature_public: schnorr::PublicKey,
}
impl SpendRevealedValues {
#[allow(clippy::too_many_arguments)]
fn compute(
value: u64,
token_id: jubjub::Fr,
randomness_value: &jubjub::Fr,
randomness_token: &jubjub::Fr,
serial: &jubjub::Fr,
randomness_coin: &jubjub::Fr,
secret: &jubjub::Fr,
merkle_path: &[(bls12_381::Scalar, bool)],
signature_secret: &jubjub::Fr,
token_id: DrkTokenId,
value_blind: DrkValueBlind,
token_blind: DrkValueBlind,
serial: DrkSerial,
coin_blind: DrkCoinBlind,
secret: DrkSecretKey,
leaf_position: incrementalmerkletree::Position,
merkle_path: Vec<MerkleNode>,
signature_secret: schnorr::SecretKey,
) -> Self {
let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
* jubjub::Fr::from(value))
+ (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
* randomness_value);
let nullifier = [secret, serial];
let nullifier =
primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(nullifier);
let token_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
* token_id)
+ (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
* randomness_token);
let public_key = derive_public_key(secret);
let coords = public_key.to_affine().coordinates().unwrap();
let messages = [
[*coords.x(), *coords.y()],
[DrkValue::from_u64(value), token_id],
[serial, coin_blind],
];
let mut nullifier = [0; 32];
nullifier.copy_from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(zcash_primitives::constants::PRF_NF_PERSONALIZATION)
.to_state()
.update(&secret.to_bytes())
.update(&serial.to_bytes())
.finalize()
.as_bytes(),
);
let nullifier = Nullifier::new(nullifier);
let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
let signature_public =
zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature_secret;
let mut coin = [0; 32];
coin.copy_from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(zcash_primitives::constants::CRH_IVK_PERSONALIZATION)
.to_state()
.update(&public.to_bytes())
.update(&value.to_le_bytes())
.update(&token_id.to_bytes())
.update(&serial.to_bytes())
.update(&randomness_coin.to_bytes())
.finalize()
.as_bytes(),
);
let merkle_root =
jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash(
zcash_primitives::pedersen_hash::Personalization::NoteCommitment,
multipack::bytes_to_bits_le(&coin),
));
let affine = merkle_root.to_affine();
let mut merkle_root = affine.get_u();
for (i, (right, is_right)) in merkle_path.iter().enumerate() {
if *is_right {
merkle_root = merkle_hash(i, &right.to_repr(), &merkle_root.to_repr());
} else {
merkle_root = merkle_hash(i, &merkle_root.to_repr(), &right.to_repr());
}
let mut coin = DrkCoin::zero();
for msg in messages.iter() {
coin += primitives::poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash(*msg);
}
let merkle_root = MerkleNode::new(merkle_root.to_repr());
let merkle_root = {
let position: u64 = leaf_position.into();
let mut current = MerkleNode(coin);
for (level, sibling) in merkle_path.iter().enumerate() {
let level = level as u8;
current = if position & (1 << level) == 0 {
MerkleNode::combine(level.into(), &current, sibling)
} else {
MerkleNode::combine(level.into(), sibling, &current)
};
}
current
};
let value_commit = pedersen_commitment_u64(value, value_blind);
let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
SpendRevealedValues {
value_commit,
token_commit,
nullifier,
nullifier: Nullifier(nullifier),
merkle_root,
signature_public,
signature_public: signature_secret.public_key(),
}
}
fn make_outputs(&self) -> [bls12_381::Scalar; 9] {
let mut public_input = [bls12_381::Scalar::zero(); 9];
fn make_outputs(&self) -> [DrkCircuitField; 8] {
let value_coords = self.value_commit.to_affine().coordinates().unwrap();
let token_coords = self.token_commit.to_affine().coordinates().unwrap();
let merkle_root = self.merkle_root.0;
let sig_coords = self.signature_public.0.to_affine().coordinates().unwrap();
// CV
{
let result = jubjub::ExtendedPoint::from(self.value_commit);
let affine = result.to_affine();
//let (u, v) = (affine.get_u(), affine.get_v());
let u = affine.get_u();
let v = affine.get_v();
public_input[0] = u;
public_input[1] = v;
}
// CA
{
let result = jubjub::ExtendedPoint::from(self.token_commit);
let affine = result.to_affine();
//let (u, v) = (affine.get_u(), affine.get_v());
let u = affine.get_u();
let v = affine.get_v();
public_input[2] = u;
public_input[3] = v;
}
// NF
{
// Pack the hash as inputs for proof verification.
let hash = multipack::bytes_to_bits_le(&self.nullifier.repr);
let hash = multipack::compute_multipacking(&hash);
// There are 2 chunks for a blake hash
assert_eq!(hash.len(), 2);
public_input[4] = hash[0];
public_input[5] = hash[1];
}
// Not revealed. We leave this code here for debug
// Coin
/*{
// Pack the hash as inputs for proof verification.
let hash = multipack::bytes_to_bits_le(&self.coin);
let hash = multipack::compute_multipacking(&hash);
// There are 2 chunks for a blake hash
assert_eq!(hash.len(), 2);
public_input[4] = hash[0];
public_input[5] = hash[1];
}*/
public_input[6] = self.merkle_root.into();
{
let result = jubjub::ExtendedPoint::from(self.signature_public);
let affine = result.to_affine();
//let (u, v) = (affine.get_u(), affine.get_v());
let u = affine.get_u();
let v = affine.get_v();
public_input[7] = u;
public_input[8] = v;
}
public_input
vec![
self.nullifier.inner(),
*value_coords.x(),
*value_coords.y(),
*token_coords.x(),
*token_coords.y(),
merkle_root,
*sig_coords.x(),
*sig_coords.y(),
]
.try_into()
.unwrap()
}
}
@@ -198,93 +136,68 @@ impl Decodable for SpendRevealedValues {
}
}
pub fn setup_spend_prover() -> groth16::Parameters<Bls12> {
println!("Spend: Making random params...");
let start = Instant::now();
let params = {
let c = SpendContract {
value: None,
token_id: None,
randomness_value: None,
randomness_token: None,
serial: None,
randomness_coin: None,
secret: None,
branch: [None; SAPLING_COMMITMENT_TREE_DEPTH],
is_right: [None; SAPLING_COMMITMENT_TREE_DEPTH],
signature_secret: None,
};
groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
};
println!("Setup: [{:?}]", start.elapsed());
params
}
#[allow(clippy::too_many_arguments)]
pub fn create_spend_proof(
params: &groth16::Parameters<Bls12>,
value: u64,
token_id: jubjub::Fr,
randomness_value: jubjub::Fr,
randomness_token: jubjub::Fr,
serial: jubjub::Fr,
randomness_coin: jubjub::Fr,
secret: jubjub::Fr,
merkle_path: Vec<(bls12_381::Scalar, bool)>,
signature_secret: jubjub::Fr,
) -> (groth16::Proof<Bls12>, SpendRevealedValues) {
assert_eq!(merkle_path.len(), SAPLING_COMMITMENT_TREE_DEPTH);
let mut branch: [_; SAPLING_COMMITMENT_TREE_DEPTH] = Default::default();
let mut is_right: [_; SAPLING_COMMITMENT_TREE_DEPTH] = Default::default();
for (i, (branch_i, is_right_i)) in merkle_path.iter().enumerate() {
branch[i] = Some(*branch_i);
is_right[i] = Some(*is_right_i);
}
let c = SpendContract {
value: Some(value),
token_id: Some(token_id),
randomness_value: Some(randomness_value),
randomness_token: Some(randomness_token),
serial: Some(serial),
randomness_coin: Some(randomness_coin),
secret: Some(secret),
branch,
is_right,
signature_secret: Some(signature_secret),
};
let start = Instant::now();
let proof = groth16::create_random_proof(c, params, &mut OsRng).unwrap();
println!("Prove: [{:?}]", start.elapsed());
token_id: DrkTokenId,
value_blind: DrkValueBlind,
token_blind: DrkValueBlind,
serial: DrkSerial,
coin_blind: DrkCoinBlind,
secret: DrkSecretKey,
leaf_position: incrementalmerkletree::Position,
merkle_path: Vec<MerkleNode>,
signature_secret: schnorr::SecretKey,
) -> Result<(Proof, SpendRevealedValues)> {
const K: u32 = 11;
let revealed = SpendRevealedValues::compute(
value,
token_id,
&randomness_value,
&randomness_token,
&serial,
&randomness_coin,
&secret,
&merkle_path,
&signature_secret,
value_blind,
token_blind,
serial,
coin_blind,
secret,
leaf_position,
merkle_path.clone(),
signature_secret.clone(),
);
(proof, revealed)
let merkle_path: Vec<pallas::Base> = merkle_path.iter().map(|node| node.0).collect();
let leaf_position: u64 = leaf_position.into();
let c = SpendContract {
secret_key: Some(secret),
serial: Some(serial),
value: Some(DrkValue::from_u64(value)),
asset: Some(token_id),
coin_blind: Some(coin_blind),
value_blind: Some(value_blind),
asset_blind: Some(token_blind),
leaf_pos: Some(leaf_position as u32),
merkle_path: Some(merkle_path.try_into().unwrap()),
sig_secret: Some(signature_secret.0),
};
let start = Instant::now();
// TODO: Don't always build this
let pk = ProvingKey::build(K, SpendContract::default());
debug!("Setup: [{:?}]", start.elapsed());
let start = Instant::now();
let public_inputs = revealed.make_outputs();
let proof = Proof::create(&pk, &[c], &public_inputs)?;
debug!("Prove: [{:?}]", start.elapsed());
Ok((proof, revealed))
}
pub fn verify_spend_proof(
pvk: &groth16::PreparedVerifyingKey<Bls12>,
proof: &groth16::Proof<Bls12>,
vk: &VerifyingKey,
proof: Proof,
revealed: &SpendRevealedValues,
) -> bool {
let public_input = revealed.make_outputs();
let start = Instant::now();
let result = groth16::verify_proof(pvk, proof, &public_input).is_ok();
println!("Verify: [{:?}]", start.elapsed());
result
) -> Result<()> {
let public_inputs = revealed.make_outputs();
Ok(proof.verify(vk, &public_inputs)?)
}

View File

@@ -1,9 +1,40 @@
use blake2b_simd::Params;
use pasta_curves::{
arithmetic::{CurveExt, FieldExt},
group::ff::PrimeField,
pallas,
};
pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr {
use super::constants::fixed_bases::{
VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
};
use crate::types::*;
pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> DrkScalar {
let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
hasher.update(a);
hasher.update(b);
let ret = hasher.finalize();
jubjub::Fr::from_bytes_wide(ret.as_array())
DrkScalar::from_bytes_wide(ret.as_array())
}
#[allow(non_snake_case)]
pub fn pedersen_commitment_scalar(value: DrkScalar, blind: DrkValueBlind) -> DrkValueCommit {
let hasher = DrkValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let V = hasher(&VALUE_COMMITMENT_V_BYTES);
let R = hasher(&VALUE_COMMITMENT_R_BYTES);
V * value + R * blind
}
pub fn pedersen_commitment_u64(value: u64, blind: DrkValueBlind) -> DrkValueCommit {
pedersen_commitment_scalar(mod_r_p(DrkValue::from_u64(value)), blind)
}
/// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
///
/// This requires no modular reduction because Pallas' base field is smaller than its
/// scalar field.
pub fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
pallas::Scalar::from_repr(x.to_repr()).unwrap()
}

View File

@@ -1,154 +1,169 @@
use std::fmt;
use crate::client;
use crate::state;
use crate::vm::ZkVmError;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, thiserror::Error)]
#[derive(Debug, Clone)]
pub enum Error {
#[error("io error: `{0:?}`")]
Io(std::io::ErrorKind),
#[error("Cannot find home directory")]
PathNotFound,
/// VarInt was encoded in a non-minimal way
#[error("non-minimal varint")]
PathNotFound,
NonMinimalVarInt,
/// Parsing And Encode/Decode errors
#[error("parse failed: `{0}`")]
ParseFailed(&'static str),
#[error(transparent)]
ParseIntError(#[from] std::num::ParseIntError),
#[error(transparent)]
ParseBigIntError(#[from] num_bigint::ParseBigIntError),
#[error(transparent)]
ParseFloatError(#[from] std::num::ParseFloatError),
#[error(transparent)]
FromHexError(#[from] hex::FromHexError),
#[error("Url parse erro `{0}`")]
UrlParseError(String),
#[error("No url found")]
NoUrlFound,
#[error("Malformed packet")]
ParseIntError,
ParseBigIntError,
ParseFloatError,
FromHexError,
UrlParseError,
MalformedPacket,
#[error(transparent)]
AddrParseError(#[from] std::net::AddrParseError),
#[error(transparent)]
Base58EncodeError(#[from] bs58::encode::Error),
#[error(transparent)]
Base58DecodeError(#[from] bs58::decode::Error),
#[error(transparent)]
Utf8Error(#[from] std::string::FromUtf8Error),
#[error(transparent)]
StrUtf8Error(#[from] std::str::Utf8Error),
#[error("TryInto error")]
AddrParseError,
Base58EncodeError(String),
Base58DecodeError(String),
Utf8Error,
StrUtf8Error(String),
TryIntoError,
#[error("TryFrom error")]
TryFromError,
#[error(transparent)]
TryFromBigIntError(#[from] num_bigint::TryFromBigIntError<num_bigint::BigUint>),
#[error("Json serialization error: `{0}`")]
TryFromBigIntError,
SerdeJsonError(String),
#[error(transparent)]
TomlDeserializeError(#[from] toml::de::Error),
#[error(transparent)]
TomlSerializeError(#[from] toml::ser::Error),
TomlDeserializeError(String),
TomlSerializeError(String),
/// Contract
#[error("Bad variable ref type byte")]
BadVariableRefType,
#[error("Bad operation type byte")]
BadOperationType,
#[error("Bad constraint type byte")]
BadConstraintType,
#[error("Invalid param name")]
InvalidParamName,
#[error("Missing params")]
InvalidParamType,
MissingParams,
#[error(transparent)]
VmError(#[from] ZkVmError),
#[error("Contract is poorly defined")]
VmError,
BadContract,
#[error("Groth16 Error: `{0}`")]
Groth16Error(String),
#[error("Operation failed")]
Groth16Error,
PlonkError,
OperationFailed,
#[error("Unable to decrypt mint note")]
NoteDecryptionFailed,
#[error(transparent)]
VerifyFailed(#[from] state::VerifyFailed),
#[error("MerkleTree is full")]
VerifyFailed,
TreeFull,
/// Service
#[error("Services Error: `{0}`")]
ServicesError(&'static str),
#[error("Client failed: `{0}`")]
ClientFailed(String),
#[cfg(feature = "btc")]
#[error(transparent)]
BtcFailed(#[from] crate::service::BtcFailed),
BtcFailed(String),
#[cfg(feature = "sol")]
#[error("Sol client failed: `{0}`")]
SolFailed(String),
#[cfg(feature = "eth")]
#[error(transparent)]
EthFailed(#[from] crate::service::EthFailed),
#[error("BridgeError Error: `{0}`")]
EthFailed(String),
BridgeError(String),
#[error("ZmqError: `{0}`")]
ZmqError(String),
/// Database/Sql errors
#[error("Rocksdb error: `{0}`")]
RocksdbError(String),
#[error("Rusqlite error: `{0}`")]
RusqliteError(String),
#[error("SlabsStore Error: `{0}`")]
SlabsStore(String),
/// RPC errors
#[error("JsonRpc Error: `{0}`")]
JsonRpcError(String),
#[error("Not supported network")]
NotSupportedNetwork,
#[error("Not supported token")]
NotSupportedToken,
#[error("Could not parse token parameter")]
TokenParseError,
#[error("Cannot parse network parameter")]
NetworkParseError,
#[error("Async_Native_TLS error: `{0}`")]
AsyncNativeTlsError(String),
#[error("TungsteniteError: `{0}`")]
TungsteniteError(String),
AsyncNativeTlsError,
TungsteniteError,
/// Network
#[error("Connection failed")]
ConnectFailed,
#[error("Connection timed out")]
ConnectTimeout,
#[error("Channel stopped")]
ChannelStopped,
#[error("Channel timed out")]
ChannelTimeout,
#[error("Service stopped")]
ServiceStopped,
/// Util
#[error("No config file detected. Please create one.")]
ConfigNotFound,
#[error("No keypair file detected.")]
KeypairPathNotFound,
#[error("No cashier public keys detected.")]
CashierKeysNotFound,
#[error("SetLoggerError")]
SetLoggerError,
#[error("Async_channel sender error")]
AsyncChannelSenderError,
#[error(transparent)]
AsyncChannelReceiverError(#[from] async_channel::RecvError),
AsyncChannelReceiverError,
}
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::ParseBigIntError => f.write_str("Parse big int error"),
Error::ParseFloatError => f.write_str("Parse float error"),
Error::UrlParseError => f.write_str("Failed to parse URL"),
Error::FromHexError => f.write_str("Failed to convert from hex"),
Error::AsyncChannelSenderError => f.write_str("Async_channel sender error"),
Error::AsyncChannelReceiverError => f.write_str("Async_channel receiver error"),
Error::AsyncNativeTlsError => f.write_str("Async_Native_TLS 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::PlonkError => f.write_str("Plonk 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::ClientFailed(ref err) => write!(f, "Client failed: {}", err),
#[cfg(feature = "btc")]
Error::BtcFailed(ref err) => write!(f, "Btc client failed: {}", err),
#[cfg(feature = "sol")]
Error::SolFailed(ref err) => write!(f, "Sol client failed: {}", err),
#[cfg(feature = "eth")]
Error::EthFailed(ref err) => write!(f, "Eth client failed: {}", err),
Error::TryIntoError => f.write_str("TryInto error"),
Error::TryFromError => f.write_str("TryFrom error"),
Error::TryFromBigIntError => f.write_str("TryFromBigInt error"),
Error::RocksdbError(ref err) => write!(f, "Rocksdb Error: {}", err),
Error::SlabsStore(ref err) => write!(f, "SlabsStore Error: {}", err),
Error::JsonRpcError(ref err) => write!(f, "JsonRpc Error: {}", err),
Error::TreeFull => f.write_str("MerkleTree is full"),
Error::NotSupportedNetwork => f.write_str("Not supported network"),
Error::NotSupportedToken => f.write_str("Not supported token"),
Error::BridgeError(ref err) => write!(f, "Bridge error: {}", err),
Error::SerdeJsonError(ref err) => write!(f, "Json serialization error: {}", err),
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::ConfigNotFound => f.write_str("No config file detected. Please create one."),
Error::KeypairPathNotFound => f.write_str("No keypair file detected."),
Error::CashierKeysNotFound => f.write_str("No cashier public keys detected."),
Error::SetLoggerError => f.write_str("SetLoggerError"),
Error::TokenParseError => f.write_str("Could not parse token parameter"),
Error::TungsteniteError => f.write_str("TungsteniteError"),
Error::NetworkParseError => f.write_str("Cannot parse network parameter"),
}
}
}
impl From<zeromq::ZmqError> for Error {
@@ -163,12 +178,6 @@ impl From<rocksdb::Error> for Error {
}
}
impl From<rusqlite::Error> for Error {
fn from(err: rusqlite::Error) -> Error {
Error::RusqliteError(err.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Error {
Error::SerdeJsonError(err.to_string())
@@ -181,9 +190,9 @@ impl From<std::io::Error> for Error {
}
}
impl From<client::ClientFailed> for Error {
fn from(err: client::ClientFailed) -> Error {
Error::ClientFailed(err.to_string())
impl From<rusqlite::Error> for Error {
fn from(err: rusqlite::Error) -> Error {
Error::RusqliteError(err.to_string())
}
}
@@ -193,26 +202,88 @@ impl<T> From<async_channel::SendError<T>> for Error {
}
}
impl From<log::SetLoggerError> for Error {
fn from(_err: log::SetLoggerError) -> Error {
Error::SetLoggerError
impl From<async_channel::RecvError> for Error {
fn from(_err: async_channel::RecvError) -> Error {
Error::AsyncChannelReceiverError
}
}
impl From<tungstenite::Error> for Error {
fn from(err: tungstenite::Error) -> Error {
Error::TungsteniteError(err.to_string())
impl From<async_native_tls::Error> for Error {
fn from(_err: async_native_tls::Error) -> Error {
Error::AsyncNativeTlsError
}
}
impl From<async_native_tls::Error> for Error {
fn from(err: async_native_tls::Error) -> Error {
Error::AsyncNativeTlsError(err.to_string())
impl From<std::net::AddrParseError> for Error {
fn from(_err: std::net::AddrParseError) -> Error {
Error::AddrParseError
}
}
impl From<url::ParseError> for Error {
fn from(err: url::ParseError) -> Error {
Error::UrlParseError(err.to_string())
fn from(_err: url::ParseError) -> Error {
Error::UrlParseError
}
}
impl From<hex::FromHexError> for Error {
fn from(_err: hex::FromHexError) -> Error {
Error::FromHexError
}
}
impl From<std::num::ParseIntError> for Error {
fn from(_err: std::num::ParseIntError) -> Error {
Error::ParseIntError
}
}
impl From<num_bigint::ParseBigIntError> for Error {
fn from(_err: num_bigint::ParseBigIntError) -> Error {
Error::ParseBigIntError
}
}
impl From<num_bigint::TryFromBigIntError<num_bigint::BigUint>> for Error {
fn from(_err: num_bigint::TryFromBigIntError<num_bigint::BigUint>) -> Error {
Error::TryFromBigIntError
}
}
impl From<std::num::ParseFloatError> for Error {
fn from(_err: std::num::ParseFloatError) -> Error {
Error::ParseFloatError
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(_err: std::string::FromUtf8Error) -> Error {
Error::Utf8Error
}
}
impl From<std::str::Utf8Error> for Error {
fn from(err: std::str::Utf8Error) -> Error {
Error::StrUtf8Error(err.to_string())
}
}
impl From<state::VerifyFailed> for Error {
fn from(_err: state::VerifyFailed) -> Error {
Error::VerifyFailed
}
}
impl From<client::ClientFailed> for Error {
fn from(err: client::ClientFailed) -> Error {
Error::ClientFailed(err.to_string())
}
}
#[cfg(feature = "btc")]
impl From<crate::service::BtcFailed> for Error {
fn from(err: crate::service::BtcFailed) -> Error {
Error::BtcFailed(err.to_string())
}
}
@@ -223,8 +294,51 @@ impl From<crate::service::SolFailed> for Error {
}
}
impl From<bellman::SynthesisError> for Error {
fn from(err: bellman::SynthesisError) -> Error {
Error::Groth16Error(err.to_string())
#[cfg(feature = "eth")]
impl From<crate::service::EthFailed> for Error {
fn from(err: crate::service::EthFailed) -> Error {
Error::EthFailed(err.to_string())
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Error {
Error::TomlDeserializeError(err.to_string())
}
}
impl From<toml::ser::Error> for Error {
fn from(err: toml::ser::Error) -> Error {
Error::TomlSerializeError(err.to_string())
}
}
impl From<bs58::encode::Error> for Error {
fn from(err: bs58::encode::Error) -> Error {
Error::Base58EncodeError(err.to_string())
}
}
impl From<bs58::decode::Error> for Error {
fn from(err: bs58::decode::Error) -> Error {
Error::Base58DecodeError(err.to_string())
}
}
impl From<log::SetLoggerError> for Error {
fn from(_err: log::SetLoggerError) -> Error {
Error::SetLoggerError
}
}
impl From<tungstenite::Error> for Error {
fn from(_err: tungstenite::Error) -> Error {
Error::TungsteniteError
}
}
impl From<halo2::plonk::Error> for Error {
fn from(_err: halo2::plonk::Error) -> Error {
Error::PlonkError
}
}

View File

@@ -1,11 +1,5 @@
use std::collections::{HashMap, HashSet};
use bellman::groth16;
use bls12_381::{Bls12, Scalar};
pub mod async_serial;
pub mod blockchain;
pub mod bls_extensions;
pub mod circuit;
pub mod cli;
pub mod client;
@@ -19,111 +13,10 @@ pub mod service;
pub mod state;
pub mod system;
pub mod tx;
pub mod types;
pub mod util;
pub mod vm;
pub mod vm_serial;
pub mod vm2;
pub mod vm2_serial;
pub mod wallet;
pub use crate::bls_extensions::BlsStringConversion;
pub use crate::error::{Error, Result};
pub use crate::net::p2p::P2p;
pub use crate::serial::{Decodable, Encodable};
pub use crate::vm::{
AllocType, ConstraintInstruction, CryptoOperation, VariableIndex, VariableRef,
ZkVirtualMachine, ZkVmCircuit,
};
pub type Bytes = Vec<u8>;
pub struct ZkContract {
pub name: String,
pub vm: ZkVirtualMachine,
params_map: HashMap<String, VariableIndex>,
pub params: HashMap<VariableIndex, Scalar>,
public_map: bimap::BiMap<String, VariableIndex>,
}
pub struct ZkProof {
pub public: HashMap<String, Scalar>,
pub proof: groth16::Proof<Bls12>,
}
impl ZkContract {
// Just have a load() and save()
// Load the contract, do the setup, save it...
pub fn setup(&mut self, filename: &str) -> Result<()> {
self.vm.setup()?;
let buffer = std::fs::File::create(filename)?;
self.vm.params.as_ref().unwrap().write(buffer)?;
Ok(())
}
pub fn load_setup(&mut self, filename: &str) -> Result<()> {
let buffer = std::fs::File::open(filename)?;
let setup = groth16::Parameters::<Bls12>::read(buffer, false)?;
let vk = groth16::prepare_verifying_key(&setup.vk);
self.vm.params = Some(setup);
self.vm.verifying_key = Some(vk);
Ok(())
}
pub fn param_names(&self) -> Vec<String> {
self.params_map.keys().cloned().collect()
}
pub fn set_param(&mut self, name: &str, value: Scalar) -> Result<()> {
match self.params_map.get(name) {
Some(index) => {
self.params.insert(*index, value);
Ok(())
}
None => Err(Error::InvalidParamName),
}
}
pub fn prove(&mut self) -> Result<ZkProof> {
// Error if params not all set
let user_params: HashSet<_> = self.params.keys().collect();
let req_params: HashSet<_> = self.params_map.values().collect();
if user_params != req_params {
return Err(Error::MissingParams);
}
// execute
let params = std::mem::take(&mut self.params);
self.vm.initialize(&params.into_iter().collect())?;
// prove
let proof = self.vm.prove();
let mut public = HashMap::new();
for (index, value) in self.vm.public() {
match self.public_map.get_by_right(&index) {
Some(name) => {
public.insert(name.clone(), value);
}
None => return Err(Error::BadContract),
}
}
// return proof and public values (Hashmap string -> scalars)
Ok(ZkProof { public, proof })
}
pub fn verify(&self, proof: &ZkProof) -> bool {
let mut public = vec![];
for (name, value) in &proof.public {
match self.public_map.get_by_left(name) {
Some(index) => {
public.push((index, *value));
}
None => return false,
}
}
public.sort_by(|a, b| a.0.partial_cmp(b.0).unwrap());
let (_, public): (Vec<VariableIndex>, Vec<Scalar>) = public.into_iter().unzip();
// Takes proof and public values
self.vm.verify(&proof.proof, &public)
}
}

View File

@@ -3,8 +3,6 @@ use std::io::{Cursor, Read, Write};
use std::net::{IpAddr, SocketAddr};
use std::{io, mem};
use bls12_381 as bls;
use crate::endian;
use crate::error::{Error, Result};
@@ -446,7 +444,7 @@ macro_rules! impl_vec {
}
};
}
impl_vec!(bls::Scalar);
impl_vec!(SocketAddr);
impl_vec!([u8; 32]);
@@ -542,29 +540,26 @@ impl Decodable for Box<[u8]> {
// 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)
}
}
($($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 }),*))
}
}
);
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);

View File

@@ -1,7 +1,7 @@
use async_std::sync::{Arc, Mutex};
use std::collections::HashMap;
use async_executor::Executor;
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use futures::stream::FuturesUnordered;
use futures::stream::StreamExt;
@@ -9,7 +9,7 @@ use log::*;
use crate::util::NetworkName;
use crate::wallet::cashierdb::TokenKey;
use crate::{Error, Result};
use crate::{types::*, Error, Result};
pub struct BridgeRequests {
pub network: NetworkName,
@@ -55,8 +55,8 @@ pub struct TokenSubscribtion {
#[derive(Debug)]
pub struct TokenNotification {
pub network: NetworkName,
pub token_id: jubjub::Fr,
pub drk_pub_key: jubjub::SubgroupPoint,
pub token_id: DrkTokenId,
pub drk_pub_key: DrkPublicKey,
pub received_balance: u64,
pub decimals: u16,
}
@@ -115,7 +115,7 @@ impl Bridge {
pub async fn subscribe(
self: Arc<Self>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint: Option<String>,
executor: Arc<Executor<'_>>,
) -> BridgeSubscribtion {
@@ -134,7 +134,7 @@ impl Bridge {
self: Arc<Self>,
req: async_channel::Receiver<BridgeRequests>,
rep: async_channel::Sender<BridgeResponse>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<()> {
@@ -238,7 +238,7 @@ impl Bridge {
pub trait NetworkClient {
async fn subscribe(
self: Arc<Self>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<TokenSubscribtion>;
@@ -248,7 +248,7 @@ pub trait NetworkClient {
self: Arc<Self>,
private_key: Vec<u8>,
public_key: Vec<u8>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<String>;

View File

@@ -1,4 +1,3 @@
use async_std::sync::{Arc, Mutex};
use std::cmp::max;
use std::collections::BTreeMap;
use std::convert::{From, TryFrom, TryInto};
@@ -9,6 +8,7 @@ use std::time::{Duration, Instant};
use anyhow::Context;
use async_executor::Executor;
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use bdk::electrum_client::{
@@ -35,7 +35,7 @@ use secp256k1::{
use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
use crate::serial::{deserialize, serialize, Decodable, Encodable};
use crate::util::{generate_id, NetworkName};
use crate::{Error, Result};
use crate::{types::*, Error, Result};
// Swap out these types for any future non bitcoin-rs types
pub type PubAddress = Address;
@@ -357,7 +357,7 @@ impl BtcClient {
async fn handle_subscribe_request(
self: Arc<Self>,
btc_keys: Account,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
) -> BtcResult<()> {
let client = self.client.clone();
@@ -528,7 +528,7 @@ impl BtcClient {
impl NetworkClient for BtcClient {
async fn subscribe(
self: Arc<Self>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
_mint: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<TokenSubscribtion> {
@@ -560,7 +560,7 @@ impl NetworkClient for BtcClient {
self: Arc<Self>,
private_key: Vec<u8>,
_public_key: Vec<u8>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
_mint: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<String> {

View File

@@ -1,8 +1,8 @@
use async_std::sync::{Arc, Mutex};
use std::convert::TryInto;
use std::time::Duration;
use async_executor::Executor;
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use hash_db::Hasher;
use keccak_hasher::KeccakHasher;
@@ -16,19 +16,14 @@ use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
use crate::{
rpc::jsonrpc,
rpc::jsonrpc::JsonResult,
serial::{deserialize, serialize, Decodable, Encodable},
serial::{deserialize, serialize},
types::*,
util::{generate_id, parse::truncate, NetworkName},
Error, Result,
};
pub const ETH_NATIVE_TOKEN_ID: &str = "0x0000000000000000000000000000000000000000";
#[derive(Clone, Debug)]
pub struct Keypair {
pub private_key: String,
pub public_key: String,
}
// An ERC-20 token transfer transaction's data is as follows:
//
// 1. The first 4 bytes of the keccak256 hash of "transfer(address,uint256)".
@@ -106,10 +101,6 @@ pub fn erc20_balanceof_data(account: &str) -> String {
format!("0x{}{}", hex::encode(*ERC20_BALANCEOF_METHOD), acc_padded)
}
pub fn erc20_decimals_data() -> String {
format!("0x{}", hex::encode(*ERC20_DECIMALS_METHOD))
}
fn to_eth_hex(val: BigUint) -> String {
let bytes = val.to_bytes_be();
let h = hex::encode(bytes);
@@ -184,7 +175,7 @@ impl EthTx {
//
pub struct EthClient {
// main_keypair (private, public)
main_keypair: Keypair,
main_keypair: (String, String),
passphrase: String,
socket_path: String,
subscriptions: Arc<Mutex<Vec<String>>>,
@@ -195,50 +186,29 @@ pub struct EthClient {
}
impl EthClient {
pub fn new(socket_path: String, passphrase: String) -> Self {
pub fn new(
socket_path: String,
passphrase: String,
main_keypair: (String, String),
) -> Arc<Self> {
let notify_channel = async_channel::unbounded();
let subscriptions = Arc::new(Mutex::new(Vec::new()));
Self {
// this must set by the cashier
main_keypair: Keypair {
private_key: String::new(),
public_key: String::new(),
},
Arc::new(Self {
main_keypair,
passphrase,
socket_path,
subscriptions,
notify_channel,
})
}
}
pub fn set_main_keypair(&mut self, keypair: &Keypair) {
self.main_keypair = keypair.clone();
}
async fn send_erc20_to_main_wallet(&self, acc: &str, amount: BigUint, mint: &str) -> Result<()> {
debug!(target: "ETH BRIDGE", "Send erc20 token to main wallet");
let tx = EthTx::new(
acc,
mint,
None,
None,
None,
Some(erc20_transfer_data(self.main_keypair.public_key.as_str(), amount)),
None,
);
self.send_transaction(&tx, &self.passphrase).await?;
Ok(())
}
async fn send_eth_to_main_wallet(&self, acc: &str, amount: BigUint) -> Result<()> {
debug!(target: "ETH BRIDGE", "Send eth to main wallet");
let tx = EthTx::new(
acc,
&self.main_keypair.public_key,
&self.main_keypair.1,
None,
None,
Some(amount),
@@ -254,39 +224,16 @@ impl EthClient {
async fn handle_subscribe_request(
self: Arc<Self>,
addr: String,
drk_pub_key: jubjub::SubgroupPoint,
mint: Option<String>,
drk_pub_key: DrkPublicKey,
) -> Result<()> {
let token_pubkey = if mint.is_some() {
mint.clone().unwrap()
} else {
addr.clone()
};
if mint.is_some() {
debug!(target: "ETH BRIDGE", "Got subscribe request for ERC20 token");
debug!(target: "ETH BRIDGE", "Main wallet: {}", addr);
debug!(target: "ETH BRIDGE", "Associated token address: {}", token_pubkey);
} else {
debug!(target: "ETH BRIDGE", "Got subscribe request for native ETH");
debug!(target: "ETH BRIDGE", "Main wallet: {}", addr);
}
if self.subscriptions.lock().await.contains(&addr) {
return Ok(());
}
let decimals = if mint.is_some() {
let hexdecimals = self.get_erc20_decimals(&addr, &token_pubkey).await?;
let hexdecimals = hexdecimals.as_str().unwrap().trim_start_matches("0x");
18
} else {
18
};
let decimals = 18;
let prev_balance = self.get_current_balance(&addr, mint.clone()).await?;
let prev_balance = self.get_current_balance(&addr, None).await?;
let mut current_balance;
@@ -303,7 +250,7 @@ impl EthClient {
sub_iter += iter_interval;
async_std::task::sleep(Duration::from_secs(iter_interval)).await;
current_balance = self.get_current_balance(&addr, mint.clone()).await?;
current_balance = self.get_current_balance(&addr, None).await?;
if current_balance != prev_balance {
break;
@@ -316,7 +263,7 @@ impl EthClient {
if current_balance < prev_balance {
return Err(crate::Error::ClientFailed(
"New balance is less than previous balance".into(),
"New balance is less than previous balance".into(),
));
}
@@ -324,49 +271,28 @@ impl EthClient {
let received_balance_ui = received_balance.clone() / u64::pow(10, decimals as u32);
if mint.is_some() {
send_notification
.send(TokenNotification {
network: NetworkName::Ethereum,
token_id: generate_id(token_pubkey.as_str(), &NetworkName::Ethereum)?,
drk_pub_key,
// TODO FIX
received_balance: received_balance.to_u64_digits()[0],
decimals: decimals as u16,
})
send_notification
.send(TokenNotification {
network: NetworkName::Ethereum,
token_id: generate_id(ETH_NATIVE_TOKEN_ID, &NetworkName::Ethereum)?,
drk_pub_key,
// TODO FIX
received_balance: received_balance.to_u64_digits()[0],
decimals: decimals as u16,
})
.await
.map_err(Error::from)?;
.map_err(Error::from)?;
self.send_erc20_to_main_wallet(&addr, received_balance, &token_pubkey)
.await?;
self.send_eth_to_main_wallet(&addr, received_balance)
.await?;
debug!(target: "ETH BRIDGE", "Received {} erc20 token: {}", received_balance_ui, token_pubkey);
} else {
send_notification
.send(TokenNotification {
network: NetworkName::Ethereum,
token_id: generate_id(ETH_NATIVE_TOKEN_ID, &NetworkName::Ethereum)?,
drk_pub_key,
// TODO FIX
received_balance: received_balance.to_u64_digits()[0],
decimals: decimals as u16,
})
.await
.map_err(Error::from)?;
self.send_eth_to_main_wallet(&addr, received_balance)
.await?;
debug!(target: "ETH BRIDGE", "Received {} eth", received_balance_ui );
debug!(target: "ETH BRIDGE", "Received {} eth", received_balance_ui );
}
Ok(())
}
async fn unsubscribe(&self, pubkey: &str) {
async fn unsubscribe(&self, pubkey: &String) {
let mut subscriptions = self.subscriptions.lock().await;
let index = subscriptions.iter().position(|p| p == pubkey);
if let Some(ind) = index {
@@ -380,10 +306,10 @@ impl EthClient {
let reply: JsonResult = match jsonrpc::send_unix_request(&self.socket_path, json!(r))
.await
.map_err(EthFailed::from)
{
Ok(v) => v,
Err(e) => return Err(e),
};
{
Ok(v) => v,
Err(e) => return Err(e),
};
match reply {
JsonResult::Resp(r) => {
@@ -409,11 +335,11 @@ impl EthClient {
}
/*
pub async fn estimate_gas(&self, tx: &EthTx) -> Result<Value> {
let req = jsonrpc::request(json!("eth_estimateGas"), json!([tx]));
Ok(self.request(req).await?)
}
*/
pub async fn estimate_gas(&self, tx: &EthTx) -> Result<Value> {
let req = jsonrpc::request(json!("eth_estimateGas"), json!([tx]));
Ok(self.request(req).await?)
}
*/
pub async fn block_number(&self) -> EthResult<Value> {
let req = jsonrpc::request(json!("eth_blockNumber"), json!([]));
@@ -439,40 +365,18 @@ impl EthClient {
Ok(self.request(req).await?)
}
pub async fn get_erc20_decimals(&self, acc: &str, mint: &str) -> EthResult<Value> {
let tx = EthTx::new(
acc,
mint,
None,
None,
None,
Some(erc20_decimals_data()),
None,
);
let req = jsonrpc::request(json!("eth_call"), json!([tx, "latest"]));
Ok(self.request(req).await?)
}
pub async fn get_current_balance(&self, acc: &str, mint: Option<String>) -> EthResult<BigUint> {
let hexbalance = if mint.is_some() {
// ERC20 balance
self.get_erc20_balance(acc, mint.unwrap().as_str()).await?
} else {
// Latest known block, used to calculate present balance.
let block = self.block_number().await?;
let block = block.as_str().unwrap();
// Native ETH balance
self.get_eth_balance(acc, block).await?
};
pub async fn get_current_balance(&self, acc: &str, _mint: Option<&str>) -> EthResult<BigUint> {
// Latest known block, used to calculate present balance.
let block = self.block_number().await?;
let block = block.as_str().unwrap();
// Native ETH balance
let hexbalance = self.get_eth_balance(&acc, block).await?;
let hexbalance = hexbalance.as_str().unwrap().trim_start_matches("0x");
let balance = BigUint::parse_bytes(hexbalance.as_bytes(), 16).unwrap();
Ok(balance)
}
pub async fn send_transaction(&self, tx: &EthTx, passphrase: &str) -> EthResult<Value> {
@@ -485,8 +389,8 @@ impl EthClient {
impl NetworkClient for EthClient {
async fn subscribe(
self: Arc<Self>,
drk_pub_key: jubjub::SubgroupPoint,
mint_address: Option<String>,
drk_pub_key: DrkPublicKey,
_mint_address: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<TokenSubscribtion> {
let private_key = generate_privkey();
@@ -503,13 +407,13 @@ impl NetworkClient for EthClient {
executor
.spawn(async move {
let result = self
.handle_subscribe_request(addr_cloned, drk_pub_key, mint_address)
.handle_subscribe_request(addr_cloned, drk_pub_key)
.await;
if let Err(e) = result {
error!(target: "ETH BRIDGE SUBSCRIPTION","{}", e.to_string());
}
})
.detach();
.detach();
let private_key: Vec<u8> = serialize(&private_key);
@@ -523,8 +427,8 @@ impl NetworkClient for EthClient {
self: Arc<Self>,
_private_key: Vec<u8>,
public_key: Vec<u8>,
drk_pub_key: jubjub::SubgroupPoint,
mint_address: Option<String>,
drk_pub_key: DrkPublicKey,
_mint_address: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<String> {
let public_key: String = deserialize(&public_key)?;
@@ -532,12 +436,12 @@ impl NetworkClient for EthClient {
let address = public_key.clone();
executor
.spawn(async move {
let result = self.handle_subscribe_request(address, drk_pub_key, mint_address).await;
let result = self.handle_subscribe_request(address, drk_pub_key).await;
if let Err(e) = result {
error!(target: "ETH BRIDGE SUBSCRIPTION","{}", e.to_string());
}
})
.detach();
.detach();
Ok(public_key)
}
@@ -549,40 +453,26 @@ impl NetworkClient for EthClient {
async fn send(
self: Arc<Self>,
address: Vec<u8>,
mint: Option<String>,
_mint: Option<String>,
amount: u64,
) -> Result<()> {
// Recipient address
let dest: String = deserialize(&address)?;
// FIXME
let decimals = 18;
// reverse truncate
let amount = truncate(amount, decimals as u16, 8)?;
let tx = if mint.is_some() {
EthTx::new(
&self.main_keypair.public_key,
&mint.unwrap(),
None,
None,
None,
Some(erc20_transfer_data(&dest, BigUint::from(amount))),
None,
)
} else {
EthTx::new(
&self.main_keypair.public_key,
&dest,
None,
None,
Some(BigUint::from(amount)),
None,
None,
)
};
let tx = EthTx::new(
&self.main_keypair.1,
&dest,
None,
None,
Some(BigUint::from(amount)),
None,
None,
);
self.send_transaction(&tx, &self.passphrase).await?;
@@ -590,48 +480,59 @@ impl NetworkClient for EthClient {
}
}
impl Encodable for Keypair {
fn encode<S: std::io::Write>(&self, mut s: S) -> Result<usize> {
let mut len = 0;
len += self.private_key.encode(&mut s)?;
len += self.public_key.encode(&mut s)?;
Ok(len)
}
}
impl Decodable for Keypair {
fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
Ok(Self {
private_key: Decodable::decode(&mut d)?,
public_key: Decodable::decode(&mut d)?,
})
}
}
#[derive(Debug, Clone, thiserror::Error)]
#[derive(Debug)]
pub enum EthFailed {
#[error("There is no enough value {0}")]
NotEnoughValue(u64),
#[error("Main Account Has no enough value")]
MainAccountNotEnoughValue,
#[error("Bad Eth Address: {0}")]
BadEthAddress(String),
#[error("Decode and decode keys error: {0}")]
DecodeAndEncodeError(String),
#[error("Rpc Error: {0}")]
RpcError(String),
#[error("Eth client error: {0}")]
EthClientError(String),
#[error("Given mint is not valid: {0}")]
MintIsNotValid(String),
#[error("JsonError: {0}")]
JsonError(String),
#[error("Parse Error: {0}")]
ParseError(String),
#[error("Unable to derive address from private key")]
ImportPrivateError,
}
impl std::error::Error for EthFailed {}
impl std::fmt::Display for EthFailed {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
EthFailed::NotEnoughValue(i) => {
write!(f, "There is no enough value {}", i)
}
EthFailed::MainAccountNotEnoughValue => {
write!(f, "Main Account Has no enough value")
}
EthFailed::BadEthAddress(ref err) => {
write!(f, "Bad Eth Address: {}", err)
}
EthFailed::DecodeAndEncodeError(ref err) => {
write!(f, "Decode and decode keys error: {}", err)
}
EthFailed::RpcError(i) => {
write!(f, "Rpc Error: {}", i)
}
EthFailed::ParseError(i) => {
write!(f, "Parse Error: {}", i)
}
EthFailed::MintIsNotValid(i) => {
write!(f, "Given mint is not valid: {}", i)
}
EthFailed::JsonError(i) => {
write!(f, "JsonError: {}", i)
}
EthFailed::ImportPrivateError => {
write!(f, "Unable to derive address from private key")
}
EthFailed::EthClientError(i) => {
write!(f, "Eth client error: {}", i)
}
}
}
}
impl From<crate::error::Error> for EthFailed {
fn from(err: crate::error::Error) -> EthFailed {
EthFailed::EthClientError(err.to_string())

View File

@@ -1,8 +1,7 @@
use std::net::ToSocketAddrs;
use async_std::sync::Arc;
use std::convert::From;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::Arc;
use async_executor::Executor;
use log::debug;

View File

@@ -1,4 +1,3 @@
//pub mod cashier;
pub mod bridge;
pub mod gateway;
pub mod reqrep;

View File

@@ -1,10 +1,9 @@
use async_std::prelude::*;
use async_std::sync::Arc;
use std::convert::TryFrom;
use std::io;
use std::net::SocketAddr;
use std::sync::Arc;
use async_executor::Executor;
use async_std::prelude::*;
use bytes::Bytes;
use futures::FutureExt;
use log::*;
@@ -13,8 +12,8 @@ use signal_hook::consts::SIGINT;
use signal_hook_async_std::Signals;
use zeromq::*;
use crate::serial::{deserialize, serialize};
use crate::{Decodable, Encodable, Result};
use crate::serial::{deserialize, serialize, Decodable, Encodable};
use crate::Result;
pub type PeerId = Vec<u8>;

View File

@@ -1,14 +1,12 @@
use async_std::sync::{Arc, Mutex};
use std::convert::TryFrom;
use std::str::FromStr;
use std::time::Duration;
use async_executor::Executor;
use async_native_tls::TlsConnector;
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use futures::{SinkExt, StreamExt};
use log::{debug, error, info, warn};
use rand::rngs::OsRng;
use serde::Serialize;
use serde_json::{json, Value};
use solana_client::{blockhash_query::BlockhashQuery, rpc_client::RpcClient};
@@ -28,7 +26,7 @@ use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
use crate::rpc::{jsonrpc, jsonrpc::JsonResult, websockets, websockets::WsStream};
use crate::serial::{deserialize, serialize, Decodable, Encodable};
use crate::util::{generate_id, parse::truncate, NetworkName};
use crate::{Error, Result};
use crate::{types::*, Error, Result};
pub const SOL_NATIVE_TOKEN_ID: &str = "So11111111111111111111111111111111111111112";
@@ -97,7 +95,7 @@ impl SolClient {
async fn handle_subscribe_request(
self: Arc<Self>,
keypair: Keypair,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint: Option<Pubkey>,
) -> SolResult<()> {
debug!(target: "SOL BRIDGE", "handle_subscribe_request()");
@@ -419,11 +417,11 @@ impl SolClient {
impl NetworkClient for SolClient {
async fn subscribe(
self: Arc<Self>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint_address: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<TokenSubscribtion> {
let keypair = Keypair::generate(&mut OsRng);
let keypair = Keypair::new();
let public_key = keypair.pubkey().to_string();
let private_key = serialize(&keypair);
@@ -459,7 +457,7 @@ impl NetworkClient for SolClient {
self: Arc<Self>,
private_key: Vec<u8>,
_public_key: Vec<u8>,
drk_pub_key: jubjub::SubgroupPoint,
drk_pub_key: DrkPublicKey,
mint_address: Option<String>,
executor: Arc<Executor<'_>>,
) -> Result<String> {

View File

@@ -1,19 +1,23 @@
use bellman::groth16;
use bls12_381::Bls12;
use std::fmt;
use log::debug;
use crate::{
crypto::{coin::Coin, merkle_node::MerkleNode, note::EncryptedNote, nullifier::Nullifier},
tx,
crypto::{
coin::Coin, merkle_node2::MerkleNode, note::EncryptedNote, nullifier::Nullifier,
proof::VerifyingKey, schnorr,
},
tx::Transaction,
types::{DrkCoinBlind, DrkPublicKey, DrkSecretKey, DrkSerial, DrkTokenId, DrkValueBlind},
};
pub trait ProgramState {
fn is_valid_cashier_public_key(&self, public: &jubjub::SubgroupPoint) -> bool;
fn is_valid_cashier_public_key(&self, public: &schnorr::PublicKey) -> bool;
fn is_valid_merkle(&self, merkle: &MerkleNode) -> bool;
fn nullifier_exists(&self, nullifier: &Nullifier) -> bool;
fn mint_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12>;
fn spend_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12>;
fn mint_vk(&self) -> &VerifyingKey;
fn spend_vk(&self) -> &VerifyingKey;
}
pub struct StateUpdate {
@@ -46,10 +50,37 @@ pub enum VerifyFailed {
AssetMismatch,
}
pub fn state_transition<S: ProgramState>(
state: &S,
tx: tx::Transaction,
) -> VerifyResult<StateUpdate> {
impl std::error::Error for VerifyFailed {}
impl fmt::Display for VerifyFailed {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
match *self {
VerifyFailed::InvalidCashierKey(i) => {
write!(f, "Invalid cashier public key for clear input {}", i)
}
VerifyFailed::InvalidMerkle(i) => {
write!(f, "Invalid merkle root for input {}", i)
}
VerifyFailed::DuplicateNullifier(i) => {
write!(f, "Duplicate nullifier for input {}", i)
}
VerifyFailed::SpendProof(i) => write!(f, "Spend proof for input {}", i),
VerifyFailed::MintProof(i) => write!(f, "Mint proof for input {}", i),
VerifyFailed::ClearInputSignature(i) => {
write!(f, "Invalid signature for clear input {}", i)
}
VerifyFailed::InputSignature(i) => write!(f, "Invalid signature for input {}", i),
VerifyFailed::MissingFunds => {
f.write_str("Money in does not match money out (value commits)")
}
VerifyFailed::AssetMismatch => {
f.write_str("Assets don't match some inputs or outputs (token commits)")
}
}
}
}
pub fn state_transition<S: ProgramState>(state: &S, tx: Transaction) -> VerifyResult<StateUpdate> {
// Check deposits are legit
debug!(target: "STATE TRANSITION", "iterate clear_inputs");
@@ -67,7 +98,6 @@ pub fn state_transition<S: ProgramState>(
debug!(target: "STATE TRANSITION", "iterate inputs");
for (i, input) in tx.inputs.iter().enumerate() {
// Check merkle roots
let merkle = &input.revealed.merkle_root;
// Merkle is used to know whether this is a coin that existed
@@ -87,7 +117,7 @@ pub fn state_transition<S: ProgramState>(
debug!(target: "STATE TRANSITION", "Check the tx Verifies correctly");
// Check the tx verifies correctly
tx.verify(state.mint_pvk(), state.spend_pvk())?;
tx.verify(state.mint_vk(), state.spend_vk())?;
let mut nullifiers = vec![];
for input in tx.inputs {
@@ -99,7 +129,7 @@ pub fn state_transition<S: ProgramState>(
let mut enc_notes = vec![];
for output in tx.outputs {
// Gather all the coins
coins.push(Coin::new(output.revealed.coin));
coins.push(Coin(output.revealed.coin.clone()));
enc_notes.push(output.enc_note);
}

View File

@@ -1,6 +1,4 @@
use bellman::groth16;
use bls12_381::Bls12;
use ff::Field;
use pasta_curves::arithmetic::Field;
use rand::rngs::OsRng;
use super::{
@@ -8,10 +6,14 @@ use super::{
Transaction, TransactionClearInput, TransactionInput, TransactionOutput,
};
use crate::crypto::{
create_mint_proof, create_spend_proof, merkle::MerklePath, merkle_node::MerkleNode, note::Note,
schnorr,
mint_proof::create_mint_proof, note::Note, schnorr, spend_proof::create_spend_proof,
};
use crate::{
crypto::merkle_node2::MerkleNode,
serial::Encodable,
types::{DrkCoinBlind, DrkPublicKey, DrkSecretKey, DrkSerial, DrkTokenId, DrkValueBlind},
Result,
};
use crate::serial::Encodable;
pub struct TransactionBuilder {
pub clear_inputs: Vec<TransactionBuilderClearInputInfo>,
@@ -21,32 +23,33 @@ pub struct TransactionBuilder {
pub struct TransactionBuilderClearInputInfo {
pub value: u64,
pub token_id: jubjub::Fr,
pub signature_secret: jubjub::Fr,
pub token_id: DrkTokenId,
pub signature_secret: schnorr::SecretKey,
}
pub struct TransactionBuilderInputInfo {
pub merkle_path: MerklePath<MerkleNode>,
pub secret: jubjub::Fr,
pub leaf_position: incrementalmerkletree::Position,
pub merkle_path: Vec<MerkleNode>,
pub secret: DrkSecretKey,
pub note: Note,
}
pub struct TransactionBuilderOutputInfo {
pub value: u64,
pub token_id: jubjub::Fr,
pub public: jubjub::SubgroupPoint,
pub token_id: DrkTokenId,
pub public: DrkPublicKey,
}
impl TransactionBuilder {
fn compute_remainder_blind(
clear_inputs: &[PartialTransactionClearInput],
input_blinds: &[jubjub::Fr],
output_blinds: &[jubjub::Fr],
) -> jubjub::Fr {
let mut total = jubjub::Fr::zero();
input_blinds: &[DrkValueBlind],
output_blinds: &[DrkValueBlind],
) -> DrkValueBlind {
let mut total = DrkValueBlind::zero();
for input in clear_inputs {
total += input.valcom_blind;
total += input.value_blind;
}
for input_blind in input_blinds {
@@ -60,23 +63,18 @@ impl TransactionBuilder {
total
}
pub fn build(
self,
mint_params: &groth16::Parameters<Bls12>,
spend_params: &groth16::Parameters<Bls12>,
) -> Transaction {
pub fn build(self) -> Result<Transaction> {
let mut clear_inputs = vec![];
let token_commit_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
let token_blind = DrkValueBlind::random(&mut OsRng);
for input in &self.clear_inputs {
let signature_public =
zcash_primitives::constants::SPENDING_KEY_GENERATOR * input.signature_secret;
let signature_public = input.signature_secret.public_key();
let value_blind = DrkValueBlind::random(&mut OsRng);
let valcom_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
let clear_input = PartialTransactionClearInput {
value: input.value,
token_id: input.token_id,
valcom_blind,
token_commit_blind,
value_blind,
token_blind,
signature_public,
};
clear_inputs.push(clear_input);
@@ -85,36 +83,25 @@ impl TransactionBuilder {
let mut inputs = vec![];
let mut input_blinds = vec![];
let mut signature_secrets = vec![];
for input in &self.inputs {
input_blinds.push(input.note.valcom_blind);
for input in self.inputs {
input_blinds.push(input.note.value_blind);
let signature_secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
// make proof
// TODO: Some stupid glue code. Need to sort this out
let auth_path: Vec<(bls12_381::Scalar, bool)> = input
.merkle_path
.auth_path
.iter()
.map(|(node, b)| ((*node).into(), *b))
.collect();
let signature_secret = schnorr::SecretKey::random();
let (proof, revealed) = create_spend_proof(
spend_params,
input.note.value,
input.note.token_id,
input.note.valcom_blind,
token_commit_blind,
input.note.value_blind,
token_blind,
input.note.serial,
input.note.coin_blind,
input.secret,
auth_path,
signature_secret,
);
input.leaf_position,
input.merkle_path,
signature_secret.clone(),
)?;
// First we make the tx then sign after
let signature_secret = schnorr::SecretKey(signature_secret);
signature_secrets.push(signature_secret);
let input = PartialTransactionInput {
@@ -128,26 +115,25 @@ impl TransactionBuilder {
let mut output_blinds = vec![];
for (i, output) in self.outputs.iter().enumerate() {
let valcom_blind = if i == self.outputs.len() - 1 {
let value_blind = if i == self.outputs.len() - 1 {
Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds)
} else {
jubjub::Fr::random(&mut OsRng)
DrkValueBlind::random(&mut OsRng)
};
output_blinds.push(valcom_blind);
output_blinds.push(value_blind);
let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
let coin_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
let serial = DrkSerial::random(&mut OsRng);
let coin_blind = DrkCoinBlind::random(&mut OsRng);
let (mint_proof, revealed) = create_mint_proof(
mint_params,
output.value,
output.token_id,
valcom_blind,
token_commit_blind,
value_blind,
token_blind,
serial,
coin_blind,
output.public,
);
)?;
// Encrypted note
@@ -156,10 +142,10 @@ impl TransactionBuilder {
value: output.value,
token_id: output.token_id,
coin_blind,
valcom_blind,
value_blind,
};
let encrypted_note = note.encrypt(&output.public).unwrap();
let encrypted_note = note.encrypt(&output.public)?;
let output = TransactionOutput {
mint_proof,
@@ -176,13 +162,11 @@ impl TransactionBuilder {
};
let mut unsigned_tx_data = vec![];
partial_tx
.encode(&mut unsigned_tx_data)
.expect("TODO handle this");
partial_tx.encode(&mut unsigned_tx_data)?;
let mut clear_inputs = vec![];
for (input, info) in partial_tx.clear_inputs.into_iter().zip(self.clear_inputs) {
let secret = schnorr::SecretKey(info.signature_secret);
let secret = info.signature_secret;
let signature = secret.sign(&unsigned_tx_data[..]);
let input = TransactionClearInput::from_partial(input, signature);
clear_inputs.push(input);
@@ -199,10 +183,10 @@ impl TransactionBuilder {
inputs.push(input);
}
Transaction {
Ok(Transaction {
clear_inputs,
inputs,
outputs: partial_tx.outputs,
}
})
}
}

View File

@@ -1,20 +1,29 @@
pub mod builder;
pub mod partial;
use bellman::groth16;
use bls12_381::Bls12;
use group::Group;
use std::io;
use self::partial::{PartialTransactionClearInput, PartialTransactionInput};
use crate::crypto::{
note::EncryptedNote, schnorr, verify_mint_proof, verify_spend_proof, MintRevealedValues,
SpendRevealedValues,
use pasta_curves::group::Group;
use crate::{
crypto::{
mint_proof::verify_mint_proof,
note::EncryptedNote,
proof::{Proof, VerifyingKey},
schnorr,
spend_proof::verify_spend_proof,
util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
MintRevealedValues, SpendRevealedValues,
},
error::Result,
impl_vec,
serial::{Decodable, Encodable, VarInt},
state,
types::{
DrkCoinBlind, DrkPublicKey, DrkSecretKey, DrkSerial, DrkTokenId, DrkValue, DrkValueBlind,
DrkValueCommit,
},
};
use crate::error::Result;
use crate::impl_vec;
use crate::serial::{Decodable, Encodable, VarInt};
use crate::state;
pub use self::builder::{
TransactionBuilder, TransactionBuilderClearInputInfo, TransactionBuilderInputInfo,
@@ -29,21 +38,21 @@ pub struct Transaction {
pub struct TransactionClearInput {
pub value: u64,
pub token_id: jubjub::Fr,
pub valcom_blind: jubjub::Fr,
pub token_commit_blind: jubjub::Fr,
pub signature_public: jubjub::SubgroupPoint,
pub token_id: DrkTokenId,
pub value_blind: DrkValueBlind,
pub token_blind: DrkValueBlind,
pub signature_public: schnorr::PublicKey,
pub signature: schnorr::Signature,
}
pub struct TransactionInput {
pub spend_proof: groth16::Proof<Bls12>,
pub spend_proof: Proof,
pub revealed: SpendRevealedValues,
pub signature: schnorr::Signature,
}
pub struct TransactionOutput {
pub mint_proof: groth16::Proof<Bls12>,
pub mint_proof: Proof,
pub revealed: MintRevealedValues,
pub enc_note: EncryptedNote,
}
@@ -57,11 +66,6 @@ impl Transaction {
Ok(len)
}
fn compute_pedersen_commit(value: jubjub::Fr, blind: &jubjub::Fr) -> jubjub::SubgroupPoint {
(zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR * value)
+ (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * blind)
}
fn verify_token_commitments(&self) -> bool {
assert_ne!(self.outputs.len(), 0);
let token_commit_value = self.outputs[0].revealed.token_commit;
@@ -77,7 +81,7 @@ impl Transaction {
.any(|output| output.revealed.token_commit != token_commit_value);
failed = failed
|| self.clear_inputs.iter().any(|input| {
Self::compute_pedersen_commit(input.token_id, &input.token_commit_blind)
pedersen_commitment_scalar(mod_r_p(input.token_id), input.token_blind)
!= token_commit_value
});
!failed
@@ -85,28 +89,30 @@ impl Transaction {
pub fn verify(
&self,
mint_pvk: &groth16::PreparedVerifyingKey<Bls12>,
spend_pvk: &groth16::PreparedVerifyingKey<Bls12>,
mint_pvk: &VerifyingKey,
spend_pvk: &VerifyingKey,
) -> state::VerifyResult<()> {
let mut valcom_total = jubjub::SubgroupPoint::identity();
let mut valcom_total = DrkValueCommit::identity();
for input in &self.clear_inputs {
let value = jubjub::Fr::from(input.value);
valcom_total += Self::compute_pedersen_commit(value, &input.valcom_blind);
valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
}
for (i, input) in self.inputs.iter().enumerate() {
if !verify_spend_proof(spend_pvk, &input.spend_proof, &input.revealed) {
if verify_spend_proof(spend_pvk, input.spend_proof.clone(), &input.revealed).is_err() {
return Err(state::VerifyFailed::SpendProof(i));
}
valcom_total += &input.revealed.value_commit;
}
for (i, output) in self.outputs.iter().enumerate() {
if !verify_mint_proof(mint_pvk, &output.mint_proof, &output.revealed) {
if verify_mint_proof(mint_pvk, &output.mint_proof, &output.revealed).is_err() {
return Err(state::VerifyFailed::MintProof(i));
}
valcom_total -= &output.revealed.value_commit;
}
if valcom_total != jubjub::SubgroupPoint::identity() {
if valcom_total != DrkValueCommit::identity() {
return Err(state::VerifyFailed::MissingFunds);
}
@@ -120,13 +126,13 @@ impl Transaction {
self.encode_without_signature(&mut unsigned_tx_data)
.expect("TODO handle this");
for (i, input) in self.clear_inputs.iter().enumerate() {
let public = schnorr::PublicKey(input.signature_public);
let public = &input.signature_public;
if !public.verify(&unsigned_tx_data[..], &input.signature) {
return Err(state::VerifyFailed::ClearInputSignature(i));
}
}
for (i, input) in self.inputs.iter().enumerate() {
let public = schnorr::PublicKey(input.revealed.signature_public);
let public = &input.revealed.signature_public;
if !public.verify(&unsigned_tx_data[..], &input.signature) {
return Err(state::VerifyFailed::InputSignature(i));
}
@@ -137,12 +143,15 @@ impl Transaction {
}
impl TransactionClearInput {
fn from_partial(partial: PartialTransactionClearInput, signature: schnorr::Signature) -> Self {
fn from_partial(
partial: partial::PartialTransactionClearInput,
signature: schnorr::Signature,
) -> Self {
Self {
value: partial.value,
token_id: partial.token_id,
valcom_blind: partial.valcom_blind,
token_commit_blind: partial.token_commit_blind,
value_blind: partial.value_blind,
token_blind: partial.token_blind,
signature_public: partial.signature_public,
signature,
}
@@ -152,15 +161,18 @@ impl TransactionClearInput {
let mut len = 0;
len += self.value.encode(&mut s)?;
len += self.token_id.encode(&mut s)?;
len += self.valcom_blind.encode(&mut s)?;
len += self.token_commit_blind.encode(&mut s)?;
len += self.value_blind.encode(&mut s)?;
len += self.token_blind.encode(&mut s)?;
len += self.signature_public.encode(s)?;
Ok(len)
}
}
impl TransactionInput {
fn from_partial(partial: PartialTransactionInput, signature: schnorr::Signature) -> Self {
fn from_partial(
partial: partial::PartialTransactionInput,
signature: schnorr::Signature,
) -> Self {
Self {
spend_proof: partial.spend_proof,
revealed: partial.revealed,
@@ -201,8 +213,8 @@ impl Encodable for TransactionClearInput {
let mut len = 0;
len += self.value.encode(&mut s)?;
len += self.token_id.encode(&mut s)?;
len += self.valcom_blind.encode(&mut s)?;
len += self.token_commit_blind.encode(&mut s)?;
len += self.value_blind.encode(&mut s)?;
len += self.token_blind.encode(&mut s)?;
len += self.signature_public.encode(&mut s)?;
len += self.signature.encode(s)?;
Ok(len)
@@ -214,8 +226,8 @@ impl Decodable for TransactionClearInput {
Ok(Self {
value: Decodable::decode(&mut d)?,
token_id: Decodable::decode(&mut d)?,
valcom_blind: Decodable::decode(&mut d)?,
token_commit_blind: Decodable::decode(&mut d)?,
value_blind: Decodable::decode(&mut d)?,
token_blind: Decodable::decode(&mut d)?,
signature_public: Decodable::decode(&mut d)?,
signature: Decodable::decode(d)?,
})

View File

@@ -1,12 +1,13 @@
use bellman::groth16;
use bls12_381::Bls12;
use std::io;
use super::TransactionOutput;
use crate::crypto::SpendRevealedValues;
use crate::error::Result;
use crate::impl_vec;
use crate::serial::{Decodable, Encodable, VarInt};
use crate::{
crypto::{schnorr, spend_proof::SpendRevealedValues, Proof},
error::Result,
impl_vec,
serial::{Decodable, Encodable, VarInt},
types::{DrkCoinBlind, DrkPublicKey, DrkSecretKey, DrkSerial, DrkTokenId, DrkValueBlind},
};
pub struct PartialTransaction {
pub clear_inputs: Vec<PartialTransactionClearInput>,
@@ -16,14 +17,14 @@ pub struct PartialTransaction {
pub struct PartialTransactionClearInput {
pub value: u64,
pub token_id: jubjub::Fr,
pub valcom_blind: jubjub::Fr,
pub token_commit_blind: jubjub::Fr,
pub signature_public: jubjub::SubgroupPoint,
pub token_id: DrkTokenId,
pub value_blind: DrkValueBlind,
pub token_blind: DrkValueBlind,
pub signature_public: schnorr::PublicKey,
}
pub struct PartialTransactionInput {
pub spend_proof: groth16::Proof<Bls12>,
pub spend_proof: Proof,
pub revealed: SpendRevealedValues,
}
@@ -52,8 +53,8 @@ impl Encodable for PartialTransactionClearInput {
let mut len = 0;
len += self.value.encode(&mut s)?;
len += self.token_id.encode(&mut s)?;
len += self.valcom_blind.encode(&mut s)?;
len += self.token_commit_blind.encode(&mut s)?;
len += self.value_blind.encode(&mut s)?;
len += self.token_blind.encode(&mut s)?;
len += self.signature_public.encode(&mut s)?;
Ok(len)
}
@@ -63,8 +64,8 @@ impl Decodable for PartialTransactionClearInput {
Ok(Self {
value: Decodable::decode(&mut d)?,
token_id: Decodable::decode(&mut d)?,
valcom_blind: Decodable::decode(&mut d)?,
token_commit_blind: Decodable::decode(&mut d)?,
value_blind: Decodable::decode(&mut d)?,
token_blind: Decodable::decode(&mut d)?,
signature_public: Decodable::decode(&mut d)?,
})
}

Some files were not shown because too many files have changed in this diff Show More