mint.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. use std::{convert::TryInto, time::Instant};
  2. use group::{ff::Field, Curve, Group};
  3. use halo2::{
  4. arithmetic::CurveAffine,
  5. circuit::{floor_planner, Layouter},
  6. dev::MockProver,
  7. pasta::{vesta, Ep, Fp, Fq},
  8. plonk,
  9. plonk::{Circuit, ConstraintSystem, Error},
  10. poly::commitment,
  11. transcript::{Blake2bRead, Blake2bWrite},
  12. };
  13. use halo2_ecc::{chip::EccChip, gadget::FixedPoint};
  14. use halo2_poseidon::{
  15. gadget::{Hash as PoseidonHash, Word},
  16. pow5t3::{Pow5T3Chip as PoseidonChip, StateWord},
  17. primitive::{ConstantLength, Hash, P128Pow5T3 as OrchardNullifier},
  18. };
  19. use halo2_utilities::{
  20. lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
  21. };
  22. use orchard::constants::fixed_bases::OrchardFixedBases;
  23. use rand::rngs::OsRng;
  24. use halo2_examples::{circuit::Config, pedersen_commitment};
  25. const K: u32 = 9;
  26. #[derive(Default, Debug)]
  27. struct MintCircuit {
  28. pub_x: Option<Fp>, // x coordinate for pubkey
  29. pub_y: Option<Fp>, // y coordinate for pubkey
  30. value: Option<Fp>, // The value of this coin
  31. asset: Option<Fp>, // The asset ID
  32. serial: Option<Fp>, // Unique serial number corresponding to this coin
  33. coin_blind: Option<Fp>, // Random blinding factor for coin
  34. value_blind: Option<Fq>, // Random blinding factor for value commitment
  35. asset_blind: Option<Fq>, // Random blinding factor for the asset ID
  36. }
  37. impl UtilitiesInstructions<Fp> for MintCircuit {
  38. type Var = CellValue<Fp>;
  39. }
  40. impl Circuit<Fp> for MintCircuit {
  41. type Config = Config;
  42. type FloorPlanner = floor_planner::V1;
  43. //type FloorPlanner = SimpleFloorPlanner;
  44. fn without_witnesses(&self) -> Self {
  45. Self::default()
  46. }
  47. fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
  48. let advices = [
  49. meta.advice_column(),
  50. meta.advice_column(),
  51. meta.advice_column(),
  52. meta.advice_column(),
  53. meta.advice_column(),
  54. meta.advice_column(),
  55. meta.advice_column(),
  56. meta.advice_column(),
  57. meta.advice_column(),
  58. meta.advice_column(),
  59. ];
  60. let q_add = meta.selector();
  61. let table_idx = meta.lookup_table_column();
  62. // let lookup = (
  63. // table_idx,
  64. // meta.lookup_table_column(),
  65. // meta.lookup_table_column(),
  66. // );
  67. let primary = meta.instance_column();
  68. meta.enable_equality(primary.into());
  69. for advice in advices.iter() {
  70. meta.enable_equality((*advice).into());
  71. }
  72. let lagrange_coeffs = [
  73. meta.fixed_column(),
  74. meta.fixed_column(),
  75. meta.fixed_column(),
  76. meta.fixed_column(),
  77. meta.fixed_column(),
  78. meta.fixed_column(),
  79. meta.fixed_column(),
  80. meta.fixed_column(),
  81. ];
  82. let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
  83. let rc_b = lagrange_coeffs[5..8].try_into().unwrap();
  84. meta.enable_constant(lagrange_coeffs[0]);
  85. let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
  86. let ecc_config = EccChip::<OrchardFixedBases>::configure(
  87. meta,
  88. advices,
  89. lagrange_coeffs,
  90. range_check.clone(),
  91. );
  92. let poseidon_config = PoseidonChip::configure(
  93. meta,
  94. OrchardNullifier,
  95. advices[6..9].try_into().unwrap(),
  96. advices[5],
  97. rc_a,
  98. rc_b,
  99. );
  100. Config {
  101. primary,
  102. q_add,
  103. advices,
  104. ecc_config,
  105. poseidon_config,
  106. }
  107. }
  108. fn synthesize(
  109. &self,
  110. config: Self::Config,
  111. mut layouter: impl Layouter<Fp>,
  112. ) -> Result<(), Error> {
  113. // Construct the ECC chip.
  114. let ecc_chip = EccChip::construct(config.ecc_config.clone());
  115. let pub_x = self.load_private(
  116. layouter.namespace(|| "load pubkey x"),
  117. config.advices[0],
  118. self.pub_x,
  119. )?;
  120. let pub_y = self.load_private(
  121. layouter.namespace(|| "load pubkey y"),
  122. config.advices[0],
  123. self.pub_y,
  124. )?;
  125. let value = self.load_private(
  126. layouter.namespace(|| "load value"),
  127. config.advices[0],
  128. self.value,
  129. )?;
  130. let asset = self.load_private(
  131. layouter.namespace(|| "load asset"),
  132. config.advices[0],
  133. self.asset,
  134. )?;
  135. let serial = self.load_private(
  136. layouter.namespace(|| "load serial"),
  137. config.advices[0],
  138. self.serial,
  139. )?;
  140. let coin_blind = self.load_private(
  141. layouter.namespace(|| "load coin_blind"),
  142. config.advices[0],
  143. self.coin_blind,
  144. )?;
  145. // =============
  146. // = Coin hash =
  147. // =============
  148. // TODO: This is a hack until issue is resolved in poseidon gadget
  149. let mut coin = Fp::zero();
  150. let messages = [[pub_x, pub_y], [value, asset], [serial, coin_blind]];
  151. //let messages = [[pub_x, pub_y], [value, asset]];
  152. //let messages = [[pub_x, pub_y]];
  153. for msg in messages.iter() {
  154. let poseidon_message = layouter.assign_region(
  155. || "load message",
  156. |mut region| {
  157. let mut message_word = |i: usize| {
  158. let val = msg[i].value();
  159. let var = region.assign_advice(
  160. || format!("load message_{}", i),
  161. config.poseidon_config.state()[i],
  162. 0,
  163. || val.ok_or(Error::SynthesisError),
  164. )?;
  165. region.constrain_equal(var, msg[i].cell())?;
  166. Ok(Word::<_, _, OrchardNullifier, 3, 2>::from_inner(
  167. StateWord::new(var, val),
  168. ))
  169. };
  170. Ok([message_word(0)?, message_word(1)?])
  171. },
  172. )?;
  173. let poseidon_hasher = PoseidonHash::init(
  174. PoseidonChip::construct(config.poseidon_config.clone()),
  175. layouter.namespace(|| "Poseidon init"),
  176. ConstantLength::<2>,
  177. )?;
  178. let poseidon_output =
  179. poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
  180. let poseidon_output: CellValue<Fp> = poseidon_output.inner().into();
  181. if !poseidon_output.value().is_none() {
  182. coin += poseidon_output.value().unwrap();
  183. }
  184. }
  185. // if coin != Fp::zero() {
  186. // println!("circuit hash: {:?}", coin);
  187. // }
  188. let hash = self.load_private(
  189. layouter.namespace(|| "load hash"),
  190. config.advices[0],
  191. Some(coin),
  192. )?;
  193. // Constrain the coin C; index in public values is 0
  194. layouter.constrain_instance(hash.cell(), config.primary, 0)?;
  195. // ====================
  196. // = Value commitment =
  197. // ====================
  198. // This constant one is used for multiplication
  199. let one = self.load_constant(
  200. layouter.namespace(|| "constant one"),
  201. config.advices[0],
  202. Fp::one(),
  203. )?;
  204. // v*G_1
  205. let (commitment, _) = {
  206. let value_commit_v = OrchardFixedBases::ValueCommitV;
  207. let value_commit_v = FixedPoint::from_inner(ecc_chip.clone(), value_commit_v);
  208. value_commit_v.mul_short(layouter.namespace(|| "[value] ValueCommitV"), (value, one))?
  209. };
  210. // r_V*G_2
  211. let (blind, _rcv) = {
  212. let rcv = self.value_blind;
  213. let value_commit_r = OrchardFixedBases::ValueCommitR;
  214. let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
  215. value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
  216. };
  217. // Constrain the x and y; indexes in public values are 1 and 2
  218. let value_commit = commitment.add(layouter.namespace(|| "valuecommit"), &blind)?;
  219. layouter.constrain_instance(value_commit.inner().x().cell(), config.primary, 1)?;
  220. layouter.constrain_instance(value_commit.inner().y().cell(), config.primary, 2)?;
  221. // ====================
  222. // = Asset commitment =
  223. // ====================
  224. // a*G_1
  225. let (commitment, _) = {
  226. let asset_commit_v = OrchardFixedBases::ValueCommitV;
  227. let asset_commit_v = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_v);
  228. asset_commit_v.mul_short(layouter.namespace(|| "[asset] ValueCommitV"), (asset, one))?
  229. };
  230. // r_A*G_2
  231. let (blind, _rca) = {
  232. let rca = self.asset_blind;
  233. let asset_commit_r = OrchardFixedBases::ValueCommitR;
  234. let asset_commit_r = FixedPoint::from_inner(ecc_chip.clone(), asset_commit_r);
  235. asset_commit_r.mul(layouter.namespace(|| "[asset_blind] ValueCommitR"), rca)?
  236. };
  237. // Constrain the x and y; indexes in public values are 3 and 4
  238. let asset_commit = commitment.add(layouter.namespace(|| "assetcommit"), &blind)?;
  239. layouter.constrain_instance(asset_commit.inner().x().cell(), config.primary, 3)?;
  240. layouter.constrain_instance(asset_commit.inner().y().cell(), config.primary, 4)?;
  241. Ok(())
  242. }
  243. }
  244. #[derive(Debug)]
  245. struct VerifyingKey {
  246. params: commitment::Params<vesta::Affine>,
  247. vk: plonk::VerifyingKey<vesta::Affine>,
  248. }
  249. impl VerifyingKey {
  250. fn build() -> Self {
  251. let params = commitment::Params::new(K);
  252. let circuit: MintCircuit = Default::default();
  253. let vk = plonk::keygen_vk(&params, &circuit).unwrap();
  254. VerifyingKey { params, vk }
  255. }
  256. }
  257. #[derive(Debug)]
  258. struct ProvingKey {
  259. params: commitment::Params<vesta::Affine>,
  260. pk: plonk::ProvingKey<vesta::Affine>,
  261. }
  262. impl ProvingKey {
  263. fn build() -> Self {
  264. let params = commitment::Params::new(K);
  265. let circuit: MintCircuit = Default::default();
  266. let vk = plonk::keygen_vk(&params, &circuit).unwrap();
  267. let pk = plonk::keygen_pk(&params, vk, &circuit).unwrap();
  268. ProvingKey { params, pk }
  269. }
  270. }
  271. #[derive(Clone, Debug)]
  272. struct Proof(Vec<u8>);
  273. impl AsRef<[u8]> for Proof {
  274. fn as_ref(&self) -> &[u8] {
  275. &self.0
  276. }
  277. }
  278. impl Proof {
  279. fn create(pk: &ProvingKey, circuits: &[MintCircuit], pubinputs: &[Fp]) -> Result<Self, Error> {
  280. let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]);
  281. plonk::create_proof(
  282. &pk.params,
  283. &pk.pk,
  284. circuits,
  285. &[&[pubinputs]],
  286. &mut transcript,
  287. )?;
  288. Ok(Proof(transcript.finalize()))
  289. }
  290. fn verify(&self, vk: &VerifyingKey, pubinputs: &[Fp]) -> Result<(), plonk::Error> {
  291. let msm = vk.params.empty_msm();
  292. let mut transcript = Blake2bRead::init(&self.0[..]);
  293. let guard = plonk::verify_proof(&vk.params, &vk.vk, msm, &[&[pubinputs]], &mut transcript)?;
  294. let msm = guard.clone().use_challenges();
  295. if msm.eval() {
  296. Ok(())
  297. } else {
  298. Err(Error::ConstraintSystemFailure)
  299. }
  300. }
  301. // fn new(bytes: Vec<u8>) -> Self {
  302. // Proof(bytes)
  303. // }
  304. }
  305. fn main() {
  306. let pubkey = Ep::random(&mut OsRng);
  307. let coords = pubkey.to_affine().coordinates().unwrap();
  308. let value = 110;
  309. let asset = 1;
  310. let value_blind = Fq::random(&mut OsRng);
  311. let asset_blind = Fq::random(&mut OsRng);
  312. let serial = Fp::random(&mut OsRng);
  313. let coin_blind = Fp::random(&mut OsRng);
  314. let mut coin = Fp::zero();
  315. let messages = [
  316. [*coords.x(), *coords.y()],
  317. [Fp::from(value), Fp::from(asset)],
  318. [serial, coin_blind],
  319. ];
  320. // TODO: This is a hack until issue is fixed in poseidon gadget
  321. for msg in messages.iter() {
  322. coin += Hash::init(OrchardNullifier, ConstantLength::<2>).hash(*msg);
  323. }
  324. let value_commit = pedersen_commitment(value, value_blind);
  325. let value_coords = value_commit.to_affine().coordinates().unwrap();
  326. let asset_commit = pedersen_commitment(asset, asset_blind);
  327. let asset_coords = asset_commit.to_affine().coordinates().unwrap();
  328. let mut public_inputs = vec![
  329. coin,
  330. *value_coords.x(),
  331. *value_coords.y(),
  332. *asset_coords.x(),
  333. *asset_coords.y(),
  334. ];
  335. let circuit = MintCircuit {
  336. pub_x: Some(*coords.x()),
  337. pub_y: Some(*coords.y()),
  338. value: Some(vesta::Scalar::from(value)),
  339. asset: Some(vesta::Scalar::from(asset)),
  340. serial: Some(serial),
  341. coin_blind: Some(coin_blind),
  342. value_blind: Some(value_blind),
  343. asset_blind: Some(asset_blind),
  344. };
  345. // Valid MockProver
  346. let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap();
  347. assert_eq!(prover.verify(), Ok(()));
  348. // Add 1 to break the public inputs
  349. public_inputs[0] += Fp::from(0xdeadbeef);
  350. // Invalid MockProver
  351. let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap();
  352. assert!(prover.verify().is_err());
  353. // Remove 1 to make the public inputs valid again
  354. public_inputs[0] -= Fp::from(0xdeadbeef);
  355. // Actual ZK proof
  356. let start = Instant::now();
  357. let vk = VerifyingKey::build();
  358. let pk = ProvingKey::build();
  359. println!("\nSetup: [{:?}]", start.elapsed());
  360. let start = Instant::now();
  361. let proof = Proof::create(&pk, &[circuit], &public_inputs).unwrap();
  362. println!("Prove: [{:?}]", start.elapsed());
  363. let start = Instant::now();
  364. assert!(proof.verify(&vk, &public_inputs).is_ok());
  365. println!("Verify: [{:?}]", start.elapsed());
  366. }