main.rs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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::{
  19. fs::{read_dir, read_to_string},
  20. io::{stdin, Cursor, Read},
  21. process::exit,
  22. str::FromStr,
  23. };
  24. use clap::{Parser, Subcommand};
  25. use darkfi::{
  26. blockchain::{BlockInfo, Blockchain, BlockchainOverlay},
  27. cli_desc,
  28. tx::{ContractCallLeaf, Transaction, TransactionBuilder},
  29. util::{encoding::base64, parse::decode_base10, path::expand_path, time::Timestamp},
  30. validator::{utils::deploy_native_contracts, verification::verify_genesis_block},
  31. zk::{empty_witnesses, ProvingKey, ZkCircuit},
  32. zkas::ZkBinary,
  33. Result,
  34. };
  35. use darkfi_contract_test_harness::vks;
  36. use darkfi_money_contract::{
  37. client::genesis_mint_v1::GenesisMintCallBuilder, MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
  38. };
  39. use darkfi_sdk::{
  40. crypto::{contract_id::MONEY_CONTRACT_ID, FuncId, PublicKey, SecretKey},
  41. pasta::{group::ff::PrimeField, pallas},
  42. ContractCall,
  43. };
  44. use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
  45. #[derive(Parser)]
  46. #[command(about = cli_desc!())]
  47. struct Args {
  48. #[command(subcommand)]
  49. command: Subcmd,
  50. }
  51. #[derive(Subcommand)]
  52. enum Subcmd {
  53. /// Read a Darkfi genesis block from stdin and display it
  54. Display,
  55. /// Generate a Darkfi genesis block and write it to stdin
  56. Generate {
  57. #[arg(short, long, default_value = "genesis_txs")]
  58. /// Path to folder containing the genesis transactions
  59. txs_folder: String,
  60. #[arg(short, long)]
  61. /// Genesis timestamp to use, instead of current one
  62. genesis_timestamp: Option<u64>,
  63. },
  64. /// Read a Darkfi genesis block from stdin and verify it
  65. Verify,
  66. /// Generate a Darkfi genesis transaction using the secret
  67. /// key from stdin
  68. GenerateTx {
  69. /// Amount to mint for this genesis transaction
  70. amount: String,
  71. #[arg(short, long)]
  72. /// Optional recipient's public key, in case we want to mint to a different address
  73. recipient: Option<String>,
  74. #[arg(short, long)]
  75. /// Optional contract spend hook to use
  76. spend_hook: Option<String>,
  77. #[arg(short, long)]
  78. /// Optional user data to use
  79. user_data: Option<String>,
  80. },
  81. }
  82. /// Auxiliary function to read a bs58 genesis block from stdin
  83. async fn read_block() -> Result<BlockInfo> {
  84. println!("Reading genesis block from stdin...");
  85. let mut buf = String::new();
  86. stdin().read_to_string(&mut buf)?;
  87. let bytes = base64::decode(buf.trim()).unwrap();
  88. let block = deserialize_async(&bytes).await?;
  89. Ok(block)
  90. }
  91. #[async_std::main]
  92. async fn main() -> Result<()> {
  93. // Parse arguments
  94. let args = Args::parse();
  95. // Execute a subcommand
  96. match args.command {
  97. Subcmd::Display => {
  98. let genesis_block = read_block().await;
  99. // TODO: display in more details
  100. println!("{genesis_block:?}");
  101. }
  102. Subcmd::Generate { txs_folder, genesis_timestamp } => {
  103. // Grab genesis transactions from folder
  104. let txs_folder = expand_path(&txs_folder).unwrap();
  105. let mut genesis_txs: Vec<Transaction> = vec![];
  106. for file in read_dir(txs_folder)? {
  107. let file = file?;
  108. let bytes = base64::decode(read_to_string(file.path())?.trim()).unwrap();
  109. let tx = deserialize_async(&bytes).await?;
  110. genesis_txs.push(tx);
  111. }
  112. // Generate the genesis block
  113. let mut genesis_block = BlockInfo::default();
  114. // Update timestamp if one was provided
  115. if let Some(timestamp) = genesis_timestamp {
  116. genesis_block.header.timestamp = Timestamp::from_u64(timestamp);
  117. }
  118. // Retrieve genesis producer transaction
  119. let producer_tx = genesis_block.txs.pop().unwrap();
  120. // Append genesis transactions
  121. if !genesis_txs.is_empty() {
  122. genesis_block.append_txs(genesis_txs);
  123. }
  124. genesis_block.append_txs(vec![producer_tx]);
  125. // Write generated genesis block to stdin
  126. let encoded = base64::encode(&serialize_async(&genesis_block).await);
  127. println!("{encoded}");
  128. }
  129. Subcmd::Verify => {
  130. let genesis_block = read_block().await?;
  131. let hash = genesis_block.hash();
  132. println!("Verifying genesis block: {hash}");
  133. // Initialize a temporary sled database
  134. let sled_db = sled::Config::new().temporary(true).open()?;
  135. let (_, vks) = vks::get_cached_pks_and_vks()?;
  136. vks::inject(&sled_db, &vks)?;
  137. // Create an overlay over whole blockchain
  138. let blockchain = Blockchain::new(&sled_db)?;
  139. let overlay = BlockchainOverlay::new(&blockchain)?;
  140. deploy_native_contracts(&overlay, 0).await?;
  141. verify_genesis_block(&overlay, &genesis_block, 0).await?;
  142. println!("Genesis block {hash} verified successfully!");
  143. }
  144. Subcmd::GenerateTx { amount, recipient, spend_hook, user_data } => {
  145. let mut buf = String::new();
  146. stdin().read_to_string(&mut buf)?;
  147. let signature_secret = SecretKey::from_str(buf.trim())?;
  148. if let Err(e) = f64::from_str(&amount) {
  149. eprintln!("Invalid amount: {e:?}");
  150. exit(2);
  151. }
  152. let amount = decode_base10(&amount, 8, true)?;
  153. let recipient = match recipient {
  154. Some(r) => match PublicKey::from_str(&r) {
  155. Ok(r) => Some(r),
  156. Err(e) => {
  157. eprintln!("Invalid recipient: {e:?}");
  158. exit(2);
  159. }
  160. },
  161. None => None,
  162. };
  163. let spend_hook = match spend_hook {
  164. Some(s) => match FuncId::from_str(&s) {
  165. Ok(s) => Some(s),
  166. Err(e) => {
  167. eprintln!("Invalid spend hook: {e:?}");
  168. exit(2);
  169. }
  170. },
  171. None => None,
  172. };
  173. let user_data = match user_data {
  174. Some(u) => {
  175. let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
  176. Ok(b) => b,
  177. Err(e) => {
  178. eprintln!("Invalid user data: {e:?}");
  179. exit(2);
  180. }
  181. };
  182. match pallas::Base::from_repr(bytes).into() {
  183. Some(v) => Some(v),
  184. None => {
  185. eprintln!("Invalid user data");
  186. exit(2);
  187. }
  188. }
  189. }
  190. None => None,
  191. };
  192. // Grab mint proving keys and zkbin
  193. let (pks, _) = vks::get_cached_pks_and_vks()?;
  194. let mut mint = None;
  195. for (bincode, namespace, pk) in pks {
  196. if namespace.as_str() != MONEY_CONTRACT_ZKAS_MINT_NS_V1 {
  197. continue
  198. }
  199. let mut reader = Cursor::new(pk);
  200. let zkbin = ZkBinary::decode(&bincode)?;
  201. let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
  202. let proving_key = ProvingKey::read(&mut reader, circuit)?;
  203. mint = Some((proving_key, zkbin));
  204. }
  205. let Some((mint_pk, mint_zkbin)) = mint else {
  206. eprintln!("Mint proving keys not found.");
  207. exit(2);
  208. };
  209. // Build the contract call
  210. let builder = GenesisMintCallBuilder {
  211. signature_public: PublicKey::from_secret(signature_secret),
  212. amount,
  213. recipient,
  214. spend_hook,
  215. user_data,
  216. mint_zkbin,
  217. mint_pk,
  218. };
  219. let debris = builder.build()?;
  220. // Encode and build the transaction
  221. let mut data = vec![MoneyFunction::GenesisMintV1 as u8];
  222. debris.params.encode_async(&mut data).await?;
  223. let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
  224. let mut tx_builder =
  225. TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
  226. let mut tx = tx_builder.build()?;
  227. let sigs = tx.create_sigs(&[signature_secret])?;
  228. tx.signatures = vec![sigs];
  229. println!("{}", base64::encode(&serialize_async(&tx).await));
  230. }
  231. }
  232. Ok(())
  233. }