main.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // SPDX-License-Identifier: AGPL-3.0-only
  2. //! This is an example program that is used to create the contract calls
  3. //! for our contract. The generated contract calls can be fed into the `drk`
  4. //! wallet utility to create a full transaction.
  5. use std::str::FromStr;
  6. use std::{env, process};
  7. use darkfi::util::encoding::base64;
  8. use darkfi::zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, Witness, ZkCircuit};
  9. use darkfi::zkas::ZkBinary;
  10. use darkfi_sdk::crypto::{ContractId, Keypair, SecretKey};
  11. use darkfi_sdk::{ContractCall, ContractCallImport};
  12. use darkfi_serial::serialize;
  13. use rand::rngs::OsRng;
  14. // This comes from our contract's lib.rs
  15. use membership::{ContractFunction, MembershipParams};
  16. const USAGE: &str = r#"
  17. Commands:
  18. generate - Creates a keypair for the membership proof
  19. register <contract-id> <secret_key> - Creates a Register contract call for the given key
  20. deregister <contract-id> <secret_key> - Creates a Deregister contract call for the given key
  21. "#;
  22. /// We have to include the compiled ZK circuit in order to create ZK proofs
  23. const ZKBIN: &[u8] = include_bytes!("../proof/membership_proof.zk.bin");
  24. fn usage(argv0: &str) {
  25. eprintln!("Usage: {argv0} <commmand> <args>\n{USAGE}");
  26. process::exit(1);
  27. }
  28. /// Generate a new membership keypair
  29. fn generate_keypair() {
  30. let keypair = Keypair::random(&mut OsRng);
  31. println!("Secret key: {}", keypair.secret);
  32. println!("Public key: {}", keypair.public);
  33. }
  34. /// Create a Register call
  35. fn register(argv0: &str, cid: ContractId, secret_key: Option<String>) {
  36. let Some(secret_key) = secret_key else {
  37. return usage(argv0);
  38. };
  39. // Let's parse the compiled ZK circuit and create a proof.
  40. let zkbin = ZkBinary::decode(ZKBIN).unwrap();
  41. // For the membership, we will use our secret key.
  42. // We'll decode it from the input.
  43. let secret_key = SecretKey::from_str(&secret_key).unwrap();
  44. let keypair = Keypair::new(secret_key);
  45. // Our circuit derives the public key by multiplying the secret
  46. // with a generator point on the curve. The `PublicKey` from the
  47. // `Keypair` object already did this. Manually it would be:
  48. // public_key = NullifierK.generator() * fp_mod_fv(secret_key.inner());
  49. // So our public inputs for the circuit are the public key's coordinates.
  50. // They have to be in the same order as the `constrain_instance` calls
  51. // in the ZK circuit.
  52. let (pub_x, pub_y) = keypair.public.xy();
  53. let public_inputs = vec![pub_x, pub_y];
  54. let circuit_witnesses = empty_witnesses(&zkbin).unwrap();
  55. // For our private witness values, we'll use our secret key.
  56. let prover_witnesses = vec![Witness::Base(Value::known(keypair.secret.inner()))];
  57. // Now we create the circuit and its ProvingKey so we are able to create
  58. // the ZK proof.
  59. eprintln!("Generating {} circuit ProvingKey...", zkbin.namespace);
  60. let circuit = ZkCircuit::new(circuit_witnesses, &zkbin);
  61. let proving_key = ProvingKey::build(zkbin.k, &circuit);
  62. // Now create the ZK proof
  63. eprintln!("Generating {} ZK proof...", zkbin.namespace);
  64. let circuit = ZkCircuit::new(prover_witnesses, &zkbin);
  65. let proof = Proof::create(&proving_key, &[circuit], &public_inputs, &mut OsRng).unwrap();
  66. // Create the contract call.
  67. // We will call the `Register` function, and we'll use our secret key
  68. // as one of the transaction signatures. This will be recognized by the
  69. // wallet when it is imported and it will use the key to sign the tx along
  70. // any other keys it needs from the wallet.
  71. let params = MembershipParams {
  72. member: keypair.public,
  73. };
  74. // Build the payload
  75. let mut payload = vec![ContractFunction::Register as u8];
  76. payload.extend_from_slice(&serialize(&params));
  77. let call = ContractCall {
  78. contract_id: cid,
  79. data: payload,
  80. };
  81. // Now we'll pack it all up for the wallet. It is printed on stdout
  82. // encoded as base64 which can later be imported by the wallet when
  83. // creating the full transaction.
  84. let call_import =
  85. ContractCallImport::new(call, vec![proof.as_ref().to_vec()], vec![keypair.secret]);
  86. println!("{}", base64::encode(&serialize(&call_import)));
  87. }
  88. /// Create a deregister call
  89. fn deregister(argv0: &str, cid: ContractId, secret_key: Option<String>) {
  90. // This function is mostly the same as `fn register()` with the only
  91. // exception being the Contract Function. So for explanations, just
  92. // reference the above.
  93. let Some(secret_key) = secret_key else {
  94. return usage(argv0);
  95. };
  96. let zkbin = ZkBinary::decode(ZKBIN).unwrap();
  97. let secret_key = SecretKey::from_str(&secret_key).unwrap();
  98. let keypair = Keypair::new(secret_key);
  99. let (pub_x, pub_y) = keypair.public.xy();
  100. let public_inputs = vec![pub_x, pub_y];
  101. let circuit_witnesses = empty_witnesses(&zkbin).unwrap();
  102. let prover_witnesses = vec![Witness::Base(Value::known(keypair.secret.inner()))];
  103. eprintln!("Generating {} circuit ProvingKey...", zkbin.namespace);
  104. let circuit = ZkCircuit::new(circuit_witnesses, &zkbin);
  105. let proving_key = ProvingKey::build(zkbin.k, &circuit);
  106. eprintln!("Generating {} ZK proof...", zkbin.namespace);
  107. let circuit = ZkCircuit::new(prover_witnesses, &zkbin);
  108. let proof = Proof::create(&proving_key, &[circuit], &public_inputs, &mut OsRng).unwrap();
  109. let params = MembershipParams {
  110. member: keypair.public,
  111. };
  112. let mut payload = vec![ContractFunction::Deregister as u8];
  113. payload.extend_from_slice(&serialize(&params));
  114. let call = ContractCall {
  115. contract_id: cid,
  116. data: payload,
  117. };
  118. let call_import =
  119. ContractCallImport::new(call, vec![proof.as_ref().to_vec()], vec![keypair.secret]);
  120. println!("{}", base64::encode(&serialize(&call_import)));
  121. }
  122. fn main() {
  123. let mut args = env::args();
  124. let argv0 = args.next().unwrap();
  125. let Some(command) = args.next() else {
  126. return usage(&argv0);
  127. };
  128. if command.as_str() == "generate" {
  129. return generate_keypair();
  130. }
  131. let Some(contract_id) = args.next() else {
  132. return usage(&argv0);
  133. };
  134. let cid = ContractId::from_str(&contract_id).unwrap();
  135. match command.as_str() {
  136. "register" => register(&argv0, cid, args.next()),
  137. "deregister" => deregister(&argv0, cid, args.next()),
  138. _ => return usage(&argv0),
  139. }
  140. process::exit(1);
  141. }