diff --git a/src/zk/circuit/lead_contract.rs b/src/zk/circuit/lead_contract.rs new file mode 100644 index 000000000..25ce2d22e --- /dev/null +++ b/src/zk/circuit/lead_contract.rs @@ -0,0 +1,617 @@ +use halo2_gadgets::{ + ecc::{ + chip::{EccChip, EccConfig}, + FixedPoint, FixedPointShort,NonIdentityPoint,Point + }, + poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig}, + primitives::poseidon::{ConstantLength, P128Pow5T3}, + utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions}, +}; + +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, SimpleFloorPlanner}, + plonk, + plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn}, +}; + +use pasta_curves::{pallas, Fp}; + +use crate::crypto::constants::{ + constants::{ + util::gen_const_array, + }, + OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV, +}; + +use create::zk::{ + arith_chip::{ArithmeticChipConfig, ArithmeticChip}, + GreaterThanChip, GreatherThanConfig, +}; + +#[derive(Clone,Debug)] +pub struct LeadConfig +{ + primary: Column, + advices: [Column;19], + ecc_config: EccConfig, + poseidon_config: PoseidonConfig, + merkle_config_1: MerkleConfig, + merkle_config_2: MerkleConfig, + sinsemilla_config_1: SinsemillaConfig, + sinsemilla_config_2: SinsemillaConfig, + greaterthan_config: GreaterThanConfig, + arith_config: ArithmeticChipConfig, +} + +//TODO what is the pederson commitment pallas order number of bits? +const COMMIT_GROUP_ORDER_BITS : usize = 264; + +impl LeadConfig +{ + fn ecc_chip(&self) -> EccChip + { + EccChip::construct(self.ecc_config.clone()) + } + + fn poseidon_chip(&self) -> PoseidonChip + { + PoseidonChip::construct(self.poseidon_config.clone()) + } + + fn merkle_chip_1( + &self, + ) -> MerkleChip { + MerkleChip::construct(self.merkle_config_1.clone()) + } + + fn merkle_chip_2( + &self, + ) -> MerkleChip { + MerkleChip::construct(self.merkle_config_2.clone()) + } + + fn greaterthan_chip(&self) -> GreaterThanChip { + GreaterThanChip::construct(self.greaterthan_config.clone()) + } + + fn arith_chip(&self) -> ArithmeticChip { + ArithmeticChip::construct(self.arith_config.clone()) + } +} + + +// +const LEAD_COIN_PK_X_OFFSET: usize = 0; +const LEAD_COIN_PK_y_OFFSET: usize = 1; +// +const LEAD_COIN_NONCE2_X_OFFSET: usize = 2; +const LEAD_COIN_NONCE2_y_OFFSET: usize = 3; + +const LEAD_COIN_SERIAL_NUMBER_X_OFFSET: usize = 4; +const LEAD_COIN_SERIAL_NUMBER_Y_OFFSET: usize = 5; +// + +const LEAD_COIN2_SERIAL_NUMBER_X_OFFSET: usize = 8; +const LEAD_COIN2_SERIAL_NUMBER_Y_OFFSET: usize = 9; +// +const LEAD_COIN_COMMIT_PATH_OFFSET: usize = 6; + +const LEAD_LEAD_THRESHOLD_OFFSET: usize = 7; + +#[derive(Debug,Default)] +pub struct LeadContract +{ + // witness + pub path : Option<[MerkleNode;MERKLE_DEPTH_ORCHARD]>, + pub root_sk : Option, // coins merkle tree secret key of coin1 + pub path_sk : Option<[MerkleNode; MERKLE_DEPTH_ORCHARD]>, // path to the secret key root_sk + pub coin_timestamp: Option, + pub coin_nonce : Option, + pub coin_opening_1 :Option, + pub value: Option, + pub coin_opening_2 :Option, + // public advices + pub cm_c2 : Option, + pub sn_c1 : Option, + pub eta : Option, //TODO name, type + pub slot : Option, + pub rho : Option, //TODO name, type + pub h : Option, // hash of this data + pub ptr: Option, //hash of the previous block + pub mau_rho: Option, //TODO name, type + pub mau_y: Option, //TODO name, type + pub root : Option, //TODO name, type +} + +impl UtilitiesInstruction for LeadContract { + type var = AssignedCell; +} + +impl circuit for LeadContract { + type Config = LeadConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witness(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> 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(), + meta.advice_column(), + meta.advice_column(), + ]; + + let table_idx = meta.lookup_table_column(); + let lookup = (table_idx, meta.lookup_table_column(), meta.lookup_table_column()); + + let primary = meta.instance_column(); + meta.enable_equality(primary); + + for advice in advices.iter() { + meta.enable_equality(*advice); + } + + 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(), + meta.fixed_column(), + ]; + + let rc_a = lagrange_coeffs[2..5].try_into().unwrap(); + let rc_b = lagrange_coeffs[5..8].try_into().unwrap(); + + meta.enable_constant(laggrange_coeffs[0]); + let range_check = LookupRangeCheckConfig::configure(meta, advices[8], table_idx); + + + //TODO how many columns needed for the eccChip? + //i assumed 5 for constants/private_witnesses + let ecc_config = EccChip::::configure(meta, advices, lagrange_coeffs, range_check); + + let poseidon_config = PoseidonChip::configure( + meta, + advices[6..9].try_into().unwrap(), + advices[5], + rc_a, + rc_b, + ); + + 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, + ); + let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone()); + (sinsemilla_config_1, merkle_config_1) + }; + + let (sinsemilla_config_2, merkle_config_2) = { + let sinsemilla_config_2 = SinsemillaChip::configure( + meta, + advices[5.9].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) + }; + + let greaterthan_config = GreaterThanChip::::configure(meta, advices[10..11]); + let arith_config = ArithmeticChip::configure(meta); + + LeadConfig { + primary, + advices, + ecc_config, + poseidon_config, + merkle_config_1, + merkle_config_2, + sinsemilla_config_1, + sinsemilla_config_2, + greaterthan_config, + arith_config, + } + } + + fn synthesize(&self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(),Error> { + SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?; + let ecc_chip = config.ecc_chip(); + let ar_chip = config.arith_chip(); + + // =============== + // load witnesses + // =============== + + // coin_timestamp tau + let coin_timestamp = self.load_private( + layouter.namespace(|| "load coin time stamp"), + config.advices[0], + self.coin_timestamp, + )?; + + // root of coin + let coin_root = self.load_private( + layouter.namespace(|| "load root coin"), + config.advices[0], + self.root_sk, + )?; + + // coin nonce + let coin_nonce = self.load_private( + layouter.namespace(|| "load coin nonce"), + config.advices[0], + self.nonce, + )?; + + let coin_opening_1 = self.load_private( + layouter.namespace(|| "load opening 1"), + config.advices[0], + self.coin_opening_1, + )?; + + let coin_value = self.load_private( + layouter.namespace(|| "load opening 1"), + config.advices[0], + self.value, + )?; + + let coin_opening_2 = self.load_private( + layouter.namespace(|| "load opening 2"), + config.advices[0], + self.coin_opening_2, + )?; + + let cm_c2 = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.cm_c2, + )?; + + let sn_c1 = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.sn_c1, + )?; + + let eta = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.eta, + )?; + + let slot = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.slot, + )?; + + let rho = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.rho, + )?; + + let h = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.h, + )?; + + let ptr = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.ptr, + )?; + + let mau_rho = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.mau_rho, + )?; + + let mau_y = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.mau_y, + )?; + + let root = self.load_private( + layouter.namespace(|| ""), + config.advices[0], + self.root, + )?; + + //TODO read the second coin commitment as constant(public input) + // in this case + // + + // =============== + // coin 2 nonce + // =============== + // m*G_1 + let (com, _ ) = { + let nonce2_commit_v = ValueCommitV; + let nonce2_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), nonce2_commit_v); + nonce2_commit_v.mul(layouter.namespace(|| "coin_pk commit v"), (coin_nonce, one))? + }; + // r*G_2 + let (blind, _) = { + let nonce2_commit_r = OrchardFixedBasesFull::ValueCommitR; + let nonce2_commit_r = FixedPoint::from_inner(ecc_chip.clone(), nonce2_commit_r); + nonce2_commit_r.mul(layouter.namespace(|| "nonce2 commit R"), (coin_root.clone(), one))? + }; + let coin2_nonce = com.add(layouter.namespace(|| "nonce2 commit"), &blind)?; + layouter.constrain_instance( + coin2_nonce.inner().x().cell(), + config.primary, + LEAD_COIN_PK_X_OFFSET, + )?; + // constrain coin's pub key y value + layouter.constrain_instance( + coin2_nonce.inner().y().cell(), + coinfig.primary, + CLEAD_COIN_PK_Y_OFFSET, + )?; + // ================ + // coin public key constraints derived from the coin timestamp + // ================ + + // m*G_1 + let (com, _ ) = { + let coin_pk_commit_v = ValueCommitV; + let coin_pk_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), coin_pk_commit_v); + coin_timestamp_commit_v.mul(layouter.namespace(|| "coin_pk commit v"), (coin_timestamp, one))? + }; + // r*G_2 + let (blind, _) = { + let coin_pk_commit_r = OrchardFixedBasesFull::ValueCommitR; + let coin_pk_commit_r = FixedPoint::from_inner(ecc_chip.clone(), coin_pk_commit_r); + coin_timestamp_commit_r.mul(layouter.namespace(|| "coin_pk commit R"), (coin_root.clone(), one))? + }; + let coin_pk_commit = com.add(layouter.namespace(|| "coin timestamp commit"), &blind)?; + // constrain coin's pub key x value + layouter.constrain_instance( + coin_pk_commit.inner().x().cell(), + config.primary, + LEAD_COIN_PK_X_OFFSET, + )?; + // constrain coin's pub key y value + layouter.constrain_instance( + coin_pk_commit.inner().y().cell(), + coinfig.primary, + CLEAD_COIN_PK_Y_OFFSET, + )?; + + // ================= + // nonce constraints derived from previous coin's nonce + // ================= + + + // ============= + // constrain coin c1 serial number + // ============= + // m*G_1 + let (com, _ ) = { + let sn_commit_v = ValueCommitV; + let sn_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), sn_commit_v); + sn_commit_v.mul(layouter.namespace(|| "coin serial number commit v"), (coin_nonce, one))? + }; + // r*G_2 + let (blind, _) = { + let sn_commit_r = OrchardFixedBasesFull::ValueCommitR; + let sn_commit_r = FixedPoint::from_inner(ecc_chip.clone(), sn_commit_r); + sn_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), (coin_root.clone(), one))? + }; + // + let sn_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?; + // constrain coin's pub key x value + layouter.constrain_instance( + sn_commit.inner().x().cell(), + config.primary, + LEAD_COIN_SERIAL_NUMBER_X_OFFSET, + )?; + // constrain coin's pub key y value + layouter.constrain_instance( + nonce_commit.inner().y().cell(), + coinfig.primary, + LEAD_COIN_SERIAL_NUMBER_X_OFFSET, + )?; + + // ========================== + // commitment of coins c1,c2 + // ========================== + //TODO should the reward be added to new minted coin? + // read the commitment + // concatenate message + // subtract those cm and commit output, constraint the output, that should equal 1. + // + //TODO this proof need to be for the two coins commitment, + // but cm1 doesn't exist in public inputs, or witnesses, + // does it make sense to calculate cm1, and proof decomm(cm1, coin2, r2)=true? + // that doesn't make any sense! + // this is the proof for the second commitment only + //TODO does both coins have the same value?!! doesn't make sense + //but only single value is in witness. + //let coin_tup = [coin_pk_commit, coin_value, coin2_nonce]; + //TODO this should be the concat of coin_tup + let coin_ = ar_chip.add(layouter.namespace(|| ""), (coin_pk_commit, coin_value))?; + let coin = ar_chip.add(layouter.namespace(|| ""), (coin_, coin_nonce))?; + let (com, _ ) = { + let coin_commit_v = ValueCommitV; + let coin_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), coin_commit_v); + sn_commit_v.mul(layouter.namespace(|| "coin commit v"), (coin, one))? + }; + // r*G_2 + let (blind, _) = { + let sn_commit_r = OrchardFixedBasesFull::ValueCommitR; + let sn_commit_r = FixedPoint::from_inner(ecc_chip.clone(), coin_opening_1); + sn_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), (coin_root, one))? + }; + let coin_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?; + //TODO it would be better if you coin1_commit + -1*cm_c2 and constraint 0 (output) + // constrain coin's pub key x value + layouter.constrain_instance( + coin_commit.inner().x().cell(), + config.primary, + LEAD_COIN_SERIAL_NUMBER_X_OFFSET, + )?; + // constrain coin's pub key y value + layouter.constrain_instance( + coin_commit.inner().y().cell(), + config.primary, + LEAD_COIN_SERIAL_NUMBER_X_OFFSET, + )?; + //========================= + let coin = ar_chip.add(layouter.namespace(|| ""), (coin_, coin2_nonce))?; + let (com, _ ) = { + let coin_commit_v = ValueCommitV; + let coin_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), coin_commit_v); + sn_commit_v.mul(layouter.namespace(|| "coin commit v"), (coin, one))? + }; + // r*G_2 + let (blind, _) = { + let r = self.root_sk; + let sn_commit_r = OrchardFixedBasesFull::ValueCommitR; + let sn_commit_r = FixedPoint::from_inner(ecc_chip.clone(), coin_opening_2); + sn_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), r)? + }; + let coin2_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?; + layouter.constrain_instance( + coin2_commit.inner().x().cell(), + config.primary, + LEAD_COIN2_SERIAL_NUMBER_X_OFFSET, + )?; + // constrain coin's pub key y value + layouter.constrain_instance( + coin2_commit.inner().y().cell(), + config.primary, + LEAD_COIN2_SERIAL_NUMBER_X_OFFSET, + )?; + //TODO you need to add this coin1_commit to the tree and return rooted by self.root, + // and return the position coin1_commit_pos + let coin1_commit_pos : u32 = 0; + // =========================== + let path: Option<[pallas::Base; MERKLE_DEPTH_ORCHARD]> = + self.path.map(|typed_path| gen_const_array(|i| typed_path[i].inner())); + + let merkle_inputs = MerklePath::construct( + config.merkle_chip_1(), + config.merkle_chip_2(), + OrchardHashDomains::MerkleCrh, + coin1_commit_pos, + path, + ); + + let computed_final_root = + merkle_inputs.calculate_root(layouter.namespace(|| "calculate root"), coin1_commit)?; + + layouter.constrain_instance( + computed_final_root.cell(), + config.primary, + LEAD_COIN_COMMIT_PATH_OFFSET, + )?; + // ============================= + /* + let path: Option<[pallas::Base; MERKLE_DEPTH_ORCHARD]> = + self.path_sk.map(|typed_path| gen_const_array(|i| typed_path[i].inner())); + + let merkle_inputs = MerklePath::construct( + config.merkle_chip_1(), + config.merkle_chip_2(), + OrchardHashDomains::MerkleCrh, + self.slot - self.coin_timestamp, + path, + ); + + //TODO fix this is a path to a leaf, i have no clue of that leaf + let computed_final_root = + merkle_inputs.calculate_root(layouter.namespace(|| "calculate root"), coin1_commit)?; + + layouter.constrain_instance( + computed_final_root.cell(), + config.primary, + LEAD_COIN_COMMIT_PATH_OFFSET, + )?; + */ + // ============================ + // constrain y + // + let message = ar_chip.add(layouter.namespace(|| ""), root_sk, coin_nonce)?; + let (com, _ ) = { + //TODO concatenate message + //TODO message need to be eccchip::var + //let message = [root_sk, coin_nonce]; + let y_commit_v = ValueCommitV; + let y_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), y_commit_v); + y_commit_v.mul(layouter.namespace(|| "coin commit v"), (message.clone(), one))? + }; + // r*G_2 + let (blind, _) = { + //let r = mau_y; + let y_commit_r = OrchardFixedBasesFull::ValueCommitR; + let y_commit_r = FixedPoint::from_inner(ecc_chip.clone(), y_commit_r); + y_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), (mau_y, one))? + }; + let y_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?; + // ============================ + // constraint rho + // + let (com, _ ) = { + //TODO concatenate message + //TODO message need to be eccchip::var + //let message = [root_sk, coin_nonce]; + let y_commit_v = ValueCommitV; + let y_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), y_commit_v); + y_commit_v.mul(layouter.namespace(|| "coin commit v"), (message.clone(), one))? + }; + // r*G_2 + let (blind, _) = { + let r = mau_rho; + let y_commit_r = OrchardFixedBasesFull::ValueCommitR; + let y_commit_r = FixedPoint::from_inner(ecc_chip.clone(), y_commit_r); + y_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), r)? + }; + let rho_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?; + + // =========================== + // lead statment + // =========================== + let scalar = pallas::Scalar::from(1024); + let c = pallas::Scalar::from(3); // leadership coefficient + let target = ar_chip.mul(layouter.namespace(|| "calculate target"), scalar, value)?; + + let greater_than_chip = config.greaterthan_config(); + greater_than_chip.greater_than(layouter.namespace("t>y"), target , y_commit); + layouter.constrain_instance( + greater_than_chip.cell(), + config.primary, + LEAD_LEAD_THRESHOLD_OFFSET, + )?; + } +}