main.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{collections::BTreeMap, str::FromStr, sync::Arc};
  19. use clap::{Parser, Subcommand};
  20. use darkfi::{
  21. cli_desc,
  22. rpc::{client::RpcClient, jsonrpc::JsonRequest, util::JsonValue},
  23. tx::{ContractCallLeaf, TransactionBuilder},
  24. util::encoding::base64,
  25. zk::{empty_witnesses, ProvingKey, ZkCircuit},
  26. zkas::ZkBinary,
  27. Error, Result,
  28. };
  29. use darkfi_sdk::{
  30. crypto::{ContractId, Keypair, PublicKey, SecretKey},
  31. pasta::pallas,
  32. ContractCall,
  33. };
  34. use darkfi_serial::{deserialize, serialize, Encodable};
  35. use smol::Executor;
  36. use url::Url;
  37. use wasm_hello_world::{
  38. ContractFunction, HELLO_CONTRACT_MEMBER_TREE, HELLO_CONTRACT_ZKAS_SECRETCOMMIT_NS,
  39. };
  40. mod commitment;
  41. use commitment::ContractCallBuilder;
  42. #[derive(Parser)]
  43. #[command(about = cli_desc!())]
  44. struct Args {
  45. #[arg(short, long)]
  46. /// Deployed Contract ID
  47. contract_id: String,
  48. #[arg(short, long, default_value = "tcp://127.0.0.1:8340")]
  49. /// darkfid JSON-RPC endpoint
  50. endpoint: Url,
  51. #[command(subcommand)]
  52. command: Subcmd,
  53. }
  54. #[derive(Subcommand)]
  55. enum Subcmd {
  56. /// Display current members
  57. List {
  58. /// Specific member to check if present (optional)
  59. member: Option<String>,
  60. },
  61. /// Generate a transaction adding a new member
  62. Register {
  63. /// To be added member secret key
  64. member_secret: String,
  65. },
  66. /// Generate a transaction removing a member
  67. Deregister {
  68. /// To be removed member secret key
  69. member_secret: String,
  70. },
  71. }
  72. fn main() -> Result<()> {
  73. // Parse arguments
  74. let args = Args::parse();
  75. let contract_id = match ContractId::from_str(&args.contract_id) {
  76. Ok(c) => c,
  77. Err(e) => {
  78. eprintln!("Invalid contract id: {e}");
  79. return Err(Error::ParseFailed("Invalid contract id"));
  80. }
  81. };
  82. // Initialize an executor
  83. let executor = Arc::new(Executor::new());
  84. smol::block_on(executor.run(async {
  85. // Initialize an rpc client
  86. let rpc_client = RpcClient::new(args.endpoint, executor.clone()).await?;
  87. // Execute a subcommand
  88. match args.command {
  89. Subcmd::List { member } => {
  90. match member {
  91. // Check if specific member exists in our contract members tree
  92. Some(member) => {
  93. // Parse the member public key
  94. let member = PublicKey::from_str(&member)?;
  95. // Create the request params
  96. let params = JsonValue::Array(vec![
  97. JsonValue::String(contract_id.to_string()),
  98. JsonValue::String(HELLO_CONTRACT_MEMBER_TREE.to_string()),
  99. JsonValue::String(member.to_string()),
  100. ]);
  101. // Execute the request
  102. let req = JsonRequest::new("blockchain.get_contract_state_key", params);
  103. let rep = rpc_client.request(req).await?;
  104. // Parse response
  105. let bytes = base64::decode(rep.get::<String>().unwrap()).unwrap();
  106. // Print info message
  107. println!("Member {member} was found!");
  108. println!("Value validity check: {}", bytes.is_empty());
  109. }
  110. // Retrieve all contract members tree records
  111. None => {
  112. // Create the request params
  113. let params = JsonValue::Array(vec![
  114. JsonValue::String(contract_id.to_string()),
  115. JsonValue::String(HELLO_CONTRACT_MEMBER_TREE.to_string()),
  116. ]);
  117. // Execute the request
  118. let req = JsonRequest::new("blockchain.get_contract_state", params);
  119. let rep = rpc_client.request(req).await?;
  120. // Parse response
  121. let bytes = base64::decode(rep.get::<String>().unwrap()).unwrap();
  122. let members: BTreeMap<Vec<u8>, Vec<u8>> = deserialize(&bytes)?;
  123. // Print records
  124. println!("{contract_id} members:");
  125. if members.is_empty() {
  126. println!("No members found");
  127. } else {
  128. let mut index = 1;
  129. for member in members.keys() {
  130. let member: pallas::Base = deserialize(member)?;
  131. println!("{index}. {member:?}");
  132. index += 1;
  133. }
  134. }
  135. }
  136. }
  137. }
  138. Subcmd::Register { member_secret } => {
  139. // Parse the member secret key
  140. let member_secret = SecretKey::from_str(&member_secret)?;
  141. let member = Keypair::new(member_secret);
  142. // Now we need to do a lookup for the zkas proof bincodes, and create
  143. // the circuit objects and proving keys so we can build the transaction.
  144. // We also do this through the RPC.
  145. let params = JsonValue::Array(vec![JsonValue::String(contract_id.to_string())]);
  146. // Execute the request
  147. let req = JsonRequest::new("blockchain.lookup_zkas", params);
  148. let rep = rpc_client.request(req).await?;
  149. let params = rep.get::<Vec<JsonValue>>().unwrap();
  150. // Parse response
  151. let mut zkas_bins = Vec::with_capacity(params.len());
  152. for param in params {
  153. let zkas_ns = param[0].get::<String>().unwrap().clone();
  154. let zkas_bincode_bytes =
  155. base64::decode(param[1].get::<String>().unwrap()).unwrap();
  156. zkas_bins.push((zkas_ns, zkas_bincode_bytes));
  157. }
  158. let Some(commitment_zkbin) =
  159. zkas_bins.iter().find(|x| x.0 == HELLO_CONTRACT_ZKAS_SECRETCOMMIT_NS)
  160. else {
  161. return Err(Error::Custom("Secret commitment circuit not found".to_string()))
  162. };
  163. let commitment_zkbin = ZkBinary::decode(&commitment_zkbin.1)?;
  164. let commitment_circuit =
  165. ZkCircuit::new(empty_witnesses(&commitment_zkbin)?, &commitment_zkbin);
  166. // Creating secret commitment circuit proving keys
  167. let commitment_pk = ProvingKey::build(commitment_zkbin.k, &commitment_circuit);
  168. // Create the contract call
  169. let builder = ContractCallBuilder { member, commitment_zkbin, commitment_pk };
  170. let debris = builder.build()?;
  171. // Encode the call
  172. let mut data = vec![ContractFunction::Register as u8];
  173. debris.params.encode(&mut data)?;
  174. let call = ContractCall { contract_id, data };
  175. // Create the TransactionBuilder containing above call
  176. let mut tx_builder = TransactionBuilder::new(
  177. ContractCallLeaf { call, proofs: debris.proofs },
  178. vec![],
  179. )?;
  180. // Build the transaction and attach the corresponding signatures
  181. let mut tx = tx_builder.build()?;
  182. let sigs = tx.create_sigs(&[])?;
  183. tx.signatures.push(sigs);
  184. println!("{}", base64::encode(&serialize(&tx)));
  185. }
  186. Subcmd::Deregister { member_secret } => {
  187. // Parse the member secret key
  188. let member_secret = SecretKey::from_str(&member_secret)?;
  189. let member = Keypair::new(member_secret);
  190. // Now we need to do a lookup for the zkas proof bincodes, and create
  191. // the circuit objects and proving keys so we can build the transaction.
  192. // We also do this through the RPC.
  193. let params = JsonValue::Array(vec![JsonValue::String(contract_id.to_string())]);
  194. // Execute the request
  195. let req = JsonRequest::new("blockchain.lookup_zkas", params);
  196. let rep = rpc_client.request(req).await?;
  197. let params = rep.get::<Vec<JsonValue>>().unwrap();
  198. // Parse response
  199. let mut zkas_bins = Vec::with_capacity(params.len());
  200. for param in params {
  201. let zkas_ns = param[0].get::<String>().unwrap().clone();
  202. let zkas_bincode_bytes =
  203. base64::decode(param[1].get::<String>().unwrap()).unwrap();
  204. zkas_bins.push((zkas_ns, zkas_bincode_bytes));
  205. }
  206. let Some(commitment_zkbin) =
  207. zkas_bins.iter().find(|x| x.0 == HELLO_CONTRACT_ZKAS_SECRETCOMMIT_NS)
  208. else {
  209. return Err(Error::Custom("Secret commitment circuit not found".to_string()))
  210. };
  211. let commitment_zkbin = ZkBinary::decode(&commitment_zkbin.1)?;
  212. let commitment_circuit =
  213. ZkCircuit::new(empty_witnesses(&commitment_zkbin)?, &commitment_zkbin);
  214. // Creating secret commitment circuit proving keys
  215. let commitment_pk = ProvingKey::build(commitment_zkbin.k, &commitment_circuit);
  216. // Create the contract call
  217. let builder = ContractCallBuilder { member, commitment_zkbin, commitment_pk };
  218. let debris = builder.build()?;
  219. // Encode the call
  220. let mut data = vec![ContractFunction::Deregister as u8];
  221. debris.params.encode(&mut data)?;
  222. let call = ContractCall { contract_id, data };
  223. // Create the TransactionBuilder containing above call
  224. let mut tx_builder = TransactionBuilder::new(
  225. ContractCallLeaf { call, proofs: debris.proofs },
  226. vec![],
  227. )?;
  228. // Build the transaction and attach the corresponding signatures
  229. let mut tx = tx_builder.build()?;
  230. let sigs = tx.create_sigs(&[])?;
  231. tx.signatures.push(sigs);
  232. println!("{}", base64::encode(&serialize(&tx)));
  233. }
  234. }
  235. // Stop the rpc client
  236. rpc_client.stop().await;
  237. Ok(())
  238. }))
  239. }