|
@@ -20,7 +20,9 @@ use darkfi_sdk::crypto::{
|
|
|
constants::{
|
|
constants::{
|
|
|
sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
|
|
sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
|
|
|
util::gen_const_array,
|
|
util::gen_const_array,
|
|
|
- NullifierK, OrchardFixedBases, OrchardFixedBasesFull, MERKLE_DEPTH_ORCHARD,
|
|
|
|
|
|
|
+ NullifierK, OrchardFixedBases,
|
|
|
|
|
+ OrchardFixedBasesFull::ValueCommitR,
|
|
|
|
|
+ MERKLE_DEPTH_ORCHARD,
|
|
|
},
|
|
},
|
|
|
MerkleNode,
|
|
MerkleNode,
|
|
|
};
|
|
};
|
|
@@ -30,8 +32,8 @@ use halo2_gadgets::{
|
|
|
FixedPoint, FixedPointBaseField, NonIdentityPoint, ScalarFixed,
|
|
FixedPoint, FixedPointBaseField, NonIdentityPoint, ScalarFixed,
|
|
|
},
|
|
},
|
|
|
poseidon::{
|
|
poseidon::{
|
|
|
- primitives as poseidon, Hash as PoseidonHash, Pow5Chip as PoseidonChip,
|
|
|
|
|
- Pow5Config as PoseidonConfig,
|
|
|
|
|
|
|
+ primitives::{ConstantLength, P128Pow5T3},
|
|
|
|
|
+ Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig,
|
|
|
},
|
|
},
|
|
|
sinsemilla::{
|
|
sinsemilla::{
|
|
|
chip::{SinsemillaChip, SinsemillaConfig},
|
|
chip::{SinsemillaChip, SinsemillaConfig},
|
|
@@ -40,39 +42,50 @@ use halo2_gadgets::{
|
|
|
MerklePath,
|
|
MerklePath,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
|
|
|
|
|
|
+ utilities::lookup_range_check::LookupRangeCheckConfig,
|
|
|
};
|
|
};
|
|
|
use halo2_proofs::{
|
|
use halo2_proofs::{
|
|
|
- circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value},
|
|
|
|
|
- pasta::{group::Curve, pallas, Fp},
|
|
|
|
|
- plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
|
|
|
|
|
|
|
+ circuit::{floor_planner, AssignedCell, Layouter, Value},
|
|
|
|
|
+ pasta::{group::Curve, pallas},
|
|
|
|
|
+ plonk,
|
|
|
|
|
+ plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn},
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-use crate::zk::gadget::{
|
|
|
|
|
- arithmetic::{ArithChip, ArithConfig, ArithInstruction},
|
|
|
|
|
- less_than::{LessThanChip, LessThanConfig},
|
|
|
|
|
- native_range_check::NativeRangeCheckChip,
|
|
|
|
|
|
|
+use crate::zk::{
|
|
|
|
|
+ assign_free_advice,
|
|
|
|
|
+ gadget::{
|
|
|
|
|
+ arithmetic::{ArithChip, ArithConfig, ArithInstruction},
|
|
|
|
|
+ less_than::{LessThanChip, LessThanConfig},
|
|
|
|
|
+ native_range_check::NativeRangeCheckChip,
|
|
|
|
|
+ },
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const WINDOW_SIZE: usize = 3;
|
|
|
|
|
-const NUM_OF_BITS: usize = 254;
|
|
|
|
|
-const NUM_OF_WINDOWS: usize = 85;
|
|
|
|
|
|
|
+
|
|
|
|
|
+/// Public input offset for the lead coin C2 nonce
|
|
|
|
|
+const LEADCOIN_C2_NONCE_OFFSET: usize = 0;
|
|
|
|
|
+/// Public input offset for lead coin public key X coordinate
|
|
|
|
|
+const LEADCOIN_PK_X_OFFSET: usize = 1;
|
|
|
|
|
+/// Public input offset for lead coin public key Y coordinate
|
|
|
|
|
+const LEADCOIN_PK_Y_OFFSET: usize = 2;
|
|
|
|
|
+/// Public input offset for the lottery target lhs
|
|
|
|
|
+const LEADCOIN_Y_BASE_OFFSET: usize = 3;
|
|
|
|
|
+/// Derivation prefix for the nullifier PRF
|
|
|
const PRF_NULLIFIER_PREFIX: u64 = 0;
|
|
const PRF_NULLIFIER_PREFIX: u64 = 0;
|
|
|
|
|
|
|
|
|
|
+/// Circuit configuration for the crypsinous leader proof
|
|
|
#[derive(Clone, Debug)]
|
|
#[derive(Clone, Debug)]
|
|
|
pub struct LeadConfig {
|
|
pub struct LeadConfig {
|
|
|
primary: Column<InstanceColumn>,
|
|
primary: Column<InstanceColumn>,
|
|
|
- advices: [Column<Advice>; 10],
|
|
|
|
|
|
|
+ advices: [Column<Advice>; 14],
|
|
|
ecc_config: EccConfig<OrchardFixedBases>,
|
|
ecc_config: EccConfig<OrchardFixedBases>,
|
|
|
poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
|
|
poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
|
|
|
- merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
|
|
|
- merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
|
|
|
sinsemilla_config_1:
|
|
sinsemilla_config_1:
|
|
|
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
|
- _sinsemilla_config_2:
|
|
|
|
|
|
|
+ sinsemilla_config_2:
|
|
|
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
|
-
|
|
|
|
|
- lessthan_config: LessThanConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>,
|
|
|
|
|
|
|
+ merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
|
|
|
+ merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
|
|
|
|
+ lessthan_config: LessThanConfig<3, 253, 85>,
|
|
|
arith_config: ArithConfig,
|
|
arith_config: ArithConfig,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -97,7 +110,7 @@ impl LeadConfig {
|
|
|
MerkleChip::construct(self.merkle_config_2.clone())
|
|
MerkleChip::construct(self.merkle_config_2.clone())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fn lessthan_chip(&self) -> LessThanChip<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS> {
|
|
|
|
|
|
|
+ fn lessthan_chip(&self) -> LessThanChip<3, 253, 85> {
|
|
|
LessThanChip::construct(self.lessthan_config.clone())
|
|
LessThanChip::construct(self.lessthan_config.clone())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -106,69 +119,62 @@ impl LeadConfig {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const LEAD_COIN_NONCE2_OFFSET: usize = 0;
|
|
|
|
|
-const LEAD_COIN_PK_X_OFFSET: usize = 1;
|
|
|
|
|
-const LEAD_COIN_PK_Y_OFFSET: usize = 2;
|
|
|
|
|
-const LEAD_Y_COMMIT_BASE_OFFSET: usize = 3;
|
|
|
|
|
-
|
|
|
|
|
-#[derive(Default, Debug)]
|
|
|
|
|
|
|
+/// Circuit implementation for the crypsinous leader proof
|
|
|
|
|
+#[derive(Default, Clone, Debug)]
|
|
|
pub struct LeadContract {
|
|
pub struct LeadContract {
|
|
|
- pub path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
|
|
|
|
|
- /// path of coin1 commitment
|
|
|
|
|
- pub sk: Value<pallas::Base>,
|
|
|
|
|
- /// coin secret key
|
|
|
|
|
- pub root_sk: Value<pallas::Base>,
|
|
|
|
|
- /// root to secret key
|
|
|
|
|
- pub path_sk: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
|
|
|
|
|
- /// path to the secret key rooted by `root_sk`
|
|
|
|
|
- pub coin_timestamp: Value<pallas::Base>,
|
|
|
|
|
- /// $\tau$ or coin index
|
|
|
|
|
- pub coin_nonce: Value<pallas::Base>,
|
|
|
|
|
- /// coin nonce $\rho_{c1}$
|
|
|
|
|
|
|
+ /// Merkle path to the commitment for `coin_1`
|
|
|
|
|
+ pub coin1_commit_merkle_path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
|
|
|
|
|
+ /// Merkle root to the commitment of `coin_1` in the Merkle tree of commitments
|
|
|
|
|
+ pub coin1_commit_root: Value<pallas::Base>,
|
|
|
|
|
+ /// `coin_1` leaf position in the Merkle tree of coin commitments
|
|
|
|
|
+ pub coin1_commit_leaf_pos: Value<u32>,
|
|
|
|
|
+ /// `coin_1` secret key.
|
|
|
|
|
+ pub coin1_sk: Value<pallas::Base>,
|
|
|
|
|
+ /// Merkle root of the `coin_1` secret key in the Merkle tree of secret keys.
|
|
|
|
|
+ pub coin1_sk_root: Value<pallas::Base>,
|
|
|
|
|
+ /// Merkle path to the secret key of `coin_1` in the Merkle tree of secret keys.
|
|
|
|
|
+ pub coin1_sk_merkle_path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
|
|
|
|
|
+ /// $\tau$ (in the crypsinous paper), can be slot index, or coin timestamp.
|
|
|
|
|
+ /// Used only in the public key calculation.
|
|
|
|
|
+ pub coin1_timestamp: Value<pallas::Base>,
|
|
|
|
|
+ /// `coin_1` nonce, a random sampled value. `coin_2` nonce is calculated
|
|
|
|
|
+ /// inside the circuit as `Hash(coin1_nonce || coin1_sk_root)`, assuming
|
|
|
|
|
+ /// `Hash` is a function that satisfies a PRF definition.
|
|
|
|
|
+ pub coin1_nonce: Value<pallas::Base>,
|
|
|
|
|
+ /// Blinding factor for the commitment of `coin_1`
|
|
|
pub coin1_blind: Value<pallas::Scalar>,
|
|
pub coin1_blind: Value<pallas::Scalar>,
|
|
|
- /// coin blind r
|
|
|
|
|
- pub coin1_sn: Value<pallas::Base>,
|
|
|
|
|
- /// coin serial number
|
|
|
|
|
- pub value: Value<pallas::Base>,
|
|
|
|
|
- /// coin value
|
|
|
|
|
|
|
+ /// Serial number for `coin_1`
|
|
|
|
|
+ pub coin1_serial: Value<pallas::Base>,
|
|
|
|
|
+ /// Value of `coin_1`
|
|
|
|
|
+ pub coin1_value: Value<pallas::Base>,
|
|
|
|
|
+ /// Blinding factor for the commitment of `coin_2`
|
|
|
pub coin2_blind: Value<pallas::Scalar>,
|
|
pub coin2_blind: Value<pallas::Scalar>,
|
|
|
- /// coin2 blind r2
|
|
|
|
|
|
|
+ /// `coin_2` pedersen commitment point
|
|
|
pub coin2_commit: Value<pallas::Point>,
|
|
pub coin2_commit: Value<pallas::Point>,
|
|
|
- /// coin2 commitment
|
|
|
|
|
- pub cm_pos: Value<u32>,
|
|
|
|
|
- /// coin position in the merkle tree
|
|
|
|
|
- pub slot: Value<pallas::Base>,
|
|
|
|
|
- /// slot index
|
|
|
|
|
|
|
+ /// Random value derived from `eta` used for constraining `rho`
|
|
|
pub mau_rho: Value<pallas::Scalar>,
|
|
pub mau_rho: Value<pallas::Scalar>,
|
|
|
- /// random value derived from eta used for constraining \rho
|
|
|
|
|
|
|
+ /// Random value derived from `eta` used for calculating `y`.
|
|
|
pub mau_y: Value<pallas::Scalar>,
|
|
pub mau_y: Value<pallas::Scalar>,
|
|
|
- /// random value derived from eta used for calculating y
|
|
|
|
|
- pub root_cm: Value<pallas::Base>,
|
|
|
|
|
- /// root to commitment in the mekle tree
|
|
|
|
|
|
|
+ /// First coefficient in 1-term T (target function) approximation.
|
|
|
|
|
+ /// sigma1 and sigma2 is not the capital sigma from the paper, but
|
|
|
|
|
+ /// the whole coefficient multiplied with the absolute stake.
|
|
|
pub sigma1: Value<pallas::Base>,
|
|
pub sigma1: Value<pallas::Base>,
|
|
|
- /// first coefficient in 1-term T (target function) approximation
|
|
|
|
|
|
|
+ /// Second coefficient in 2-term T (target function) approximation
|
|
|
pub sigma2: Value<pallas::Base>,
|
|
pub sigma2: Value<pallas::Base>,
|
|
|
- /// second coefficient in 2-term T (target function) approximation
|
|
|
|
|
- /// constrained nonce $\rho$
|
|
|
|
|
|
|
+ /// Constrained nonce `rho`.
|
|
|
pub rho: Value<pallas::Point>,
|
|
pub rho: Value<pallas::Point>,
|
|
|
- //pub eta : Option<u32>,
|
|
|
|
|
- //pub h : Option<u32>, // hash of this data
|
|
|
|
|
- //pub ptr: Option<u32>, //hash of the previous block
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-impl UtilitiesInstructions<pallas::Base> for LeadContract {
|
|
|
|
|
- type Var = AssignedCell<Fp, Fp>;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Circuit<pallas::Base> for LeadContract {
|
|
impl Circuit<pallas::Base> for LeadContract {
|
|
|
type Config = LeadConfig;
|
|
type Config = LeadConfig;
|
|
|
- type FloorPlanner = SimpleFloorPlanner;
|
|
|
|
|
|
|
+ type FloorPlanner = floor_planner::V1;
|
|
|
|
|
|
|
|
fn without_witnesses(&self) -> Self {
|
|
fn without_witnesses(&self) -> Self {
|
|
|
Self::default()
|
|
Self::default()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
|
|
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
|
|
|
|
|
+ // Advice columns used in the circuit
|
|
|
let advices = [
|
|
let advices = [
|
|
|
meta.advice_column(),
|
|
meta.advice_column(),
|
|
|
meta.advice_column(),
|
|
meta.advice_column(),
|
|
@@ -180,14 +186,21 @@ impl Circuit<pallas::Base> for LeadContract {
|
|
|
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 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();
|
|
let primary = meta.instance_column();
|
|
|
meta.enable_equality(primary);
|
|
meta.enable_equality(primary);
|
|
|
|
|
|
|
|
|
|
+ // Permutation over all advice columns
|
|
|
for advice in advices.iter() {
|
|
for advice in advices.iter() {
|
|
|
meta.enable_equality(*advice);
|
|
meta.enable_equality(*advice);
|
|
|
}
|
|
}
|
|
@@ -202,28 +215,35 @@ impl Circuit<pallas::Base> for LeadContract {
|
|
|
meta.fixed_column(),
|
|
meta.fixed_column(),
|
|
|
meta.fixed_column(),
|
|
meta.fixed_column(),
|
|
|
];
|
|
];
|
|
|
-
|
|
|
|
|
let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
|
|
let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
|
|
|
let rc_b = lagrange_coeffs[5..8].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]);
|
|
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);
|
|
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
|
|
|
|
|
|
|
|
|
|
+ // Configuration for curve point operations. This uses 10 advice columns.
|
|
|
let ecc_config = EccChip::<OrchardFixedBases>::configure(
|
|
let ecc_config = EccChip::<OrchardFixedBases>::configure(
|
|
|
meta,
|
|
meta,
|
|
|
- advices[0..10].try_into().expect("wrong slice size"),
|
|
|
|
|
|
|
+ advices[..10].try_into().unwrap(),
|
|
|
lagrange_coeffs,
|
|
lagrange_coeffs,
|
|
|
range_check,
|
|
range_check,
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- let poseidon_config = PoseidonChip::configure::<poseidon::P128Pow5T3>(
|
|
|
|
|
|
|
+ let poseidon_config = PoseidonChip::configure::<P128Pow5T3>(
|
|
|
meta,
|
|
meta,
|
|
|
- advices[6..9].try_into().unwrap(),
|
|
|
|
|
- advices[5],
|
|
|
|
|
|
|
+ advices[10..13].try_into().unwrap(),
|
|
|
|
|
+ advices[13],
|
|
|
rc_a,
|
|
rc_a,
|
|
|
rc_b,
|
|
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, merkle_config_1) = {
|
|
|
let sinsemilla_config_1 = SinsemillaChip::configure(
|
|
let sinsemilla_config_1 = SinsemillaChip::configure(
|
|
|
meta,
|
|
meta,
|
|
@@ -247,34 +267,34 @@ impl Circuit<pallas::Base> for LeadContract {
|
|
|
range_check,
|
|
range_check,
|
|
|
);
|
|
);
|
|
|
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
|
|
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
|
|
|
-
|
|
|
|
|
(sinsemilla_config_2, merkle_config_2)
|
|
(sinsemilla_config_2, merkle_config_2)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // Lookup table for native range checks and less-than check
|
|
|
let k_values_table = meta.lookup_table_column();
|
|
let k_values_table = meta.lookup_table_column();
|
|
|
-
|
|
|
|
|
let lessthan_config = {
|
|
let lessthan_config = {
|
|
|
- let a = meta.advice_column();
|
|
|
|
|
- let b = meta.advice_column();
|
|
|
|
|
- let a_offset = meta.advice_column();
|
|
|
|
|
-
|
|
|
|
|
- let z1 = meta.advice_column();
|
|
|
|
|
- let z2 = meta.advice_column();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ let a = advices[10];
|
|
|
|
|
+ let b = advices[11];
|
|
|
|
|
+ let a_offset = advices[12];
|
|
|
|
|
+ let z1 = advices[13];
|
|
|
|
|
+ let z2 = advices[14];
|
|
|
|
|
+ */
|
|
|
|
|
+ let a = advices[9];
|
|
|
|
|
+ let b = advices[10];
|
|
|
|
|
+ let a_offset = advices[11];
|
|
|
|
|
+ let z1 = advices[12];
|
|
|
|
|
+ let z2 = advices[13];
|
|
|
let constants = meta.fixed_column();
|
|
let constants = meta.fixed_column();
|
|
|
meta.enable_constant(constants);
|
|
meta.enable_constant(constants);
|
|
|
|
|
+ meta.enable_equality(a);
|
|
|
|
|
+ meta.enable_equality(b);
|
|
|
|
|
+ meta.enable_equality(a_offset);
|
|
|
|
|
|
|
|
- LessThanChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::configure(
|
|
|
|
|
- meta,
|
|
|
|
|
- a,
|
|
|
|
|
- b,
|
|
|
|
|
- a_offset,
|
|
|
|
|
- z1,
|
|
|
|
|
- z2,
|
|
|
|
|
- k_values_table,
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ LessThanChip::<3, 253, 85>::configure(meta, a, b, a_offset, z1, z2, k_values_table)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // Configuration for the arithmetic chip
|
|
|
let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
|
|
let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
|
|
|
|
|
|
|
|
LeadConfig {
|
|
LeadConfig {
|
|
@@ -282,10 +302,10 @@ impl Circuit<pallas::Base> for LeadContract {
|
|
|
advices,
|
|
advices,
|
|
|
ecc_config,
|
|
ecc_config,
|
|
|
poseidon_config,
|
|
poseidon_config,
|
|
|
|
|
+ sinsemilla_config_1,
|
|
|
|
|
+ sinsemilla_config_2,
|
|
|
merkle_config_1,
|
|
merkle_config_1,
|
|
|
merkle_config_2,
|
|
merkle_config_2,
|
|
|
- sinsemilla_config_1,
|
|
|
|
|
- _sinsemilla_config_2: sinsemilla_config_2,
|
|
|
|
|
lessthan_config,
|
|
lessthan_config,
|
|
|
arith_config,
|
|
arith_config,
|
|
|
}
|
|
}
|
|
@@ -295,406 +315,446 @@ impl Circuit<pallas::Base> for LeadContract {
|
|
|
&self,
|
|
&self,
|
|
|
config: Self::Config,
|
|
config: Self::Config,
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
- ) -> Result<(), Error> {
|
|
|
|
|
- let less_than_chip = config.lessthan_chip();
|
|
|
|
|
- NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::load_k_table(
|
|
|
|
|
|
|
+ ) -> Result<(), plonk::Error> {
|
|
|
|
|
+ // Initialize necessary chips
|
|
|
|
|
+ let lessthan_chip = config.lessthan_chip();
|
|
|
|
|
+ NativeRangeCheckChip::<3, 253, 85>::load_k_table(
|
|
|
&mut layouter,
|
|
&mut layouter,
|
|
|
config.lessthan_config.k_values_table,
|
|
config.lessthan_config.k_values_table,
|
|
|
)?;
|
|
)?;
|
|
|
SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
|
|
SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
|
|
|
let ecc_chip = config.ecc_chip();
|
|
let ecc_chip = config.ecc_chip();
|
|
|
- let ar_chip = config.arith_chip();
|
|
|
|
|
- let _ps_chip = config.poseidon_chip();
|
|
|
|
|
-
|
|
|
|
|
- // ===============
|
|
|
|
|
- // load witnesses
|
|
|
|
|
- // ===============
|
|
|
|
|
-
|
|
|
|
|
- // prefix to the pseudo-random-function that prefix input
|
|
|
|
|
- // to the nullifier poseidon hash
|
|
|
|
|
- let prf_nullifier_prefix_base = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "PRF NULLIFIER PREFIX BASE"),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
|
|
+ let arith_chip = config.arith_chip();
|
|
|
|
|
+
|
|
|
|
|
+ // ====================================
|
|
|
|
|
+ // Load witness values into the circuit
|
|
|
|
|
+ // ====================================
|
|
|
|
|
+ let prf_nullifier_prefix_base = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness nullifier prefix"),
|
|
|
|
|
+ config.advices[8],
|
|
|
Value::known(pallas::Base::from(PRF_NULLIFIER_PREFIX)),
|
|
Value::known(pallas::Base::from(PRF_NULLIFIER_PREFIX)),
|
|
|
)?;
|
|
)?;
|
|
|
- // staking coin nonce
|
|
|
|
|
- let coin_nonce: AssignedCell<Fp, Fp> = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "load coin nonce"),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
- self.coin_nonce,
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_commit_merkle_path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> = self
|
|
|
|
|
+ .coin1_commit_merkle_path
|
|
|
|
|
+ .map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_commit_root = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin_commitment_root"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_commit_root,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_sk = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_sk"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_sk,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_sk_root = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_sk_root"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_sk_root,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_sk_merkle_path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> =
|
|
|
|
|
+ self.coin1_sk_merkle_path.map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_timestamp = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_timestamp"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_timestamp,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_nonce = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_nonce"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_nonce,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- let coin1_sn: AssignedCell<Fp, Fp> = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "load coin1 sn"),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
- self.coin1_sn,
|
|
|
|
|
|
|
+ let coin1_blind = ScalarFixed::new(
|
|
|
|
|
+ ecc_chip.clone(),
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_blind"),
|
|
|
|
|
+ self.coin1_blind,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- // staking coin value
|
|
|
|
|
- let coin_value = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "load coin value"),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
- self.value,
|
|
|
|
|
|
|
+ let coin1_serial = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_serial"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_serial,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- // staking coin secret key
|
|
|
|
|
- let _root_sk =
|
|
|
|
|
- self.load_private(layouter.namespace(|| "root sk"), config.advices[0], self.root_sk)?;
|
|
|
|
|
|
|
+ let coin1_value = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness coin1_value"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ self.coin1_value,
|
|
|
|
|
+ )?;
|
|
|
|
|
|
|
|
- let root_cm =
|
|
|
|
|
- self.load_private(layouter.namespace(|| ""), config.advices[0], self.root_cm)?;
|
|
|
|
|
|
|
+ let coin2_blind = ScalarFixed::new(
|
|
|
|
|
+ ecc_chip.clone(),
|
|
|
|
|
+ layouter.namespace(|| "witness coin2_blind"),
|
|
|
|
|
+ self.coin2_blind,
|
|
|
|
|
+ )?;
|
|
|
|
|
|
|
|
- // staking coin secret key
|
|
|
|
|
- let sk: AssignedCell<Fp, Fp> =
|
|
|
|
|
- self.load_private(layouter.namespace(|| "sk"), config.advices[0], self.sk)?;
|
|
|
|
|
|
|
+ let coin2_commit = NonIdentityPoint::new(
|
|
|
|
|
+ ecc_chip.clone(),
|
|
|
|
|
+ layouter.namespace(|| "witness coin2_commit"),
|
|
|
|
|
+ self.coin2_commit.as_ref().map(|cm| cm.to_affine()),
|
|
|
|
|
+ )?;
|
|
|
|
|
|
|
|
- let sigma1 = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "load sigma1 "),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
|
|
+ let mau_rho = ScalarFixed::new(
|
|
|
|
|
+ ecc_chip.clone(),
|
|
|
|
|
+ layouter.namespace(|| "witness mau_rho"),
|
|
|
|
|
+ self.mau_rho,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let mau_y =
|
|
|
|
|
+ ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "witness mau_y"), self.mau_y)?;
|
|
|
|
|
+
|
|
|
|
|
+ let sigma1 = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness sigma1"),
|
|
|
|
|
+ config.advices[8],
|
|
|
self.sigma1,
|
|
self.sigma1,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- let sigma2 = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "load sigma2 "),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
|
|
+ let sigma2 = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness sigma2"),
|
|
|
|
|
+ config.advices[8],
|
|
|
self.sigma2,
|
|
self.sigma2,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- let one = self.load_private(
|
|
|
|
|
- layouter.namespace(|| "one"),
|
|
|
|
|
- config.advices[0],
|
|
|
|
|
|
|
+ let rho = NonIdentityPoint::new(
|
|
|
|
|
+ ecc_chip.clone(),
|
|
|
|
|
+ layouter.namespace(|| "witness rho"),
|
|
|
|
|
+ self.rho.as_ref().map(|cm| cm.to_affine()),
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let zero = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness constant zero"),
|
|
|
|
|
+ config.advices[8],
|
|
|
|
|
+ Value::known(pallas::Base::zero()),
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ let one = assign_free_advice(
|
|
|
|
|
+ layouter.namespace(|| "witness constant one"),
|
|
|
|
|
+ config.advices[8],
|
|
|
Value::known(pallas::Base::one()),
|
|
Value::known(pallas::Base::one()),
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- // the original crypsinous coin pk is as follows.
|
|
|
|
|
- // coin public key pk=PRF_{root_sk}(tau)
|
|
|
|
|
- // coin public key is pseudo random hash of concatenation of the following:
|
|
|
|
|
- // coin timestamp, and root of coin's secret key.
|
|
|
|
|
- // staking coin timestamp
|
|
|
|
|
- //let coin_timestamp = self.load_private(
|
|
|
|
|
- //layouter.namespace(|| "load coin time stamp"),
|
|
|
|
|
- //config.advices[0],
|
|
|
|
|
- //self.coin_timestamp,
|
|
|
|
|
- //)?;
|
|
|
|
|
- //let coin_pk_commit: AssignedCell<Fp, Fp> = {
|
|
|
|
|
- // let poseidon_message = [coin_timestamp, _root_sk.clone()];
|
|
|
|
|
- // //let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- // _,
|
|
|
|
|
- // _,
|
|
|
|
|
- // poseidon::P128Pow5T3,
|
|
|
|
|
- // poseidon::ConstantLength<2>,
|
|
|
|
|
- // 3,
|
|
|
|
|
- // 2,
|
|
|
|
|
- // >::init(
|
|
|
|
|
- // config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
|
|
|
|
|
- // )?;
|
|
|
|
|
- //
|
|
|
|
|
- // let poseidon_output =
|
|
|
|
|
- // poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
|
|
|
|
|
- // let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- // poseidon_output
|
|
|
|
|
- //};
|
|
|
|
|
- // darkfi coin pk is based off secret key:
|
|
|
|
|
- // pk = G(nullifierK) * sk
|
|
|
|
|
- // the later is implemented for the sake of conversion between
|
|
|
|
|
- // lead coin and owncoin.
|
|
|
|
|
|
|
+ // ========================
|
|
|
|
|
+ // Derive coin's public key
|
|
|
|
|
+ // ========================
|
|
|
let coin_pk = {
|
|
let coin_pk = {
|
|
|
let coin_pk_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
let coin_pk_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
- coin_pk_commit_v.mul(layouter.namespace(|| "coin pk commit v"), sk)?
|
|
|
|
|
|
|
+ coin_pk_commit_v.mul(layouter.namespace(|| "coin_1sk * NullifierK"), coin1_sk)?
|
|
|
};
|
|
};
|
|
|
- let coin_pk_x = coin_pk.inner().x();
|
|
|
|
|
- let coin_pk_y = coin_pk.inner().y();
|
|
|
|
|
- // coin c1 serial number sn=PRF_{root_sk}(nonce)
|
|
|
|
|
- // coin's serial number is derived from coin nonce (sampled at random)
|
|
|
|
|
- // and root of the coin's secret key sampled an random.
|
|
|
|
|
- let sn_commit: AssignedCell<Fp, Fp> = {
|
|
|
|
|
- let poseidon_message = [coin_nonce.clone(), _root_sk.clone()];
|
|
|
|
|
- let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- _,
|
|
|
|
|
- _,
|
|
|
|
|
- poseidon::P128Pow5T3,
|
|
|
|
|
- poseidon::ConstantLength<2>,
|
|
|
|
|
- 3,
|
|
|
|
|
- 2,
|
|
|
|
|
- >::init(
|
|
|
|
|
- config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
|
|
|
|
|
- )?;
|
|
|
|
|
|
|
|
|
|
- let poseidon_output =
|
|
|
|
|
- poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
|
|
|
|
|
- let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- poseidon_output
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Coin `c1` serial number:
|
|
|
|
|
+ // sn=PRF_{root_sk}(nonce)
|
|
|
|
|
+ // Coin's serial number is derived from coin nonce (sampled at random)
|
|
|
|
|
+ // and root of the coin's secret key sampled at random.
|
|
|
|
|
+ let sn_commit: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
|
|
+ // For derivation here, we append one 0 and one 1 to the hashed message.
|
|
|
|
|
+ // TODO: Add these constants to ouroboros/consts.rs
|
|
|
|
|
+ let poseidon_message =
|
|
|
|
|
+ [coin1_nonce.clone(), coin1_sk_root.clone(), zero.clone(), one.clone()];
|
|
|
|
|
+ let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<4>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "sn_commit poseidon init"),
|
|
|
|
|
+ )?;
|
|
|
|
|
+ let poseidon_output = poseidon_hasher
|
|
|
|
|
+ .hash(layouter.namespace(|| "sn_commit poseidon hash"), poseidon_message)?;
|
|
|
|
|
+ poseidon_output.into()
|
|
|
};
|
|
};
|
|
|
- // commitment to the staking coin
|
|
|
|
|
- // coin commiment H=COMMIT(PRF(prefix||pk||V||nonce), r)
|
|
|
|
|
- let com = {
|
|
|
|
|
- // coin c1 nullifier is a commitment of the following
|
|
|
|
|
- // nullifier input
|
|
|
|
|
- let nullifier_msg: AssignedCell<Fp, Fp> = {
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // ==============================
|
|
|
|
|
+ // Commitment to the staking coin
|
|
|
|
|
+ // ==============================
|
|
|
|
|
+ // coin commitment H=Commit(PRF(prefix||pk||V||nonce), r)
|
|
|
|
|
+ let coin_commitment_v = {
|
|
|
|
|
+ // Coin c1 nullifier is a commitment of the following:
|
|
|
|
|
+ let nullifier_msg: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
let poseidon_message = [
|
|
let poseidon_message = [
|
|
|
prf_nullifier_prefix_base.clone(),
|
|
prf_nullifier_prefix_base.clone(),
|
|
|
- coin_pk_x.clone(),
|
|
|
|
|
- coin_pk_y.clone(),
|
|
|
|
|
- coin_value.clone(),
|
|
|
|
|
- coin_nonce.clone(),
|
|
|
|
|
- one.clone(),
|
|
|
|
|
|
|
+ coin_pk.inner().x(),
|
|
|
|
|
+ coin_pk.inner().y(),
|
|
|
|
|
+ coin1_value.clone(),
|
|
|
|
|
+ coin1_nonce.clone(),
|
|
|
|
|
+ one.clone(), // One is here because of poseidon odd-n bug
|
|
|
];
|
|
];
|
|
|
- let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- _,
|
|
|
|
|
- _,
|
|
|
|
|
- poseidon::P128Pow5T3,
|
|
|
|
|
- poseidon::ConstantLength<6>,
|
|
|
|
|
- 3,
|
|
|
|
|
- 2,
|
|
|
|
|
- >::init(
|
|
|
|
|
- config.poseidon_chip(),
|
|
|
|
|
- layouter.namespace(|| "Poseidon init"),
|
|
|
|
|
- )?;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let poseidon_hasher =
|
|
|
|
|
+ PoseidonHash::<_, _, P128Pow5T3, ConstantLength<6>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "nullifier poseidon init"),
|
|
|
|
|
+ )?;
|
|
|
let poseidon_output = poseidon_hasher
|
|
let poseidon_output = poseidon_hasher
|
|
|
- .hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
|
|
|
|
|
- let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- poseidon_output
|
|
|
|
|
|
|
+ .hash(layouter.namespace(|| "nullifier poseidon hash"), poseidon_message)?;
|
|
|
|
|
+ poseidon_output.into()
|
|
|
};
|
|
};
|
|
|
- let coin_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
|
|
- coin_commit_v.mul(layouter.namespace(|| "coin commit v"), nullifier_msg)?
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
|
|
+ v.mul(layouter.namespace(|| "nullifier_msg * NullifierK"), nullifier_msg)?
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // r*G_2
|
|
|
|
|
- let (blind, _) = {
|
|
|
|
|
- let rcv = ScalarFixed::new(
|
|
|
|
|
- ecc_chip.clone(),
|
|
|
|
|
- layouter.namespace(|| "coin1 blind scalar"),
|
|
|
|
|
- self.coin1_blind,
|
|
|
|
|
- )?;
|
|
|
|
|
- let coin_commit_r =
|
|
|
|
|
- FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
|
|
|
|
|
- coin_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), rcv)?
|
|
|
|
|
|
|
+ let (coin_commitment_r, _) = {
|
|
|
|
|
+ let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
|
|
|
|
|
+ r.mul(layouter.namespace(|| "coin1_blind * ValueCommitR"), coin1_blind)?
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- let coin_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?;
|
|
|
|
|
-
|
|
|
|
|
- // nonce2 = PRF_{root_sk}(coin_nonce)
|
|
|
|
|
- // poured coin nonce as a poseidon of the previous nonce, and
|
|
|
|
|
- // root of secret key.
|
|
|
|
|
- let coin2_nonce: AssignedCell<Fp, Fp> = {
|
|
|
|
|
- let poseidon_message = [coin_nonce.clone(), _root_sk.clone()];
|
|
|
|
|
- let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- _,
|
|
|
|
|
- _,
|
|
|
|
|
- poseidon::P128Pow5T3,
|
|
|
|
|
- poseidon::ConstantLength<2>,
|
|
|
|
|
- 3,
|
|
|
|
|
- 2,
|
|
|
|
|
- >::init(
|
|
|
|
|
- config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
|
|
|
|
|
|
|
+ let coin_commitment = coin_commitment_v.add(
|
|
|
|
|
+ layouter.namespace(|| "coin_commitment_v + coin_commitment_r"),
|
|
|
|
|
+ &coin_commitment_r,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ // ================================================
|
|
|
|
|
+ // Validate Merkle path to staked coin's commitment
|
|
|
|
|
+ // ================================================
|
|
|
|
|
+ let merkle_inputs = MerklePath::construct(
|
|
|
|
|
+ [config.merkle_chip_1(), config.merkle_chip_2()],
|
|
|
|
|
+ OrchardHashDomains::MerkleCrh,
|
|
|
|
|
+ self.coin1_commit_leaf_pos,
|
|
|
|
|
+ coin1_commit_merkle_path,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_commit_hash: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
|
|
+ let poseidon_message = [coin_commitment.inner().x(), coin_commitment.inner().y()];
|
|
|
|
|
+ let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "coin1_commit_hash poseidon init"),
|
|
|
)?;
|
|
)?;
|
|
|
|
|
+ let poseidon_output = poseidon_hasher
|
|
|
|
|
+ .hash(layouter.namespace(|| "coin1_commit_hash poseidon hash"), poseidon_message)?;
|
|
|
|
|
+ poseidon_output.into()
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let coin1_cm_root = merkle_inputs.calculate_root(
|
|
|
|
|
+ layouter.namespace(|| "calculate merkle root for coin1 commitment"),
|
|
|
|
|
+ coin1_commit_hash,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- let poseidon_output =
|
|
|
|
|
- poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
|
|
|
|
|
- let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- poseidon_output
|
|
|
|
|
|
|
+ // ===========================
|
|
|
|
|
+ // Derivation of coin2's nonce
|
|
|
|
|
+ // ===========================
|
|
|
|
|
+ let coin2_nonce: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
|
|
+ // For derivation here, we append 1 two times to the hashed message.
|
|
|
|
|
+ // TODO: Add these constants to ouroboros/consts.rs
|
|
|
|
|
+ let poseidon_message =
|
|
|
|
|
+ [coin1_nonce.clone(), coin1_sk_root.clone(), one.clone(), one.clone()];
|
|
|
|
|
+ let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<4>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "coin2_nonce poseidon init"),
|
|
|
|
|
+ )?;
|
|
|
|
|
+ let poseidon_output = poseidon_hasher
|
|
|
|
|
+ .hash(layouter.namespace(|| "coin2_nonce poseidon hash"), poseidon_message)?;
|
|
|
|
|
+ poseidon_output.into()
|
|
|
};
|
|
};
|
|
|
- // coin2 commiment H=COMMIT(PRF(pk||V||nonce2), r2)
|
|
|
|
|
- // poured coin's commitment is a nullifier
|
|
|
|
|
- let com2 = {
|
|
|
|
|
- // coin2's commitment input body as a poseidon of input concatenation of
|
|
|
|
|
- // public key, stake, and poured coin's nonce.
|
|
|
|
|
- let nullifier2_msg: AssignedCell<Fp, Fp> = {
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // ================
|
|
|
|
|
+ // Coin2 commitment
|
|
|
|
|
+ // ================
|
|
|
|
|
+ // H=Commit(PRF(pk||V||nonce2), r2)
|
|
|
|
|
+ // Poured coin's commitment is a nullifier
|
|
|
|
|
+ let coin2_commitment_v = {
|
|
|
|
|
+ // coin2's commitment input body as a poseidon hash of input
|
|
|
|
|
+ // concatenation of public key, stake, and poured coin's nonce.
|
|
|
|
|
+ let nullifier2: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
let poseidon_message = [
|
|
let poseidon_message = [
|
|
|
prf_nullifier_prefix_base,
|
|
prf_nullifier_prefix_base,
|
|
|
- coin_pk_x.clone(),
|
|
|
|
|
- coin_pk_y.clone(),
|
|
|
|
|
- coin_value.clone(),
|
|
|
|
|
|
|
+ coin_pk.inner().x(),
|
|
|
|
|
+ coin_pk.inner().y(),
|
|
|
|
|
+ coin1_value.clone(),
|
|
|
coin2_nonce.clone(),
|
|
coin2_nonce.clone(),
|
|
|
- one.clone(),
|
|
|
|
|
|
|
+ one.clone(), // Used here because of poseidon odd-n bug
|
|
|
];
|
|
];
|
|
|
- let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- _,
|
|
|
|
|
- _,
|
|
|
|
|
- poseidon::P128Pow5T3,
|
|
|
|
|
- poseidon::ConstantLength<6>,
|
|
|
|
|
- 3,
|
|
|
|
|
- 2,
|
|
|
|
|
- >::init(
|
|
|
|
|
- config.poseidon_chip(),
|
|
|
|
|
- layouter.namespace(|| "Poseidon init"),
|
|
|
|
|
|
|
+ let poseidon_hasher =
|
|
|
|
|
+ PoseidonHash::<_, _, P128Pow5T3, ConstantLength<6>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "coin2_commitment_v poseidon init"),
|
|
|
|
|
+ )?;
|
|
|
|
|
+ let poseidon_output = poseidon_hasher.hash(
|
|
|
|
|
+ layouter.namespace(|| "coin2_commitment_v poseidon hash"),
|
|
|
|
|
+ poseidon_message,
|
|
|
)?;
|
|
)?;
|
|
|
-
|
|
|
|
|
- let poseidon_output = poseidon_hasher
|
|
|
|
|
- .hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
|
|
|
|
|
- let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- poseidon_output
|
|
|
|
|
|
|
+ poseidon_output.into()
|
|
|
};
|
|
};
|
|
|
- let coin_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
|
|
- coin_commit_v.mul(layouter.namespace(|| "coin commit v"), nullifier2_msg)?
|
|
|
|
|
- };
|
|
|
|
|
- // r*G_2
|
|
|
|
|
- let (blind, _) = {
|
|
|
|
|
- let coin2_blind = ScalarFixed::new(
|
|
|
|
|
- ecc_chip.clone(),
|
|
|
|
|
- layouter.namespace(|| "coin2 blind scalar"),
|
|
|
|
|
- self.coin2_blind,
|
|
|
|
|
- )?;
|
|
|
|
|
- let coin_commit_r =
|
|
|
|
|
- FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
|
|
|
|
|
- coin_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), coin2_blind)?
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
|
|
+ v.mul(layouter.namespace(|| "nullifier2 * NullifierK"), nullifier2)?
|
|
|
};
|
|
};
|
|
|
- let coin2_commit = com2.add(layouter.namespace(|| "nonce commit"), &blind)?;
|
|
|
|
|
|
|
|
|
|
- // path is valid path to staked coin's commitment
|
|
|
|
|
- let path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> =
|
|
|
|
|
- self.path.map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
|
|
|
|
|
|
|
+ let (coin2_commitment_r, _) = {
|
|
|
|
|
+ let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
|
|
|
|
|
+ r.mul(layouter.namespace(|| "coin2_blind * ValueCommitR"), coin2_blind)?
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- let merkle_inputs = MerklePath::construct(
|
|
|
|
|
- [config.merkle_chip_1(), config.merkle_chip_2()],
|
|
|
|
|
- OrchardHashDomains::MerkleCrh,
|
|
|
|
|
- self.cm_pos,
|
|
|
|
|
- path,
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ let coin2_commitment = coin2_commitment_v.add(
|
|
|
|
|
+ layouter.namespace(|| "coin2_commitment_v + coin2_commitment_r"),
|
|
|
|
|
+ &coin2_commitment_r,
|
|
|
|
|
+ )?;
|
|
|
|
|
|
|
|
- //TODO (fix) replace mul by hash
|
|
|
|
|
- let coin_commit_prod: AssignedCell<Fp, Fp> = {
|
|
|
|
|
- let coin_commit_coordinates = coin_commit.inner();
|
|
|
|
|
|
|
|
|
|
- let res: AssignedCell<Fp, Fp> = ar_chip.mul(
|
|
|
|
|
- layouter.namespace(|| ""),
|
|
|
|
|
- &coin_commit_coordinates.x(),
|
|
|
|
|
- &coin_commit_coordinates.y(),
|
|
|
|
|
- )?;
|
|
|
|
|
- res
|
|
|
|
|
- };
|
|
|
|
|
|
|
|
|
|
- let coin_cm_root = merkle_inputs
|
|
|
|
|
- .calculate_root(layouter.namespace(|| "calculate root"), coin_commit_prod)?;
|
|
|
|
|
|
|
+ // ==================================
|
|
|
// lhs of the leader election lottery
|
|
// lhs of the leader election lottery
|
|
|
- // * y as COMIT(root_sk||nonce, mau_y)
|
|
|
|
|
- // beging the commitment to the coin's secret key, coin's nonce, and
|
|
|
|
|
- // random value deriven from the epoch sampled random eta.
|
|
|
|
|
- let lottery_commit_msg: AssignedCell<Fp, Fp> = {
|
|
|
|
|
- let poseidon_message = [_root_sk, coin_nonce];
|
|
|
|
|
- let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- _,
|
|
|
|
|
- _,
|
|
|
|
|
- poseidon::P128Pow5T3,
|
|
|
|
|
- poseidon::ConstantLength<2>,
|
|
|
|
|
- 3,
|
|
|
|
|
- 2,
|
|
|
|
|
- >::init(
|
|
|
|
|
- config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
|
|
|
|
|
|
|
+ // ==================================
|
|
|
|
|
+ // * y as Commit(root_sk||nonce, mau_y)
|
|
|
|
|
+ // Commitment to the coin's secret key, coin's nonce, and random value
|
|
|
|
|
+ // derived from the epoch sampled random eta.
|
|
|
|
|
+ let lottery_commit_msg: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
|
|
+ let poseidon_message = [coin1_sk_root.clone(), coin1_nonce.clone()];
|
|
|
|
|
+ let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "lottery_commit_msg poseidon init"),
|
|
|
|
|
+ )?;
|
|
|
|
|
+ let poseidon_output = poseidon_hasher.hash(
|
|
|
|
|
+ layouter.namespace(|| "lottery_commit_msg poseidon hash"),
|
|
|
|
|
+ poseidon_message,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
+ poseidon_output.into()
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- let poseidon_output =
|
|
|
|
|
- poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
|
|
|
|
|
- let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- poseidon_output
|
|
|
|
|
|
|
+ let lottery_commit_v = {
|
|
|
|
|
+ let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
|
|
+ v.mul(layouter.namespace(|| "lottery_commit_msg * NullifierK"), lottery_commit_msg)?
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- let com = {
|
|
|
|
|
- let y_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
|
|
|
|
- y_commit_v.mul(layouter.namespace(|| "coin commit v"), lottery_commit_msg)?
|
|
|
|
|
|
|
+ let (lottery_commit_r, _) = {
|
|
|
|
|
+ let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
|
|
|
|
|
+ r.mul(layouter.namespace(|| "mau_y * ValueCommitR"), mau_y)?
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // r*G_2
|
|
|
|
|
- let (blind, _) = {
|
|
|
|
|
- let mau_y = ScalarFixed::new(
|
|
|
|
|
- ecc_chip.clone(),
|
|
|
|
|
- layouter.namespace(|| "mau_y scalar"),
|
|
|
|
|
- self.mau_y,
|
|
|
|
|
|
|
+ let y_commit = lottery_commit_v
|
|
|
|
|
+ .add(layouter.namespace(|| "lottery_commit_v + lottery_commit_r"), &lottery_commit_r)?;
|
|
|
|
|
+
|
|
|
|
|
+ // Hash the coordinates to get a base field element
|
|
|
|
|
+ let y_commit_base: AssignedCell<pallas::Base, pallas::Base> = {
|
|
|
|
|
+ let poseidon_message = [y_commit.inner().x(), y_commit.inner().y()];
|
|
|
|
|
+ let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
|
|
|
|
|
+ config.poseidon_chip(),
|
|
|
|
|
+ layouter.namespace(|| "lottery_commit coords poseidon init"),
|
|
|
)?;
|
|
)?;
|
|
|
- let y_commit_r =
|
|
|
|
|
- FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
|
|
|
|
|
- y_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), mau_y)?
|
|
|
|
|
- };
|
|
|
|
|
- let y_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?;
|
|
|
|
|
- let y_commit_base: AssignedCell<Fp, Fp> = {
|
|
|
|
|
- let y_commit_base_x = y_commit.inner().x();
|
|
|
|
|
- let y_commit_base_y = y_commit.inner().y();
|
|
|
|
|
- let y_coord = [y_commit_base_x, y_commit_base_y];
|
|
|
|
|
- let poseidon_hasher = PoseidonHash::<
|
|
|
|
|
- _,
|
|
|
|
|
- _,
|
|
|
|
|
- poseidon::P128Pow5T3,
|
|
|
|
|
- poseidon::ConstantLength<2>,
|
|
|
|
|
- 3,
|
|
|
|
|
- 2,
|
|
|
|
|
- >::init(
|
|
|
|
|
- config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
|
|
|
|
|
|
|
+ let poseidon_output = poseidon_hasher.hash(
|
|
|
|
|
+ layouter.namespace(|| "lottery_commit coords poseidon hash"),
|
|
|
|
|
+ poseidon_message,
|
|
|
)?;
|
|
)?;
|
|
|
-
|
|
|
|
|
- let poseidon_output =
|
|
|
|
|
- poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), y_coord)?;
|
|
|
|
|
- let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
|
|
|
|
|
- poseidon_output
|
|
|
|
|
|
|
+ poseidon_output.into()
|
|
|
};
|
|
};
|
|
|
- // constraint rho as COMIT(PRF(root_sk||nonce), rho_mu)
|
|
|
|
|
- // r*G_2
|
|
|
|
|
- let (blind, _) = {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // y_commit also becomes V of the following pedersen commitment for rho
|
|
|
|
|
+ let (rho_cm, _) = {
|
|
|
let mau_rho = ScalarFixed::new(
|
|
let mau_rho = ScalarFixed::new(
|
|
|
ecc_chip.clone(),
|
|
ecc_chip.clone(),
|
|
|
layouter.namespace(|| "mau_rho scalar"),
|
|
layouter.namespace(|| "mau_rho scalar"),
|
|
|
self.mau_rho,
|
|
self.mau_rho,
|
|
|
)?;
|
|
)?;
|
|
|
let rho_commit_r =
|
|
let rho_commit_r =
|
|
|
- FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
|
|
|
|
|
|
|
+ FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
|
|
|
rho_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), mau_rho)?
|
|
rho_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), mau_rho)?
|
|
|
};
|
|
};
|
|
|
- let rho_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?;
|
|
|
|
|
- let rho = NonIdentityPoint::new(
|
|
|
|
|
- ecc_chip.clone(),
|
|
|
|
|
- layouter.namespace(|| "witness rho"),
|
|
|
|
|
- self.rho.map(|x| x.to_affine()),
|
|
|
|
|
- )?;
|
|
|
|
|
- let term1 =
|
|
|
|
|
- ar_chip.mul(layouter.namespace(|| "calculate term1"), &sigma1, &coin_value.clone())?;
|
|
|
|
|
|
|
+ let rho_commit = lottery_commit_v.add(layouter.namespace(|| "nonce commit"), &rho_cm)?;
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- let term2_1 = ar_chip.mul(
|
|
|
|
|
- layouter.namespace(|| "calculate term2_1"),
|
|
|
|
|
|
|
+ // Calculate term1 and term2 for the lottery
|
|
|
|
|
+ let term1 = arith_chip.mul(
|
|
|
|
|
+ layouter.namespace(|| "term1 = sigma1 * coin1_value"),
|
|
|
|
|
+ &sigma1,
|
|
|
|
|
+ &coin1_value,
|
|
|
|
|
+ )?;
|
|
|
|
|
+ let term2_1 = arith_chip.mul(
|
|
|
|
|
+ layouter.namespace(|| "term2_1 = sigma2 * coin1_value"),
|
|
|
&sigma2,
|
|
&sigma2,
|
|
|
- &coin_value.clone(),
|
|
|
|
|
|
|
+ &coin1_value,
|
|
|
|
|
+ )?;
|
|
|
|
|
+ let term2 = arith_chip.mul(
|
|
|
|
|
+ layouter.namespace(|| "term2 = term2_1 * coin1_value"),
|
|
|
|
|
+ &term2_1,
|
|
|
|
|
+ &coin1_value,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- let term2 =
|
|
|
|
|
- ar_chip.mul(layouter.namespace(|| "calculate term2"), &term2_1, &coin_value.clone())?;
|
|
|
|
|
-
|
|
|
|
|
- let target = ar_chip.add(layouter.namespace(|| "calculate target"), &term1, &term2)?;
|
|
|
|
|
- let target: Value<pallas::Base> = target.value().cloned();
|
|
|
|
|
|
|
+ // Calculate lottery target
|
|
|
|
|
+ let target =
|
|
|
|
|
+ arith_chip.add(layouter.namespace(|| "target = term1 + term2"), &term1, &term2)?;
|
|
|
|
|
|
|
|
- let y: Value<pallas::Base> = y_commit_base.value().cloned();
|
|
|
|
|
|
|
|
|
|
- less_than_chip.witness_less_than(
|
|
|
|
|
|
|
+ // Constrain y < target
|
|
|
|
|
+ lessthan_chip.copy_less_than(
|
|
|
layouter.namespace(|| "y < target"),
|
|
layouter.namespace(|| "y < target"),
|
|
|
- y,
|
|
|
|
|
|
|
+ y_commit_base.clone(),
|
|
|
target,
|
|
target,
|
|
|
0,
|
|
0,
|
|
|
true,
|
|
true,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
|
|
|
- layouter.constrain_instance(coin2_nonce.cell(), config.primary, LEAD_COIN_NONCE2_OFFSET)?;
|
|
|
|
|
|
|
|
|
|
- layouter.constrain_instance(coin_pk_x.cell(), config.primary, LEAD_COIN_PK_X_OFFSET)?;
|
|
|
|
|
- layouter.constrain_instance(coin_pk_y.cell(), config.primary, LEAD_COIN_PK_Y_OFFSET)?;
|
|
|
|
|
|
|
+ // Constrain derived `sn_commit` to be equal to witnessed `coin1_serial`.
|
|
|
|
|
+ layouter.assign_region(
|
|
|
|
|
+ || "sn_commit equality",
|
|
|
|
|
+ |mut region| {
|
|
|
|
|
+ region.constrain_equal(sn_commit.cell(), coin1_serial.cell())?;
|
|
|
|
|
+ region.constrain_equal(coin1_cm_root.cell(), coin1_commit_root.cell())
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // Constrain equality between witnessed and derived commitment
|
|
|
|
|
+ coin2_commitment.constrain_equal(layouter.namespace(|| "coin2_commit equality"), &coin2_commit)?;
|
|
|
|
|
|
|
|
|
|
+ // Constrain derived rho_commit to witnessed rho
|
|
|
|
|
+ rho_commit.constrain_equal(layouter.namespace(|| "rho equality"), &rho)?;
|
|
|
|
|
+
|
|
|
|
|
+ // Constrain coin's public key coordinates with public inputs
|
|
|
layouter.constrain_instance(
|
|
layouter.constrain_instance(
|
|
|
- y_commit_base.cell(),
|
|
|
|
|
|
|
+ coin_pk.inner().x().cell(),
|
|
|
config.primary,
|
|
config.primary,
|
|
|
- LEAD_Y_COMMIT_BASE_OFFSET,
|
|
|
|
|
|
|
+ LEADCOIN_PK_X_OFFSET,
|
|
|
)?;
|
|
)?;
|
|
|
-
|
|
|
|
|
- rho_commit.constrain_equal(layouter.namespace(|| ""), &rho)?;
|
|
|
|
|
- let ref_coin2_cm = NonIdentityPoint::new(
|
|
|
|
|
- ecc_chip.clone(),
|
|
|
|
|
- layouter.namespace(|| "witness coin2 cm"),
|
|
|
|
|
- self.coin2_commit.map(|x| x.to_affine()),
|
|
|
|
|
|
|
+ layouter.constrain_instance(
|
|
|
|
|
+ coin_pk.inner().y().cell(),
|
|
|
|
|
+ config.primary,
|
|
|
|
|
+ LEADCOIN_PK_Y_OFFSET,
|
|
|
)?;
|
|
)?;
|
|
|
- coin2_commit.constrain_equal(layouter.namespace(|| ""), &ref_coin2_cm)?;
|
|
|
|
|
- layouter.assign_region(
|
|
|
|
|
- || "",
|
|
|
|
|
- |mut region| {
|
|
|
|
|
- region.constrain_equal(sn_commit.cell(), coin1_sn.cell())?;
|
|
|
|
|
- region.constrain_equal(coin_cm_root.cell(), root_cm.cell())
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ // Constrain coin2_nonce with associated public input
|
|
|
|
|
+ layouter.constrain_instance(
|
|
|
|
|
+ coin2_nonce.cell(),
|
|
|
|
|
+ config.primary,
|
|
|
|
|
+ LEADCOIN_C2_NONCE_OFFSET,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ // Constrain y to its respective public input
|
|
|
|
|
+ layouter.constrain_instance(
|
|
|
|
|
+ y_commit_base.cell(),
|
|
|
|
|
+ config.primary,
|
|
|
|
|
+ LEADCOIN_Y_BASE_OFFSET,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[cfg(test)]
|
|
|
|
|
+mod tests {
|
|
|
|
|
+ use super::*;
|
|
|
|
|
+ use crate::Result;
|
|
|
|
|
+ use halo2_proofs::dev::CircuitLayout;
|
|
|
|
|
+ use plotters::prelude::*;
|
|
|
|
|
+
|
|
|
|
|
+ #[test]
|
|
|
|
|
+ fn test_leader_circuit() -> Result<()> {
|
|
|
|
|
+ let k = 11;
|
|
|
|
|
+ let circuit = LeadContract::default();
|
|
|
|
|
+
|
|
|
|
|
+ let root = BitMapBackend::new("target/leader_circuit_layout.png", (3840, 2160))
|
|
|
|
|
+ .into_drawing_area();
|
|
|
|
|
+ root.fill(&WHITE).unwrap();
|
|
|
|
|
+ let root = root.titled("Lead Circuit Layout", ("sans-serif", 60)).unwrap();
|
|
|
|
|
+ CircuitLayout::default()
|
|
|
|
|
+ //.view_width(0..10)
|
|
|
|
|
+ .render(k, &circuit, &root).unwrap();
|
|
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|