diff --git a/proof/lead.zk b/proof/lead.zk new file mode 100644 index 000000000..56b343a73 --- /dev/null +++ b/proof/lead.zk @@ -0,0 +1,90 @@ +constant "Lead" { +EcFixedPointShort VALUE_COMMIT_VALUE, +EcFixedPoint VALUE_COMMIT_RANDOM, +EcFixedPointBase NULLIFIER_K, +Base PREFIX_CM, +Base PREFIX_PK, +Base PREFIX_EVL, +BASE PREFIX_SEED, +Base ONE, +Base ZERO, +} + +contract "Lead" { +MerklePath c1_cm_path, +Uint32 c1_cm_pos, +Unit32 c1_sk_pos, +Base c1_sk, +Base c1_sk_root, +MerklePath c1_sk_path, +Base c1_tau, +Base c1_rho, +Scalar c1_opening, +Base value, +Scalar c2_opening, +Scalar rho_mu, +Scalar y_mu, +Base sigam1, +Base sigma2 +} + +Circuit "Lead" { +# coin (1) pk +pk = poseidon_hash(PREFIX_PK, c1_sk_root, c1_tau, ZERO); +constrain_instance(pk); +# coin (2) rho/nonce +c2_rho = poseidon_hash(PREFIX_EVL, c1_sk_root, c1_rho, ZERO); +constrain_instance(c2_rho); +# coin (1) cm/commitment +c1_cm_msg = poseidon_hash(PREFIX_CM, pk, value, c1_rho); +c1_cm_v = ec_mul_short(c1_cm_msg, VALUE_COMMIT_VALUE); +c1_cm_r = ec_mul(c1_opening, VALUE_COMMIT_RANDOM); +c1_cm = ec_add(c1_cm_v, c1_cm_r); +c1_cm_x = ec_get_x(c1_cm); +c1_cm_y = ec_get_y(c1_cm); +constrain_instance(c1_cm_x); +constrain_instance(c1_cm_y); +# coin (2) cm/commitment +c2_cm_msg = poseidon_hash(PREFIX_CM, pk, value, c2_rho); +c2_cm_v = ec_mul_short(c2_cm_msg, VALUE_COMMIT_VALUE); +c2_cm_r = ec_mul(c2_opening, VALUE_COMMIT_RANDOM); +c2_cm = ec_add(c2_cm_v, c2_cm_r); +c2_cm_x = ec_get_x(c2_cm); +c2_cm_y = ec_get_y(c2_cm); +constrain_instance(c2_cm_x); +constrain_instance(c2_cm_y); +# root of path to burnt coin commitment at given pos +root = merkle_root(c1_cm_pos, c1_cm_path, c1_cm); +constrain_instance(root); +# root of path at c1_sk_pos +root_sk = merkle_root(c1_sk_pos, c1_sk_path, c1_sk); +constrain_instance(root_sk); +# coin (1) sn/nullifier +sn = poseidon_hash(PREFIX_SN, c1_sk_root, c1_rho, ZERO); +constrain_instance(sn); +# lottery seed +seed = poseidon_hash(PREFIX_SEED, c1_sk_root, c1_rho, ZERO); +# y +y_v = ec_mul_short(seed, VALUE_COMMIT_VALUE); +y_r = ec_mul(y_mu, VALUE_COMMIT_RANDOM); +y = ec_add(y_v, y_r); +y_x = ec_get_x(y); +y_y = ec_get_y(y); +constrain_instance(y_x); +constrain_instance(y_y); +# rho +rho_v = ec_mul_short(seed, VALUE_COMMIT_VALUE); +rho_r = ec_mul(rho_mu, VALUE_COMMIT_RANDOM); +rho = ec_add(rho_v, rho_r); +rho_x = ec_get_x(rho); +rho_y = ec_get_y(rho); +constrain_instance(rho_x); +constrain_instance(rho_y); +# target +term1 = base_mul(sigma1, value); +term2_1 = base_mul(sigam2, value); +term2 = base_mul(term_2_1, value); +target = base_add(term1, term2); +# lottery +less_than(y, target) +} diff --git a/src/consensus/leadcoin.rs b/src/consensus/leadcoin.rs index 5411e4214..d5bd7ffb9 100644 --- a/src/consensus/leadcoin.rs +++ b/src/consensus/leadcoin.rs @@ -239,20 +239,29 @@ impl LeadCoin { let pubkey = PublicKey::from_secret(self.secret_key); let (pub_x, pub_y) = pubkey.xy(); - - vec![self.nonce_cm, pub_x, pub_y, y] - } - - /// Try to create a ZK proof of consensus leadership - pub fn create_lead_proof(&self, pk: &ProvingKey) -> Result { + let c2_cm = self.coin2_commitment.coordinates().unwrap(); + // rho // Initialize circuit with witnesses let lottery_msg_input = [self.coin1_sk_root.inner(), self.nonce]; let lottery_msg = poseidon_hash(lottery_msg_input); let rho = pedersen_commitment_base(lottery_msg, mod_r_p(self.rho_mu)); + let rho_coord = rho.coordinates().unwrap(); + vec![ + self.coin1_commitment_root.inner(), + self.sn, + *c2_cm.x(), *c2_cm.y(), + *rho_coord.x(), *rho_coord.y(), + self.nonce_cm, + pub_x, pub_y, + y + ] + } + + /// Try to create a ZK proof of consensus leadership + pub fn create_lead_proof(&self, pk: &ProvingKey) -> Result { let circuit = LeadContract { coin1_commit_merkle_path: Value::known(self.coin1_commitment_merkle_path), - coin1_commit_root: Value::known(self.coin1_commitment_root.inner()), coin1_commit_leaf_pos: Value::known(self.idx), coin1_sk: Value::known(self.secret_key.inner()), coin1_sk_root: Value::known(self.coin1_sk_root.inner()), @@ -260,7 +269,6 @@ impl LeadCoin { coin1_timestamp: Value::known(self.tau), coin1_nonce: Value::known(self.nonce), coin1_blind: Value::known(self.coin1_blind), - coin1_serial: Value::known(self.sn), coin1_value: Value::known(pallas::Base::from(self.value)), coin2_blind: Value::known(self.coin2_blind), coin2_commit: Value::known(self.coin2_commitment), @@ -268,7 +276,6 @@ impl LeadCoin { y_mu: Value::known(mod_r_p(self.y_mu)), sigma1: Value::known(self.sigma1), sigma2: Value::known(self.sigma2), - rho: Value::known(rho), }; Ok(Proof::create(pk, &[circuit], &self.public_inputs(), &mut OsRng)?) diff --git a/src/consensus/mod.rs b/src/consensus/mod.rs index 39556ebd4..99e4c9d2b 100644 --- a/src/consensus/mod.rs +++ b/src/consensus/mod.rs @@ -53,3 +53,5 @@ pub mod utils; /// Wallet functions pub mod wallet; + +pub mod ouroboros; diff --git a/src/zk/circuit/lead_contract.rs b/src/zk/circuit/lead_contract.rs index c338d2305..ee8645f84 100644 --- a/src/zk/circuit/lead_contract.rs +++ b/src/zk/circuit/lead_contract.rs @@ -61,14 +61,26 @@ use crate::zk::{ }; use log::info; +/// public input offset for lead coin 1 commitment root. +const LEADCOIN_COIN1_COMMIT_ROOT_OFFSET: usize = 0; +/// public input offset for lead coin 1 nullifier. +const LEADCOIN_COIN1_NULLIFIER_ROOT_OFFSET: usize = 1; +/// public input offset for coin2 commitment x coordinate. +const LEADCOIN_COIN2_COMMITMENT_X_OFFSET: usize = 2; +/// public input offset for coin2 commitment y coordinate. +const LEADCOIN_COIN2_COMMITMENT_Y_OFFSET: usize = 3; +/// public input offset for rho commitment x coordinate. +const LEADCOIN_RHO_COMMITMENT_X_OFFSET: usize = 4; +/// public input offset for rho commitment y coordinate. +const LEADCOIN_RHO_COMMITMENT_Y_OFFSET: usize = 5; /// Public input offset for the lead coin C2 nonce -const LEADCOIN_C2_NONCE_OFFSET: usize = 0; +const LEADCOIN_C2_NONCE_OFFSET: usize = 6; /// Public input offset for lead coin public key X coordinate -const LEADCOIN_PK_X_OFFSET: usize = 1; +const LEADCOIN_PK_X_OFFSET: usize = 7; /// Public input offset for lead coin public key Y coordinate -const LEADCOIN_PK_Y_OFFSET: usize = 2; +const LEADCOIN_PK_Y_OFFSET: usize = 8; /// Public input offset for the lottery target lhs -const LEADCOIN_Y_BASE_OFFSET: usize = 3; +const LEADCOIN_Y_BASE_OFFSET: usize = 9; /// Derivation prefix for the nullifier PRF const PRF_NULLIFIER_PREFIX: u64 = 0; @@ -124,8 +136,6 @@ impl LeadConfig { pub struct LeadContract { /// 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, /// `coin_1` leaf position in the Merkle tree of coin commitments pub coin1_commit_leaf_pos: Value, /// `coin_1` secret key. @@ -143,14 +153,10 @@ pub struct LeadContract { pub coin1_nonce: Value, /// Blinding factor for the commitment of `coin_1` pub coin1_blind: Value, - /// Serial number for `coin_1` - pub coin1_serial: Value, /// Value of `coin_1` pub coin1_value: Value, /// Blinding factor for the commitment of `coin_2` pub coin2_blind: Value, - /// `coin_2` pedersen commitment point - pub coin2_commit: Value, /// Random value derived from `eta` used for constraining `rho` pub rho_mu: Value, /// Random value derived from `eta` used for calculating `y`. @@ -161,8 +167,6 @@ pub struct LeadContract { pub sigma1: Value, /// Second coefficient in 2-term T (target function) approximation pub sigma2: Value, - /// Constrained nonce `rho`. - pub rho: Value, } impl Circuit for LeadContract { @@ -339,11 +343,13 @@ impl Circuit for LeadContract { .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"), @@ -378,11 +384,13 @@ impl Circuit for LeadContract { self.coin1_blind, )?; + /* let coin1_serial = assign_free_advice( layouter.namespace(|| "witness coin1_serial"), config.advices[8], self.coin1_serial, - )?; + )?; + */ let coin1_value = assign_free_advice( layouter.namespace(|| "witness coin1_value"), @@ -396,11 +404,13 @@ impl Circuit for LeadContract { self.coin2_blind, )?; + /* let coin2_commit = NonIdentityPoint::new( ecc_chip.clone(), layouter.namespace(|| "witness coin2_commit"), self.coin2_commit.as_ref().map(|cm| cm.to_affine()), )?; + */ let rho_mu = ScalarFixed::new( ecc_chip.clone(), @@ -423,11 +433,13 @@ impl Circuit for LeadContract { self.sigma2, )?; + /* 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"), @@ -670,33 +682,71 @@ impl Circuit for LeadContract { // Constrain derived `sn_commit` to be equal to witnessed `coin1_serial`. info!("coin1 cm root LHS: {:?}", coin1_cm_root.value()); + /* info!("coin1 cm root RHS: {:?}", coin1_commit_root.value()); layouter.assign_region( - || "coin1_cm_root equality", - |mut region| region.constrain_equal(coin1_cm_root.cell(), coin1_commit_root.cell()), + || "coin1_cm_root equality", + |mut region| region.constrain_equal(coin1_cm_root.cell(), coin1_commit_root.cell()), + )?; + */ + layouter.constrain_instance( + coin1_cm_root.cell(), + config.primary, + LEADCOIN_COIN1_COMMIT_ROOT_OFFSET, )?; info!("coin1 serial commit LHS: {:?}", sn_commit.value()); + /* info!("coin1 serial commit RHS: {:?}", coin1_serial.value()); layouter.assign_region( - || "sn_commit equality", - |mut region| region.constrain_equal(sn_commit.cell(), coin1_serial.cell()), + || "sn_commit equality", + |mut region| region.constrain_equal(sn_commit.cell(), coin1_serial.cell()), + )?; + */ + layouter.constrain_instance( + sn_commit.cell(), + config.primary, + LEADCOIN_COIN1_NULLIFIER_ROOT_OFFSET, )?; info!("coin2_commit LHS: x {:?}", coin2_commitment.inner().x()); info!("coin2_commit LHS: y {:?}", coin2_commitment.inner().y()); + // Constrain equality between witnessed and derived commitment + /* info!("coin2_commit RHS: x {:?}", coin2_commit.inner().x()); info!("coin2_commit RHS: y {:?}", coin2_commit.inner().y()); - // Constrain equality between witnessed and derived commitment coin2_commitment - .constrain_equal(layouter.namespace(|| "coin2_commit equality"), &coin2_commit)?; + .constrain_equal(layouter.namespace(|| "coin2_commit equality"), &coin2_commit)?; + */ + layouter.constrain_instance( + coin2_commitment.inner().x().cell(), + config.primary, + LEADCOIN_COIN2_COMMITMENT_X_OFFSET, + )?; + layouter.constrain_instance( + coin2_commitment.inner().y().cell(), + config.primary, + LEADCOIN_COIN2_COMMITMENT_Y_OFFSET, + )?; info!("rho commit LHS: x {:?}", rho_commit.inner().x()); info!("rho commit LHS: y {:?}", rho_commit.inner().y()); + + // Constrain derived rho_commit to witnessed rho + /* info!("rho commit RHS: x {:?}", rho.inner().x()); info!("rho commit RHS: y {:?}", rho.inner().y()); - // Constrain derived rho_commit to witnessed rho rho_commit.constrain_equal(layouter.namespace(|| "rho equality"), &rho)?; - + */ + layouter.constrain_instance( + rho_commit.inner().x().cell(), + config.primary, + LEADCOIN_RHO_COMMITMENT_X_OFFSET, + )?; + layouter.constrain_instance( + rho_commit.inner().y().cell(), + config.primary, + LEADCOIN_RHO_COMMITMENT_Y_OFFSET, + )?; info!("coin pk: x {:?}", coin_pk.inner().x()); info!("coin pk: y {:?}", coin_pk.inner().y()); // Constrain coin's public key coordinates with public inputs