main.rs 6.5 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, false).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. We are using `eprintln` for execution logs so they are
  59. // printed in `stderr` as we want to print just the encoded transaction
  60. // in `stdout` for further processing.
  61. eprintln!("Generating {} circuit ProvingKey...", zkbin.namespace);
  62. let circuit = ZkCircuit::new(circuit_witnesses, &zkbin);
  63. let proving_key = ProvingKey::build(zkbin.k, &circuit);
  64. // Now create the ZK proof
  65. eprintln!("Generating {} ZK proof...", zkbin.namespace);
  66. let circuit = ZkCircuit::new(prover_witnesses, &zkbin);
  67. let proof = Proof::create(&proving_key, &[circuit], &public_inputs, &mut OsRng).unwrap();
  68. // Create the contract call.
  69. // We will call the `Register` function, and we'll use our secret key
  70. // as one of the transaction signatures. This will be recognized by the
  71. // wallet when it is imported and it will use the key to sign the tx along
  72. // any other keys it needs from the wallet.
  73. let params = MembershipParams {
  74. member: keypair.public,
  75. };
  76. // Build the payload
  77. let mut payload = vec![ContractFunction::Register as u8];
  78. payload.extend_from_slice(&serialize(&params));
  79. let call = ContractCall {
  80. contract_id: cid,
  81. data: payload,
  82. };
  83. // Now we'll pack it all up for the wallet. It is printed on stdout
  84. // encoded as base64 which can later be imported by the wallet when
  85. // creating the full transaction.
  86. let call_import =
  87. ContractCallImport::new(call, vec![proof.as_ref().to_vec()], vec![keypair.secret]);
  88. println!("{}", base64::encode(&serialize(&call_import)));
  89. }
  90. /// Create a deregister call
  91. fn deregister(argv0: &str, cid: ContractId, secret_key: Option<String>) {
  92. // This function is mostly the same as `fn register()` with the only
  93. // exception being the Contract Function. So for explanations, just
  94. // reference the above.
  95. let Some(secret_key) = secret_key else {
  96. return usage(argv0);
  97. };
  98. let zkbin = ZkBinary::decode(ZKBIN, false).unwrap();
  99. let secret_key = SecretKey::from_str(&secret_key).unwrap();
  100. let keypair = Keypair::new(secret_key);
  101. let (pub_x, pub_y) = keypair.public.xy();
  102. let public_inputs = vec![pub_x, pub_y];
  103. let circuit_witnesses = empty_witnesses(&zkbin).unwrap();
  104. let prover_witnesses = vec![Witness::Base(Value::known(keypair.secret.inner()))];
  105. eprintln!("Generating {} circuit ProvingKey...", zkbin.namespace);
  106. let circuit = ZkCircuit::new(circuit_witnesses, &zkbin);
  107. let proving_key = ProvingKey::build(zkbin.k, &circuit);
  108. eprintln!("Generating {} ZK proof...", zkbin.namespace);
  109. let circuit = ZkCircuit::new(prover_witnesses, &zkbin);
  110. let proof = Proof::create(&proving_key, &[circuit], &public_inputs, &mut OsRng).unwrap();
  111. let params = MembershipParams {
  112. member: keypair.public,
  113. };
  114. let mut payload = vec![ContractFunction::Deregister as u8];
  115. payload.extend_from_slice(&serialize(&params));
  116. let call = ContractCall {
  117. contract_id: cid,
  118. data: payload,
  119. };
  120. let call_import =
  121. ContractCallImport::new(call, vec![proof.as_ref().to_vec()], vec![keypair.secret]);
  122. println!("{}", base64::encode(&serialize(&call_import)));
  123. }
  124. fn main() {
  125. let mut args = env::args();
  126. let argv0 = args.next().unwrap();
  127. let Some(command) = args.next() else {
  128. return usage(&argv0);
  129. };
  130. if command.as_str() == "generate" {
  131. return generate_keypair();
  132. }
  133. let Some(contract_id) = args.next() else {
  134. return usage(&argv0);
  135. };
  136. let cid = ContractId::from_str(&contract_id).unwrap();
  137. match command.as_str() {
  138. "register" => register(&argv0, cid, args.next()),
  139. "deregister" => deregister(&argv0, cid, args.next()),
  140. _ => usage(&argv0),
  141. }
  142. }