mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 22:57:59 -05:00
Merge branch 'halo2-integration'
This commit is contained in:
1544
Cargo.lock
generated
1544
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
128
Cargo.toml
128
Cargo.toml
@@ -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"]
|
||||
|
||||
21
Makefile
21
Makefile
@@ -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
1213
example/halo2/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"]
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Always use the --release flag otherwise it's too slow:
|
||||
|
||||
```
|
||||
cargo run --release --bin simple3
|
||||
```
|
||||
|
||||
303
example/halo2/src/bin/anonvote.rs
Normal file
303
example/halo2/src/bin/anonvote.rs
Normal 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(())
|
||||
}
|
||||
@@ -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(),
|
||||
];
|
||||
}
|
||||
@@ -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(¶ms, &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(¶ms, &circuit).unwrap();
|
||||
let pk = plonk::keygen_pk(¶ms, 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();
|
||||
|
||||
@@ -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(¶ms, &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(¶ms, &circuit).unwrap();
|
||||
let pk = plonk::keygen_pk(¶ms, 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(()));
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
@@ -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(¶ms, &empty_circuit).expect("keygen_vk should not fail");
|
||||
let pk = keygen_pk(¶ms, 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(¶ms, &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(¶ms, 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());
|
||||
}
|
||||
|
||||
@@ -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(¶ms, &empty_circuit).expect("keygen_vk should not fail");
|
||||
let pk = keygen_pk(¶ms, 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(¶ms, &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(¶ms, 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());
|
||||
}
|
||||
|
||||
@@ -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
137
example/halo2/src/endian.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
macro_rules! define_slice_to_be {
|
||||
($name: ident, $type: ty) => {
|
||||
#[inline]
|
||||
pub fn $name(slice: &[u8]) -> $type {
|
||||
assert_eq!(slice.len(), ::std::mem::size_of::<$type>());
|
||||
let mut res = 0;
|
||||
for i in 0..::std::mem::size_of::<$type>() {
|
||||
res |= (slice[i] as $type) << (::std::mem::size_of::<$type>() - i - 1) * 8;
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! define_slice_to_le {
|
||||
($name: ident, $type: ty) => {
|
||||
#[inline]
|
||||
pub fn $name(slice: &[u8]) -> $type {
|
||||
assert_eq!(slice.len(), ::std::mem::size_of::<$type>());
|
||||
let mut res = 0;
|
||||
for i in 0..::std::mem::size_of::<$type>() {
|
||||
res |= (slice[i] as $type) << i * 8;
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! define_be_to_array {
|
||||
($name: ident, $type: ty, $byte_len: expr) => {
|
||||
#[inline]
|
||||
pub fn $name(val: $type) -> [u8; $byte_len] {
|
||||
assert_eq!(::std::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22
|
||||
let mut res = [0; $byte_len];
|
||||
for i in 0..$byte_len {
|
||||
res[i] = ((val >> ($byte_len - i - 1) * 8) & 0xff) as u8;
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! define_le_to_array {
|
||||
($name: ident, $type: ty, $byte_len: expr) => {
|
||||
#[inline]
|
||||
pub fn $name(val: $type) -> [u8; $byte_len] {
|
||||
assert_eq!(::std::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22
|
||||
let mut res = [0; $byte_len];
|
||||
for i in 0..$byte_len {
|
||||
res[i] = ((val >> i * 8) & 0xff) as u8;
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_slice_to_be!(slice_to_u32_be, u32);
|
||||
define_be_to_array!(u32_to_array_be, u32, 4);
|
||||
define_slice_to_le!(slice_to_u16_le, u16);
|
||||
define_slice_to_le!(slice_to_u32_le, u32);
|
||||
define_slice_to_le!(slice_to_u64_le, u64);
|
||||
define_le_to_array!(u16_to_array_le, u16, 2);
|
||||
define_le_to_array!(u32_to_array_le, u32, 4);
|
||||
define_le_to_array!(u64_to_array_le, u64, 8);
|
||||
|
||||
#[inline]
|
||||
pub fn i16_to_array_le(val: i16) -> [u8; 2] {
|
||||
u16_to_array_le(val as u16)
|
||||
}
|
||||
#[inline]
|
||||
pub fn slice_to_i16_le(slice: &[u8]) -> i16 {
|
||||
slice_to_u16_le(slice) as i16
|
||||
}
|
||||
#[inline]
|
||||
pub fn slice_to_i32_le(slice: &[u8]) -> i32 {
|
||||
slice_to_u32_le(slice) as i32
|
||||
}
|
||||
#[inline]
|
||||
pub fn i32_to_array_le(val: i32) -> [u8; 4] {
|
||||
u32_to_array_le(val as u32)
|
||||
}
|
||||
#[inline]
|
||||
pub fn slice_to_i64_le(slice: &[u8]) -> i64 {
|
||||
slice_to_u64_le(slice) as i64
|
||||
}
|
||||
#[inline]
|
||||
pub fn i64_to_array_le(val: i64) -> [u8; 8] {
|
||||
u64_to_array_le(val as u64)
|
||||
}
|
||||
|
||||
macro_rules! define_chunk_slice_to_int {
|
||||
($name: ident, $type: ty, $converter: ident) => {
|
||||
#[inline]
|
||||
pub fn $name(inp: &[u8], outp: &mut [$type]) {
|
||||
assert_eq!(inp.len(), outp.len() * ::std::mem::size_of::<$type>());
|
||||
for (outp_val, data_bytes) in outp
|
||||
.iter_mut()
|
||||
.zip(inp.chunks(::std::mem::size_of::<$type>()))
|
||||
{
|
||||
*outp_val = $converter(data_bytes);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
define_chunk_slice_to_int!(bytes_to_u64_slice_le, u64, slice_to_u64_le);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn endianness_test() {
|
||||
assert_eq!(slice_to_u32_be(&[0xde, 0xad, 0xbe, 0xef]), 0xdeadbeef);
|
||||
assert_eq!(u32_to_array_be(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]);
|
||||
|
||||
assert_eq!(slice_to_u16_le(&[0xad, 0xde]), 0xdead);
|
||||
assert_eq!(slice_to_u32_le(&[0xef, 0xbe, 0xad, 0xde]), 0xdeadbeef);
|
||||
assert_eq!(
|
||||
slice_to_u64_le(&[0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]),
|
||||
0x1badcafedeadbeef
|
||||
);
|
||||
assert_eq!(u16_to_array_le(0xdead), [0xad, 0xde]);
|
||||
assert_eq!(u32_to_array_le(0xdeadbeef), [0xef, 0xbe, 0xad, 0xde]);
|
||||
assert_eq!(
|
||||
u64_to_array_le(0x1badcafedeadbeef),
|
||||
[0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endian_chunk_test() {
|
||||
let inp = [
|
||||
0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b, 0xfe, 0xca, 0xad, 0x1b, 0xce, 0xfa,
|
||||
0x01, 0x02,
|
||||
];
|
||||
let mut out = [0; 2];
|
||||
bytes_to_u64_slice_le(&inp, &mut out);
|
||||
assert_eq!(out, [0x1badcafedeadbeef, 0x0201face1badcafe]);
|
||||
}
|
||||
}
|
||||
113
example/halo2/src/error.rs
Normal file
113
example/halo2/src/error.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use std::fmt;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
Io(std::io::ErrorKind),
|
||||
/// VarInt was encoded in a non-minimal way
|
||||
PathNotFound,
|
||||
NonMinimalVarInt,
|
||||
/// Parsing error
|
||||
ParseFailed(&'static str),
|
||||
ParseIntError,
|
||||
AsyncChannelError,
|
||||
MalformedPacket,
|
||||
AddrParseError,
|
||||
BadVariableRefType,
|
||||
BadOperationType,
|
||||
BadConstraintType,
|
||||
InvalidParamName,
|
||||
InvalidParamType,
|
||||
MissingParams,
|
||||
VmError,
|
||||
BadContract,
|
||||
Groth16Error,
|
||||
RusqliteError(String),
|
||||
OperationFailed,
|
||||
ConnectFailed,
|
||||
ConnectTimeout,
|
||||
ChannelStopped,
|
||||
ChannelTimeout,
|
||||
ServiceStopped,
|
||||
Utf8Error,
|
||||
StrUtf8Error(String),
|
||||
NoteDecryptionFailed,
|
||||
ServicesError(&'static str),
|
||||
ZmqError(String),
|
||||
VerifyFailed,
|
||||
TryIntoError,
|
||||
TryFromError,
|
||||
JsonRpcError(String),
|
||||
RocksdbError(String),
|
||||
TreeFull,
|
||||
SerdeJsonError(String),
|
||||
SurfHttpError(String),
|
||||
EmptyPassword,
|
||||
TomlDeserializeError(String),
|
||||
TomlSerializeError(String),
|
||||
CashierNoReply,
|
||||
Base58EncodeError(String),
|
||||
Base58DecodeError(String),
|
||||
BadBTCAddress(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
|
||||
match *self {
|
||||
Error::PathNotFound => f.write_str("Cannot find home directory"),
|
||||
Error::Io(ref err) => write!(f, "io error:{:?}", err),
|
||||
Error::NonMinimalVarInt => f.write_str("non-minimal varint"),
|
||||
Error::ParseFailed(ref err) => write!(f, "parse failed: {}", err),
|
||||
Error::ParseIntError => f.write_str("Parse int error"),
|
||||
Error::AsyncChannelError => f.write_str("Async_channel error"),
|
||||
Error::MalformedPacket => f.write_str("Malformed packet"),
|
||||
Error::AddrParseError => f.write_str("Unable to parse address"),
|
||||
Error::BadVariableRefType => f.write_str("Bad variable ref type byte"),
|
||||
Error::BadOperationType => f.write_str("Bad operation type byte"),
|
||||
Error::BadConstraintType => f.write_str("Bad constraint type byte"),
|
||||
Error::InvalidParamName => f.write_str("Invalid param name"),
|
||||
Error::InvalidParamType => f.write_str("Invalid param type"),
|
||||
Error::MissingParams => f.write_str("Missing params"),
|
||||
Error::VmError => f.write_str("VM error"),
|
||||
Error::BadContract => f.write_str("Contract is poorly defined"),
|
||||
Error::Groth16Error => f.write_str("Groth16 error"),
|
||||
Error::RusqliteError(ref err) => write!(f, "Rusqlite error {}", err),
|
||||
Error::OperationFailed => f.write_str("Operation failed"),
|
||||
|
||||
Error::ConnectFailed => f.write_str("Connection failed"),
|
||||
Error::ConnectTimeout => f.write_str("Connection timed out"),
|
||||
Error::ChannelStopped => f.write_str("Channel stopped"),
|
||||
Error::ChannelTimeout => f.write_str("Channel timed out"),
|
||||
Error::ServiceStopped => f.write_str("Service stopped"),
|
||||
Error::Utf8Error => f.write_str("Malformed UTF8"),
|
||||
Error::StrUtf8Error(ref err) => write!(f, "Malformed UTF8: {}", err),
|
||||
Error::NoteDecryptionFailed => f.write_str("Unable to decrypt mint note"),
|
||||
Error::ServicesError(ref err) => write!(f, "Services error: {}", err),
|
||||
Error::ZmqError(ref err) => write!(f, "ZmqError: {}", err),
|
||||
Error::VerifyFailed => f.write_str("Verify failed"),
|
||||
Error::TryIntoError => f.write_str("TryInto error"),
|
||||
Error::TryFromError => f.write_str("TryFrom error"),
|
||||
Error::RocksdbError(ref err) => write!(f, "Rocksdb Error: {}", err),
|
||||
Error::JsonRpcError(ref err) => write!(f, "JsonRpc Error: {}", err),
|
||||
Error::TreeFull => f.write_str("MerkleTree is full"),
|
||||
Error::SerdeJsonError(ref err) => write!(f, "Json serialization error: {}", err),
|
||||
Error::SurfHttpError(ref err) => write!(f, "Surf Http error: {}", err),
|
||||
Error::EmptyPassword => f.write_str("Password is empty. Cannot create database"),
|
||||
Error::TomlDeserializeError(ref err) => write!(f, "Toml parsing error: {}", err),
|
||||
Error::TomlSerializeError(ref err) => write!(f, "Toml parsing error: {}", err),
|
||||
Error::Base58EncodeError(ref err) => write!(f, "bs58 encode error: {}", err),
|
||||
Error::Base58DecodeError(ref err) => write!(f, "bs58 decode error: {}", err),
|
||||
Error::CashierNoReply => f.write_str("Cashier did not reply with BTC address"),
|
||||
Error::BadBTCAddress(ref err) => write!(f, "could not parse BTC address: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Error {
|
||||
Error::Io(err.kind())
|
||||
}
|
||||
}
|
||||
@@ -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
870
example/halo2/src/serial.rs
Normal file
@@ -0,0 +1,870 @@
|
||||
use std::borrow::Cow;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::{io, mem};
|
||||
|
||||
use crate::endian;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
/// Encode an object into a vector
|
||||
pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
|
||||
let mut encoder = Vec::new();
|
||||
let len = data.encode(&mut encoder).unwrap();
|
||||
assert_eq!(len, encoder.len());
|
||||
encoder
|
||||
}
|
||||
|
||||
/// Encode an object into a hex-encoded string
|
||||
pub fn serialize_hex<T: Encodable + ?Sized>(data: &T) -> String {
|
||||
hex::encode(serialize(data))
|
||||
}
|
||||
|
||||
/// Deserialize an object from a vector, will error if said deserialization
|
||||
/// doesn't consume the entire vector.
|
||||
pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T> {
|
||||
let (rv, consumed) = deserialize_partial(data)?;
|
||||
|
||||
// Fail if data are not consumed entirely.
|
||||
if consumed == data.len() {
|
||||
Ok(rv)
|
||||
} else {
|
||||
Err(Error::ParseFailed(
|
||||
"data not consumed entirely when explicitly deserializing",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize an object from a vector, but will not report an error if said
|
||||
/// deserialization doesn't consume the entire vector.
|
||||
pub fn deserialize_partial<T: Decodable>(data: &[u8]) -> Result<(T, usize)> {
|
||||
let mut decoder = Cursor::new(data);
|
||||
let rv = Decodable::decode(&mut decoder)?;
|
||||
let consumed = decoder.position() as usize;
|
||||
|
||||
Ok((rv, consumed))
|
||||
}
|
||||
|
||||
/// Extensions of `Write` to encode data as per Bitcoin consensus
|
||||
pub trait WriteExt {
|
||||
/// Output a 64-bit uint
|
||||
fn write_u64(&mut self, v: u64) -> Result<()>;
|
||||
/// Output a 32-bit uint
|
||||
fn write_u32(&mut self, v: u32) -> Result<()>;
|
||||
/// Output a 16-bit uint
|
||||
fn write_u16(&mut self, v: u16) -> Result<()>;
|
||||
/// Output a 8-bit uint
|
||||
fn write_u8(&mut self, v: u8) -> Result<()>;
|
||||
|
||||
/// Output a 64-bit int
|
||||
fn write_i64(&mut self, v: i64) -> Result<()>;
|
||||
/// Output a 32-bit int
|
||||
fn write_i32(&mut self, v: i32) -> Result<()>;
|
||||
/// Output a 16-bit int
|
||||
fn write_i16(&mut self, v: i16) -> Result<()>;
|
||||
/// Output a 8-bit int
|
||||
fn write_i8(&mut self, v: i8) -> Result<()>;
|
||||
|
||||
/// Output a boolean
|
||||
fn write_bool(&mut self, v: bool) -> Result<()>;
|
||||
|
||||
/// Output a byte slice
|
||||
fn write_slice(&mut self, v: &[u8]) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Extensions of `Read` to decode data as per Bitcoin consensus
|
||||
pub trait ReadExt {
|
||||
/// Read a 64-bit uint
|
||||
fn read_u64(&mut self) -> Result<u64>;
|
||||
/// Read a 32-bit uint
|
||||
fn read_u32(&mut self) -> Result<u32>;
|
||||
/// Read a 16-bit uint
|
||||
fn read_u16(&mut self) -> Result<u16>;
|
||||
/// Read a 8-bit uint
|
||||
fn read_u8(&mut self) -> Result<u8>;
|
||||
|
||||
/// Read a 64-bit int
|
||||
fn read_i64(&mut self) -> Result<i64>;
|
||||
/// Read a 32-bit int
|
||||
fn read_i32(&mut self) -> Result<i32>;
|
||||
/// Read a 16-bit int
|
||||
fn read_i16(&mut self) -> Result<i16>;
|
||||
/// Read a 8-bit int
|
||||
fn read_i8(&mut self) -> Result<i8>;
|
||||
|
||||
/// Read a boolean
|
||||
fn read_bool(&mut self) -> Result<bool>;
|
||||
|
||||
/// Read a byte slice
|
||||
fn read_slice(&mut self, slice: &mut [u8]) -> Result<()>;
|
||||
}
|
||||
|
||||
macro_rules! encoder_fn {
|
||||
($name:ident, $val_type:ty, $writefn:ident) => {
|
||||
#[inline]
|
||||
fn $name(&mut self, v: $val_type) -> Result<()> {
|
||||
self.write_all(&endian::$writefn(v))
|
||||
.map_err(|e| Error::Io(e.kind()))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! decoder_fn {
|
||||
($name:ident, $val_type:ty, $readfn:ident, $byte_len: expr) => {
|
||||
#[inline]
|
||||
fn $name(&mut self) -> Result<$val_type> {
|
||||
assert_eq!(::std::mem::size_of::<$val_type>(), $byte_len); // size_of isn't a constfn in 1.22
|
||||
let mut val = [0; $byte_len];
|
||||
self.read_exact(&mut val[..])
|
||||
.map_err(|e| Error::Io(e.kind()))?;
|
||||
Ok(endian::$readfn(&val))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<W: Write> WriteExt for W {
|
||||
encoder_fn!(write_u64, u64, u64_to_array_le);
|
||||
encoder_fn!(write_u32, u32, u32_to_array_le);
|
||||
encoder_fn!(write_u16, u16, u16_to_array_le);
|
||||
encoder_fn!(write_i64, i64, i64_to_array_le);
|
||||
encoder_fn!(write_i32, i32, i32_to_array_le);
|
||||
encoder_fn!(write_i16, i16, i16_to_array_le);
|
||||
|
||||
#[inline]
|
||||
fn write_i8(&mut self, v: i8) -> Result<()> {
|
||||
self.write_all(&[v as u8]).map_err(|e| Error::Io(e.kind()))
|
||||
}
|
||||
#[inline]
|
||||
fn write_u8(&mut self, v: u8) -> Result<()> {
|
||||
self.write_all(&[v]).map_err(|e| Error::Io(e.kind()))
|
||||
}
|
||||
#[inline]
|
||||
fn write_bool(&mut self, v: bool) -> Result<()> {
|
||||
self.write_all(&[v as u8]).map_err(|e| Error::Io(e.kind()))
|
||||
}
|
||||
#[inline]
|
||||
fn write_slice(&mut self, v: &[u8]) -> Result<()> {
|
||||
self.write_all(v).map_err(|e| Error::Io(e.kind()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> ReadExt for R {
|
||||
decoder_fn!(read_u64, u64, slice_to_u64_le, 8);
|
||||
decoder_fn!(read_u32, u32, slice_to_u32_le, 4);
|
||||
decoder_fn!(read_u16, u16, slice_to_u16_le, 2);
|
||||
decoder_fn!(read_i64, i64, slice_to_i64_le, 8);
|
||||
decoder_fn!(read_i32, i32, slice_to_i32_le, 4);
|
||||
decoder_fn!(read_i16, i16, slice_to_i16_le, 2);
|
||||
|
||||
#[inline]
|
||||
fn read_u8(&mut self) -> Result<u8> {
|
||||
let mut slice = [0u8; 1];
|
||||
self.read_exact(&mut slice)?;
|
||||
Ok(slice[0])
|
||||
}
|
||||
#[inline]
|
||||
fn read_i8(&mut self) -> Result<i8> {
|
||||
let mut slice = [0u8; 1];
|
||||
self.read_exact(&mut slice)?;
|
||||
Ok(slice[0] as i8)
|
||||
}
|
||||
#[inline]
|
||||
fn read_bool(&mut self) -> Result<bool> {
|
||||
ReadExt::read_i8(self).map(|bit| bit != 0)
|
||||
}
|
||||
#[inline]
|
||||
fn read_slice(&mut self, slice: &mut [u8]) -> Result<()> {
|
||||
self.read_exact(slice).map_err(|e| Error::Io(e.kind()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Data which can be encoded in a consensus-consistent way
|
||||
pub trait Encodable {
|
||||
/// Encode an object with a well-defined format, should only ever error if
|
||||
/// the underlying `Write` errors. Returns the number of bytes written on
|
||||
/// success
|
||||
fn encode<W: io::Write>(&self, e: W) -> Result<usize>;
|
||||
}
|
||||
|
||||
/// Data which can be encoded in a consensus-consistent way
|
||||
pub trait Decodable: Sized {
|
||||
/// Decode an object with a well-defined format
|
||||
fn decode<D: io::Read>(d: D) -> Result<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
||||
pub struct VarInt(pub u64);
|
||||
|
||||
// Primitive types
|
||||
macro_rules! impl_int_encodable {
|
||||
($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
|
||||
impl Decodable for $ty {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
ReadExt::$meth_dec(&mut d).map($ty::from_le)
|
||||
}
|
||||
}
|
||||
impl Encodable for $ty {
|
||||
#[inline]
|
||||
fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize> {
|
||||
s.$meth_enc(self.to_le())?;
|
||||
Ok(mem::size_of::<$ty>())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_int_encodable!(u8, read_u8, write_u8);
|
||||
impl_int_encodable!(u16, read_u16, write_u16);
|
||||
impl_int_encodable!(u32, read_u32, write_u32);
|
||||
impl_int_encodable!(u64, read_u64, write_u64);
|
||||
impl_int_encodable!(i8, read_i8, write_i8);
|
||||
impl_int_encodable!(i16, read_i16, write_i16);
|
||||
impl_int_encodable!(i32, read_i32, write_i32);
|
||||
impl_int_encodable!(i64, read_i64, write_i64);
|
||||
|
||||
impl VarInt {
|
||||
/// Gets the length of this VarInt when encoded.
|
||||
/// Returns 1 for 0...0xFC, 3 for 0xFD...(2^16-1), 5 for 0x10000...(2^32-1),
|
||||
/// and 9 otherwise.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
match self.0 {
|
||||
0..=0xFC => 1,
|
||||
0xFD..=0xFFFF => 3,
|
||||
0x10000..=0xFFFFFFFF => 5,
|
||||
_ => 9,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for VarInt {
|
||||
#[inline]
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
match self.0 {
|
||||
0..=0xFC => {
|
||||
(self.0 as u8).encode(s)?;
|
||||
Ok(1)
|
||||
}
|
||||
0xFD..=0xFFFF => {
|
||||
s.write_u8(0xFD)?;
|
||||
(self.0 as u16).encode(s)?;
|
||||
Ok(3)
|
||||
}
|
||||
0x10000..=0xFFFFFFFF => {
|
||||
s.write_u8(0xFE)?;
|
||||
(self.0 as u32).encode(s)?;
|
||||
Ok(5)
|
||||
}
|
||||
_ => {
|
||||
s.write_u8(0xFF)?;
|
||||
(self.0 as u64).encode(s)?;
|
||||
Ok(9)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for VarInt {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let n = ReadExt::read_u8(&mut d)?;
|
||||
match n {
|
||||
0xFF => {
|
||||
let x = ReadExt::read_u64(&mut d)?;
|
||||
if x < 0x100000000 {
|
||||
Err(self::Error::NonMinimalVarInt)
|
||||
} else {
|
||||
Ok(VarInt(x))
|
||||
}
|
||||
}
|
||||
0xFE => {
|
||||
let x = ReadExt::read_u32(&mut d)?;
|
||||
if x < 0x10000 {
|
||||
Err(self::Error::NonMinimalVarInt)
|
||||
} else {
|
||||
Ok(VarInt(x as u64))
|
||||
}
|
||||
}
|
||||
0xFD => {
|
||||
let x = ReadExt::read_u16(&mut d)?;
|
||||
if x < 0xFD {
|
||||
Err(self::Error::NonMinimalVarInt)
|
||||
} else {
|
||||
Ok(VarInt(x as u64))
|
||||
}
|
||||
}
|
||||
n => Ok(VarInt(n as u64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Booleans
|
||||
impl Encodable for bool {
|
||||
#[inline]
|
||||
fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize> {
|
||||
s.write_bool(*self)?;
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for bool {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<bool> {
|
||||
ReadExt::read_bool(&mut d)
|
||||
}
|
||||
}
|
||||
|
||||
// Strings
|
||||
impl Encodable for String {
|
||||
#[inline]
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let b = self.as_bytes();
|
||||
let vi_len = VarInt(b.len() as u64).encode(&mut s)?;
|
||||
s.write_slice(&b)?;
|
||||
Ok(vi_len + b.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for String {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(d: D) -> Result<String> {
|
||||
String::from_utf8(Decodable::decode(d)?)
|
||||
.map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
|
||||
}
|
||||
}
|
||||
|
||||
// Cow<'static, str>
|
||||
impl Encodable for Cow<'static, str> {
|
||||
#[inline]
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let b = self.as_bytes();
|
||||
let vi_len = VarInt(b.len() as u64).encode(&mut s)?;
|
||||
s.write_slice(&b)?;
|
||||
Ok(vi_len + b.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Cow<'static, str> {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(d: D) -> Result<Cow<'static, str>> {
|
||||
String::from_utf8(Decodable::decode(d)?)
|
||||
.map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
|
||||
.map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
// Arrays
|
||||
macro_rules! impl_array {
|
||||
( $size:expr ) => {
|
||||
impl Encodable for [u8; $size] {
|
||||
#[inline]
|
||||
fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize> {
|
||||
s.write_slice(&self[..])?;
|
||||
Ok(self.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for [u8; $size] {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let mut ret = [0; $size];
|
||||
d.read_slice(&mut ret)?;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_array!(2);
|
||||
impl_array!(4);
|
||||
impl_array!(8);
|
||||
impl_array!(12);
|
||||
impl_array!(16);
|
||||
impl_array!(32);
|
||||
impl_array!(33);
|
||||
|
||||
// Options
|
||||
impl<T: Encodable> Encodable for Option<T> {
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
if let Some(v) = self {
|
||||
len += true.encode(&mut s)?;
|
||||
len += v.encode(&mut s)?;
|
||||
} else {
|
||||
len += false.encode(&mut s)?;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
impl<T: Decodable> Decodable for Option<T> {
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let valid: bool = Decodable::decode(&mut d)?;
|
||||
let mut val: Option<T> = None;
|
||||
|
||||
if valid {
|
||||
val = Some(Decodable::decode(&mut d)?);
|
||||
}
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encodable> Encodable for Vec<Option<T>> {
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
len += VarInt(self.len() as u64).encode(&mut s)?;
|
||||
for val in self {
|
||||
len += val.encode(&mut s)?;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
impl<T: Decodable> Decodable for Vec<Option<T>> {
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let len = VarInt::decode(&mut d)?.0;
|
||||
let mut ret = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
ret.push(Decodable::decode(&mut d)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
// Vectors
|
||||
#[macro_export]
|
||||
macro_rules! impl_vec {
|
||||
($type: ty) => {
|
||||
impl Encodable for Vec<$type> {
|
||||
#[inline]
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
len += VarInt(self.len() as u64).encode(&mut s)?;
|
||||
for c in self.iter() {
|
||||
len += c.encode(&mut s)?;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
impl Decodable for Vec<$type> {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let len = VarInt::decode(&mut d)?.0;
|
||||
let mut ret = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
ret.push(Decodable::decode(&mut d)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_vec!(SocketAddr);
|
||||
impl_vec!([u8; 32]);
|
||||
|
||||
impl Encodable for IpAddr {
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
match self {
|
||||
IpAddr::V4(ip) => {
|
||||
let version: u8 = 4;
|
||||
len += version.encode(&mut s)?;
|
||||
len += ip.octets().encode(s)?;
|
||||
}
|
||||
IpAddr::V6(ip) => {
|
||||
let version: u8 = 6;
|
||||
len += version.encode(&mut s)?;
|
||||
len += ip.octets().encode(s)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for IpAddr {
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let version: u8 = Decodable::decode(&mut d)?;
|
||||
match version {
|
||||
4 => {
|
||||
let addr: [u8; 4] = Decodable::decode(&mut d)?;
|
||||
Ok(IpAddr::from(addr))
|
||||
}
|
||||
6 => {
|
||||
let addr: [u8; 16] = Decodable::decode(&mut d)?;
|
||||
Ok(IpAddr::from(addr))
|
||||
}
|
||||
_ => Err(Error::ParseFailed("couldn't decode IpAddr")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for SocketAddr {
|
||||
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
len += self.ip().encode(&mut s)?;
|
||||
len += self.port().encode(s)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for SocketAddr {
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let ip = Decodable::decode(&mut d)?;
|
||||
let port: u16 = Decodable::decode(d)?;
|
||||
Ok(SocketAddr::new(ip, port))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_with_size<S: io::Write>(data: &[u8], mut s: S) -> Result<usize> {
|
||||
let vi_len = VarInt(data.len() as u64).encode(&mut s)?;
|
||||
s.write_slice(&data)?;
|
||||
Ok(vi_len + data.len())
|
||||
}
|
||||
|
||||
impl Encodable for Vec<u8> {
|
||||
#[inline]
|
||||
fn encode<S: io::Write>(&self, s: S) -> Result<usize> {
|
||||
encode_with_size(self, s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Vec<u8> {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
let len = VarInt::decode(&mut d)?.0 as usize;
|
||||
let mut ret = vec![0u8; len];
|
||||
d.read_slice(&mut ret)?;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Box<[u8]> {
|
||||
#[inline]
|
||||
fn encode<S: io::Write>(&self, s: S) -> Result<usize> {
|
||||
encode_with_size(self, s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Box<[u8]> {
|
||||
#[inline]
|
||||
fn decode<D: io::Read>(d: D) -> Result<Self> {
|
||||
<Vec<u8>>::decode(d).map(From::from)
|
||||
}
|
||||
}
|
||||
|
||||
// Tuples
|
||||
macro_rules! tuple_encode {
|
||||
($($x:ident),*) => (
|
||||
impl <$($x: Encodable),*> Encodable for ($($x),*) {
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn encode<S: io::Write>(
|
||||
&self,
|
||||
mut s: S,
|
||||
) -> Result<usize> {
|
||||
let &($(ref $x),*) = self;
|
||||
let mut len = 0;
|
||||
$(len += $x.encode(&mut s)?;)*
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($x: Decodable),*> Decodable for ($($x),*) {
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
||||
Ok(($({let $x = Decodable::decode(&mut d)?; $x }),*))
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
tuple_encode!(T0, T1);
|
||||
tuple_encode!(T0, T1, T2, T3);
|
||||
tuple_encode!(T0, T1, T2, T3, T4, T5);
|
||||
tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{deserialize, serialize, Error, Result, VarInt};
|
||||
use super::{deserialize_partial, Encodable};
|
||||
use crate::endian::{u16_to_array_le, u32_to_array_le, u64_to_array_le};
|
||||
use std::io;
|
||||
use std::mem::discriminant;
|
||||
|
||||
#[test]
|
||||
fn serialize_int_test() {
|
||||
// bool
|
||||
assert_eq!(serialize(&false), vec![0u8]);
|
||||
assert_eq!(serialize(&true), vec![1u8]);
|
||||
// u8
|
||||
assert_eq!(serialize(&1u8), vec![1u8]);
|
||||
assert_eq!(serialize(&0u8), vec![0u8]);
|
||||
assert_eq!(serialize(&255u8), vec![255u8]);
|
||||
// u16
|
||||
assert_eq!(serialize(&1u16), vec![1u8, 0]);
|
||||
assert_eq!(serialize(&256u16), vec![0u8, 1]);
|
||||
assert_eq!(serialize(&5000u16), vec![136u8, 19]);
|
||||
// u32
|
||||
assert_eq!(serialize(&1u32), vec![1u8, 0, 0, 0]);
|
||||
assert_eq!(serialize(&256u32), vec![0u8, 1, 0, 0]);
|
||||
assert_eq!(serialize(&5000u32), vec![136u8, 19, 0, 0]);
|
||||
assert_eq!(serialize(&500000u32), vec![32u8, 161, 7, 0]);
|
||||
assert_eq!(serialize(&168430090u32), vec![10u8, 10, 10, 10]);
|
||||
// i32
|
||||
assert_eq!(serialize(&-1i32), vec![255u8, 255, 255, 255]);
|
||||
assert_eq!(serialize(&-256i32), vec![0u8, 255, 255, 255]);
|
||||
assert_eq!(serialize(&-5000i32), vec![120u8, 236, 255, 255]);
|
||||
assert_eq!(serialize(&-500000i32), vec![224u8, 94, 248, 255]);
|
||||
assert_eq!(serialize(&-168430090i32), vec![246u8, 245, 245, 245]);
|
||||
assert_eq!(serialize(&1i32), vec![1u8, 0, 0, 0]);
|
||||
assert_eq!(serialize(&256i32), vec![0u8, 1, 0, 0]);
|
||||
assert_eq!(serialize(&5000i32), vec![136u8, 19, 0, 0]);
|
||||
assert_eq!(serialize(&500000i32), vec![32u8, 161, 7, 0]);
|
||||
assert_eq!(serialize(&168430090i32), vec![10u8, 10, 10, 10]);
|
||||
// u64
|
||||
assert_eq!(serialize(&1u64), vec![1u8, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(serialize(&256u64), vec![0u8, 1, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(serialize(&5000u64), vec![136u8, 19, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(serialize(&500000u64), vec![32u8, 161, 7, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(
|
||||
serialize(&723401728380766730u64),
|
||||
vec![10u8, 10, 10, 10, 10, 10, 10, 10]
|
||||
);
|
||||
// i64
|
||||
assert_eq!(
|
||||
serialize(&-1i64),
|
||||
vec![255u8, 255, 255, 255, 255, 255, 255, 255]
|
||||
);
|
||||
assert_eq!(
|
||||
serialize(&-256i64),
|
||||
vec![0u8, 255, 255, 255, 255, 255, 255, 255]
|
||||
);
|
||||
assert_eq!(
|
||||
serialize(&-5000i64),
|
||||
vec![120u8, 236, 255, 255, 255, 255, 255, 255]
|
||||
);
|
||||
assert_eq!(
|
||||
serialize(&-500000i64),
|
||||
vec![224u8, 94, 248, 255, 255, 255, 255, 255]
|
||||
);
|
||||
assert_eq!(
|
||||
serialize(&-723401728380766730i64),
|
||||
vec![246u8, 245, 245, 245, 245, 245, 245, 245]
|
||||
);
|
||||
assert_eq!(serialize(&1i64), vec![1u8, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(serialize(&256i64), vec![0u8, 1, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(serialize(&5000i64), vec![136u8, 19, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(serialize(&500000i64), vec![32u8, 161, 7, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(
|
||||
serialize(&723401728380766730i64),
|
||||
vec![10u8, 10, 10, 10, 10, 10, 10, 10]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_varint_test() {
|
||||
assert_eq!(serialize(&VarInt(10)), vec![10u8]);
|
||||
assert_eq!(serialize(&VarInt(0xFC)), vec![0xFCu8]);
|
||||
assert_eq!(serialize(&VarInt(0xFD)), vec![0xFDu8, 0xFD, 0]);
|
||||
assert_eq!(serialize(&VarInt(0xFFF)), vec![0xFDu8, 0xFF, 0xF]);
|
||||
assert_eq!(
|
||||
serialize(&VarInt(0xF0F0F0F)),
|
||||
vec![0xFEu8, 0xF, 0xF, 0xF, 0xF]
|
||||
);
|
||||
assert_eq!(
|
||||
serialize(&VarInt(0xF0F0F0F0F0E0)),
|
||||
vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]
|
||||
);
|
||||
assert_eq!(
|
||||
test_varint_encode(0xFF, &u64_to_array_le(0x100000000)).unwrap(),
|
||||
VarInt(0x100000000)
|
||||
);
|
||||
assert_eq!(
|
||||
test_varint_encode(0xFE, &u64_to_array_le(0x10000)).unwrap(),
|
||||
VarInt(0x10000)
|
||||
);
|
||||
assert_eq!(
|
||||
test_varint_encode(0xFD, &u64_to_array_le(0xFD)).unwrap(),
|
||||
VarInt(0xFD)
|
||||
);
|
||||
|
||||
// Test that length calc is working correctly
|
||||
test_varint_len(VarInt(0), 1);
|
||||
test_varint_len(VarInt(0xFC), 1);
|
||||
test_varint_len(VarInt(0xFD), 3);
|
||||
test_varint_len(VarInt(0xFFFF), 3);
|
||||
test_varint_len(VarInt(0x10000), 5);
|
||||
test_varint_len(VarInt(0xFFFFFFFF), 5);
|
||||
test_varint_len(VarInt(0xFFFFFFFF + 1), 9);
|
||||
test_varint_len(VarInt(u64::max_value()), 9);
|
||||
}
|
||||
|
||||
fn test_varint_len(varint: VarInt, expected: usize) {
|
||||
let mut encoder = io::Cursor::new(vec![]);
|
||||
assert_eq!(varint.encode(&mut encoder).unwrap(), expected);
|
||||
assert_eq!(varint.len(), expected);
|
||||
}
|
||||
|
||||
fn test_varint_encode(n: u8, x: &[u8]) -> Result<VarInt> {
|
||||
let mut input = [0u8; 9];
|
||||
input[0] = n;
|
||||
input[1..x.len() + 1].copy_from_slice(x);
|
||||
deserialize_partial::<VarInt>(&input).map(|t| t.0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_nonminimal_vec() {
|
||||
// Check the edges for variant int
|
||||
assert_eq!(
|
||||
discriminant(&test_varint_encode(0xFF, &u64_to_array_le(0x100000000 - 1)).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(&test_varint_encode(0xFE, &u32_to_array_le(0x10000 - 1)).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(&test_varint_encode(0xFD, &u16_to_array_le(0xFD - 1)).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]).unwrap_err()),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(
|
||||
&deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
|
||||
.unwrap_err()
|
||||
),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
assert_eq!(
|
||||
discriminant(
|
||||
&deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00])
|
||||
.unwrap_err()
|
||||
),
|
||||
discriminant(&Error::NonMinimalVarInt)
|
||||
);
|
||||
|
||||
let mut vec_256 = vec![0; 259];
|
||||
vec_256[0] = 0xfd;
|
||||
vec_256[1] = 0x00;
|
||||
vec_256[2] = 0x01;
|
||||
assert!(deserialize::<Vec<u8>>(&vec_256).is_ok());
|
||||
|
||||
let mut vec_253 = vec![0; 256];
|
||||
vec_253[0] = 0xfd;
|
||||
vec_253[1] = 0xfd;
|
||||
vec_253[2] = 0x00;
|
||||
assert!(deserialize::<Vec<u8>>(&vec_253).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_vector_test() {
|
||||
assert_eq!(serialize(&vec![1u8, 2, 3]), vec![3u8, 1, 2, 3]);
|
||||
// TODO: test vectors of more interesting objects
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_strbuf_test() {
|
||||
assert_eq!(
|
||||
serialize(&"Andrew".to_string()),
|
||||
vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_int_test() {
|
||||
// bool
|
||||
assert!((deserialize(&[58u8, 0]) as Result<bool>).is_err());
|
||||
assert_eq!(deserialize(&[58u8]).ok(), Some(true));
|
||||
assert_eq!(deserialize(&[1u8]).ok(), Some(true));
|
||||
assert_eq!(deserialize(&[0u8]).ok(), Some(false));
|
||||
assert!((deserialize(&[0u8, 1]) as Result<bool>).is_err());
|
||||
|
||||
// u8
|
||||
assert_eq!(deserialize(&[58u8]).ok(), Some(58u8));
|
||||
|
||||
// u16
|
||||
assert_eq!(deserialize(&[0x01u8, 0x02]).ok(), Some(0x0201u16));
|
||||
assert_eq!(deserialize(&[0xABu8, 0xCD]).ok(), Some(0xCDABu16));
|
||||
assert_eq!(deserialize(&[0xA0u8, 0x0D]).ok(), Some(0xDA0u16));
|
||||
let failure16: Result<u16> = deserialize(&[1u8]);
|
||||
assert!(failure16.is_err());
|
||||
|
||||
// u32
|
||||
assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0]).ok(), Some(0xCDABu32));
|
||||
assert_eq!(
|
||||
deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD]).ok(),
|
||||
Some(0xCDAB0DA0u32)
|
||||
);
|
||||
let failure32: Result<u32> = deserialize(&[1u8, 2, 3]);
|
||||
assert!(failure32.is_err());
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0]).ok(), Some(0xCDABi32));
|
||||
assert_eq!(
|
||||
deserialize(&[0xA0u8, 0x0D, 0xAB, 0x2D]).ok(),
|
||||
Some(0x2DAB0DA0i32)
|
||||
);
|
||||
let failurei32: Result<i32> = deserialize(&[1u8, 2, 3]);
|
||||
assert!(failurei32.is_err());
|
||||
|
||||
// u64
|
||||
assert_eq!(
|
||||
deserialize(&[0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]).ok(),
|
||||
Some(0xCDABu64)
|
||||
);
|
||||
assert_eq!(
|
||||
deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]).ok(),
|
||||
Some(0x99000099CDAB0DA0u64)
|
||||
);
|
||||
let failure64: Result<u64> = deserialize(&[1u8, 2, 3, 4, 5, 6, 7]);
|
||||
assert!(failure64.is_err());
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(
|
||||
deserialize(&[0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]).ok(),
|
||||
Some(0xCDABi64)
|
||||
);
|
||||
assert_eq!(
|
||||
deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]).ok(),
|
||||
Some(-0x66ffff663254f260i64)
|
||||
);
|
||||
let failurei64: Result<i64> = deserialize(&[1u8, 2, 3, 4, 5, 6, 7]);
|
||||
assert!(failurei64.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_vec_test() {
|
||||
assert_eq!(deserialize(&[3u8, 2, 3, 4]).ok(), Some(vec![2u8, 3, 4]));
|
||||
assert!((deserialize(&[4u8, 2, 3, 4, 5, 6]) as Result<Vec<u8>>).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_strbuf_test() {
|
||||
assert_eq!(
|
||||
deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
|
||||
Some("Andrew".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
|
||||
Some(::std::borrow::Cow::Borrowed("Andrew"))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
22
example/vm/src/lib.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
//pub mod async_serial;
|
||||
//pub mod blockchain;
|
||||
//pub mod bls_extensions;
|
||||
//pub mod circuit;
|
||||
//pub mod cli;
|
||||
//pub mod crypto;
|
||||
pub mod arith_chip;
|
||||
pub mod endian;
|
||||
pub mod error;
|
||||
//pub mod net;
|
||||
//pub mod rpc;
|
||||
pub mod serial;
|
||||
//pub mod service;
|
||||
//pub mod state;
|
||||
//pub mod system;
|
||||
//pub mod tx;
|
||||
//pub mod util;
|
||||
//pub mod vm;
|
||||
pub mod vm2;
|
||||
pub mod vm2_serial;
|
||||
//pub mod vm_serial;
|
||||
//pub mod wallet;
|
||||
1531
examples/halo2/Cargo.lock
generated
1531
examples/halo2/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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());
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod constants;
|
||||
pub mod crypto;
|
||||
pub mod proof;
|
||||
pub mod spec;
|
||||
56
proof/burn.zk
Normal file
56
proof/burn.zk
Normal 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
46
proof/mint.zk
Normal 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
7
run_burn_zk.sh
Executable 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
7
run_mint_zk.sh
Executable 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
|
||||
|
||||
187
script/research/halo/plonk_kate.sage
Normal file
187
script/research/halo/plonk_kate.sage
Normal 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
|
||||
33
script/research/rsa_accum.sage
Normal file
33
script/research/rsa_accum.sage
Normal 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
5
script/to_html.sh
Executable 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
41
script/zk.vim
Normal 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
402
script/zkas.py
Normal 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
0
script/zkas/__init__.py
Normal file
50
script/zkas/bincode_output.py
Normal file
50
script/zkas/bincode_output.py
Normal 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)
|
||||
|
||||
21
script/zkas/text_output.py
Normal file
21
script/zkas/text_output.py
Normal 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
77
script/zkas/types.py
Normal 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())
|
||||
@@ -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> {
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
|
||||
570
src/bin/mint.rs
570
src/bin/mint.rs
@@ -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
180
src/bin/tx2.rs
Normal 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
153
src/bin/vm2.rs
Normal 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
210
src/bin/vm2_burn.rs
Normal 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(())
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
@@ -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
@@ -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
208
src/crypto/arith_chip.rs
Normal 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(())
|
||||
},
|
||||
)
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -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
11
src/crypto/constants.rs
Normal 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;
|
||||
214
src/crypto/constants/fixed_bases.rs
Normal file
214
src/crypto/constants/fixed_bases.rs
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
2965
src/crypto/constants/fixed_bases/commit_ivk_r.rs
Normal file
2965
src/crypto/constants/fixed_bases/commit_ivk_r.rs
Normal file
File diff suppressed because it is too large
Load Diff
2965
src/crypto/constants/fixed_bases/note_commit_r.rs
Normal file
2965
src/crypto/constants/fixed_bases/note_commit_r.rs
Normal file
File diff suppressed because it is too large
Load Diff
2963
src/crypto/constants/fixed_bases/nullifier_k.rs
Normal file
2963
src/crypto/constants/fixed_bases/nullifier_k.rs
Normal file
File diff suppressed because it is too large
Load Diff
2965
src/crypto/constants/fixed_bases/spend_auth_g.rs
Normal file
2965
src/crypto/constants/fixed_bases/spend_auth_g.rs
Normal file
File diff suppressed because it is too large
Load Diff
2965
src/crypto/constants/fixed_bases/value_commit_r.rs
Normal file
2965
src/crypto/constants/fixed_bases/value_commit_r.rs
Normal file
File diff suppressed because it is too large
Load Diff
818
src/crypto/constants/fixed_bases/value_commit_v.rs
Normal file
818
src/crypto/constants/fixed_bases/value_commit_v.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
261
src/crypto/constants/sinsemilla.rs
Normal file
261
src/crypto/constants/sinsemilla.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
31
src/crypto/constants/util.rs
Normal file
31
src/crypto/constants/util.rs
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
453
src/crypto/merkle_old.rs
Normal 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),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)?)
|
||||
}
|
||||
|
||||
@@ -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(¶ms.vk);
|
||||
Ok((params, pvk))
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
109
src/crypto/proof.rs
Normal 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(¶ms, &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(¶ms, &c).unwrap();
|
||||
let pk = plonk::keygen_pk(¶ms, 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))
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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(), ¤t, sibling)
|
||||
} else {
|
||||
MerkleNode::combine(level.into(), sibling, ¤t)
|
||||
};
|
||||
}
|
||||
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)?)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
334
src/error.rs
334
src/error.rs
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
113
src/lib.rs
113
src/lib.rs
@@ -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(¶ms.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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
//pub mod cashier;
|
||||
pub mod bridge;
|
||||
pub mod gateway;
|
||||
pub mod reqrep;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
58
src/state.rs
58
src/state.rs
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)?,
|
||||
})
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user