main.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2022 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. io::{stdin, Read},
  20. process::exit,
  21. };
  22. use clap::{Parser, Subcommand};
  23. use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
  24. use halo2_proofs::{arithmetic::Field, pasta::group::ff::PrimeField};
  25. use rand::rngs::OsRng;
  26. use url::Url;
  27. use darkfi::{
  28. cli_desc,
  29. crypto::{
  30. burn_proof::{create_burn_proof, verify_burn_proof},
  31. keypair::{PublicKey, SecretKey},
  32. mint_proof::{create_mint_proof, verify_mint_proof},
  33. note::{EncryptedNote, Note},
  34. proof::{ProvingKey, VerifyingKey},
  35. schnorr,
  36. schnorr::SchnorrSecret,
  37. token_id,
  38. types::{
  39. DrkCoinBlind, DrkSerial, DrkSpendHook, DrkTokenId, DrkUserData, DrkUserDataBlind,
  40. DrkValueBlind,
  41. },
  42. util::{pedersen_commitment_base, pedersen_commitment_u64},
  43. BurnRevealedValues, MintRevealedValues, Proof,
  44. },
  45. rpc::client::RpcClient,
  46. tx::{
  47. partial::{PartialTransaction, PartialTransactionInput},
  48. Transaction, TransactionInput, TransactionOutput,
  49. },
  50. util::{
  51. cli::{fg_green, fg_red, progress_bar},
  52. parse::encode_base10,
  53. },
  54. zk::circuit::{BurnContract, MintContract},
  55. Result,
  56. };
  57. mod cli_util;
  58. use cli_util::{parse_token_pair, parse_value_pair};
  59. mod rpc;
  60. use rpc::Rpc;
  61. #[derive(Parser)]
  62. #[clap(name = "darkotc", about = cli_desc!(), version)]
  63. #[clap(arg_required_else_help(true))]
  64. struct Args {
  65. #[clap(short, parse(from_occurrences))]
  66. /// Increase verbosity (-vvv supported)
  67. verbose: u8,
  68. #[clap(short, long, default_value = "tcp://127.0.0.1:8340")]
  69. /// darkfid JSON-RPC endpoint
  70. endpoint: Url,
  71. #[clap(subcommand)]
  72. command: Subcmd,
  73. }
  74. #[derive(Subcommand)]
  75. enum Subcmd {
  76. /// Initialize an atomic swap
  77. Init {
  78. #[clap(short, long)]
  79. /// Pair of token IDs to swap: token_to_send:token_to_recv
  80. token_pair: String,
  81. #[clap(short, long)]
  82. /// Pair of values to swap: value_to_send:value_to_recv
  83. value_pair: String,
  84. },
  85. /// Inspect partial swap data from stdin.
  86. InspectPartial,
  87. /// Join two partial swap data files and build a tx
  88. Join { data0: String, data1: String },
  89. /// Sign a transaction given from stdin.
  90. SignTx,
  91. }
  92. #[derive(SerialEncodable, SerialDecodable)]
  93. /// Half of the swap data, includes the coin that is supposed to be received,
  94. /// and the coin that is supposed to be sent.
  95. struct PartialSwapData {
  96. /// Mint proof of coin to be received
  97. mint_proof: Proof,
  98. /// Public values for the mint proof
  99. mint_revealed: MintRevealedValues,
  100. /// Value of the coin to be received
  101. mint_value: u64,
  102. /// Token ID of the coin to be received
  103. mint_token: DrkTokenId,
  104. /// Blinding factor for the minted value pedersen commitment
  105. mint_value_blind: DrkValueBlind,
  106. /// Blinding factor for the minted token ID pedersen commitment
  107. mint_token_blind: DrkValueBlind,
  108. /// Burn proof of the coin to be sent
  109. burn_proof: Proof,
  110. /// Public values for the burn proof
  111. burn_revealed: BurnRevealedValues,
  112. /// Value of the coin to be sent
  113. burn_value: u64,
  114. /// Token ID of the coin to be sent
  115. burn_token: DrkTokenId,
  116. /// Blinding factor for the burned value pedersen commitment
  117. burn_value_blind: DrkValueBlind,
  118. /// Blinding factor for the burned token ID pedersen commitment
  119. burn_token_blind: DrkValueBlind,
  120. /// Encrypted note
  121. encrypted_note: EncryptedNote,
  122. }
  123. #[derive(SerialEncodable, SerialDecodable)]
  124. /// Full swap data, containing two instances of `PartialSwapData`, which
  125. /// represent an atomic swap.
  126. struct SwapData {
  127. swap0: PartialSwapData,
  128. swap1: PartialSwapData,
  129. }
  130. async fn init_swap(
  131. endpoint: Url,
  132. token_pair: (String, String),
  133. value_pair: (u64, u64),
  134. ) -> Result<PartialSwapData> {
  135. let rpc_client = match RpcClient::new(endpoint).await {
  136. Ok(v) => v,
  137. Err(e) => {
  138. eprintln!("Error: Failed connecting to darkfid JSON-RPC endpoint.");
  139. return Err(e)
  140. }
  141. };
  142. let rpc = Rpc { rpc_client };
  143. // TODO: Implement metadata for decimals, don't hardcode.
  144. let tp = (token_id::parse_b58(&token_pair.0)?, token_id::parse_b58(&token_pair.1)?);
  145. let vp = value_pair;
  146. // Connect to darkfid and see if there's available funds.
  147. let balance = rpc.balance_of(&token_pair.0).await?;
  148. if balance < vp.0 {
  149. eprintln!(
  150. "Error: There's not enough balance for token \"{}\" in your wallet.",
  151. token_pair.0
  152. );
  153. eprintln!("Available balance is {} ({})", encode_base10(balance, 8), balance);
  154. exit(1);
  155. }
  156. // If there's not enough funds in a single coin, mint a single new coin
  157. // with the funds. We do this to minimize the size of the swap transaction.
  158. // i.e. 2 inputs and 2 outputs.
  159. // TODO: Implement ^
  160. // TODO: Maybe this should be done by the user beforehand?
  161. // Find a coin to spend. We can find multiple, but we'll pick the first one.
  162. let coins = rpc.get_coins_valtok(vp.0, &token_pair.0).await?;
  163. if coins.is_empty() {
  164. eprintln!("Error: Did not manage to find a coin with enough value to spend.");
  165. exit(1);
  166. }
  167. // Fetch our default address
  168. let our_addr = rpc.wallet_address().await?;
  169. let our_pubk = match PublicKey::try_from(our_addr) {
  170. Ok(v) => v,
  171. Err(e) => {
  172. eprintln!("Error converting our address into PublicKey: {}", e);
  173. exit(1);
  174. }
  175. };
  176. // Build ZK proving keys
  177. let pb = progress_bar("Building proving key for the Mint contract");
  178. let mint_pk = ProvingKey::build(11, &MintContract::default());
  179. pb.finish();
  180. let pb = progress_bar("Building proving key for the Burn contract");
  181. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  182. pb.finish();
  183. // The coin we want to receive
  184. let recv_value_blind = DrkValueBlind::random(&mut OsRng);
  185. let recv_token_blind = DrkValueBlind::random(&mut OsRng);
  186. let recv_coin_blind = DrkCoinBlind::random(&mut OsRng);
  187. let recv_serial = DrkSerial::random(&mut OsRng);
  188. // Spend hook and user data disabled
  189. let spend_hook = DrkSpendHook::from(0);
  190. let user_data = DrkUserData::from(0);
  191. let pb = progress_bar("Building Mint proof for the receiving coin");
  192. let (mint_proof, mint_revealed) = create_mint_proof(
  193. &mint_pk,
  194. vp.1,
  195. tp.1,
  196. recv_value_blind,
  197. recv_token_blind,
  198. recv_serial,
  199. spend_hook,
  200. user_data,
  201. recv_coin_blind,
  202. our_pubk,
  203. )?;
  204. pb.finish();
  205. // The coin we are spending.
  206. let coin = coins[0].clone();
  207. let pb = progress_bar("Building Burn proof for the spending coin");
  208. let signature_secret = SecretKey::random(&mut OsRng);
  209. let merkle_path = match rpc.get_merkle_path(usize::from(coin.leaf_position)).await {
  210. Ok(v) => v,
  211. Err(e) => {
  212. eprintln!("Failed to get Merkle path for our coin from darkfid RPC: {}", e);
  213. exit(1);
  214. }
  215. };
  216. // Spend hook and user data disabled
  217. let spend_hook = DrkSpendHook::from(0);
  218. let user_data = DrkUserData::from(0);
  219. let user_data_blind = DrkUserDataBlind::random(&mut OsRng);
  220. let (burn_proof, burn_revealed) = create_burn_proof(
  221. &burn_pk,
  222. vp.0,
  223. tp.0,
  224. coin.note.value_blind,
  225. coin.note.token_blind,
  226. coin.note.serial,
  227. spend_hook,
  228. user_data,
  229. user_data_blind,
  230. coin.note.coin_blind,
  231. coin.secret,
  232. coin.leaf_position,
  233. merkle_path,
  234. signature_secret,
  235. )?;
  236. pb.finish();
  237. // Create encrypted note
  238. let note = Note {
  239. serial: recv_serial,
  240. value: vp.1,
  241. token_id: tp.1,
  242. coin_blind: recv_coin_blind,
  243. value_blind: recv_value_blind,
  244. token_blind: recv_token_blind,
  245. // Here we store our secret key we used for signing
  246. memo: signature_secret.to_bytes().to_vec(),
  247. };
  248. let encrypted_note = note.encrypt(&our_pubk)?;
  249. // Pack proofs together with pedersen commitment openings so
  250. // counterparty can verify correctness.
  251. let partial_swap_data = PartialSwapData {
  252. mint_proof,
  253. mint_revealed,
  254. mint_value: vp.1,
  255. mint_token: tp.1,
  256. mint_value_blind: recv_value_blind,
  257. mint_token_blind: recv_token_blind,
  258. burn_proof,
  259. burn_value: vp.0,
  260. burn_token: tp.0,
  261. burn_revealed,
  262. burn_value_blind: coin.note.value_blind,
  263. burn_token_blind: coin.note.token_blind,
  264. encrypted_note,
  265. };
  266. Ok(partial_swap_data)
  267. }
  268. fn inspect_partial(data: &str) -> Result<()> {
  269. let bytes = match bs58::decode(data).into_vec() {
  270. Ok(v) => v,
  271. Err(e) => {
  272. eprintln!("Error decoding base58 data from input: {}", e);
  273. exit(1);
  274. }
  275. };
  276. let sd: PartialSwapData = match deserialize(&bytes) {
  277. Ok(v) => v,
  278. Err(e) => {
  279. eprintln!("Error deserializing partial swap data into struct: {}", e);
  280. exit(1);
  281. }
  282. };
  283. eprintln!("Successfully decoded partial swap data");
  284. // Build ZK verifying keys
  285. let pb = progress_bar("Building verifying key for the Mint contract");
  286. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  287. pb.finish();
  288. let pb = progress_bar("Building verifying key for the Burn contract");
  289. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  290. pb.finish();
  291. let pb = progress_bar("Verifying Burn proof");
  292. let burn_valid = verify_burn_proof(&burn_vk, &sd.burn_proof, &sd.burn_revealed).is_ok();
  293. pb.finish();
  294. let pb = progress_bar("Verifying Mint proof");
  295. let mint_valid = verify_mint_proof(&mint_vk, &sd.mint_proof, &sd.mint_revealed).is_ok();
  296. pb.finish();
  297. eprintln!(" Verifying Pedersen commitments");
  298. let burn_value_valid = pedersen_commitment_u64(sd.burn_value, sd.burn_value_blind) ==
  299. sd.burn_revealed.value_commit;
  300. let burn_token_valid = pedersen_commitment_base(sd.burn_token, sd.burn_token_blind) ==
  301. sd.burn_revealed.token_commit;
  302. let mint_value_valid = pedersen_commitment_u64(sd.mint_value, sd.mint_value_blind) ==
  303. sd.mint_revealed.value_commit;
  304. let mint_token_valid = pedersen_commitment_base(sd.mint_token, sd.mint_token_blind) ==
  305. sd.mint_revealed.token_commit;
  306. let mut valid = true;
  307. eprintln!("Summary:");
  308. eprint!(" Burn proof: ");
  309. if burn_valid {
  310. eprintln!("{}", fg_green("VALID"));
  311. } else {
  312. eprintln!("{}", fg_red("INVALID"));
  313. valid = false;
  314. }
  315. eprint!(" Burn proof value commitment: ");
  316. if burn_value_valid {
  317. eprintln!("{}", fg_green("VALID"));
  318. } else {
  319. eprintln!("{}", fg_red("INVALID"));
  320. valid = false;
  321. }
  322. eprint!(" Burn proof token commitment: ");
  323. if burn_token_valid {
  324. eprintln!("{}", fg_green("VALID"));
  325. } else {
  326. eprintln!("{}", fg_red("INVALID"));
  327. valid = false;
  328. }
  329. eprint!(" Mint proof: ");
  330. if mint_valid {
  331. eprintln!("{}", fg_green("VALID"));
  332. } else {
  333. eprintln!("{}", fg_red("INVALID"));
  334. valid = false;
  335. }
  336. eprint!(" Mint proof value commitment: ");
  337. if mint_value_valid {
  338. eprintln!("{}", fg_green("VALID"));
  339. } else {
  340. eprintln!("{}", fg_red("INVALID"));
  341. valid = false;
  342. }
  343. eprint!(" Mint proof token commitment: ");
  344. if mint_token_valid {
  345. eprintln!("{}", fg_green("VALID"));
  346. } else {
  347. eprintln!("{}", fg_red("INVALID"));
  348. valid = false;
  349. }
  350. eprintln!("========================================");
  351. eprintln!(
  352. "Mint: {} {}",
  353. encode_base10(sd.mint_value, 8),
  354. bs58::encode(sd.mint_token.to_repr()).into_string()
  355. );
  356. eprintln!(
  357. "Burn: {} {}",
  358. encode_base10(sd.burn_value, 8),
  359. bs58::encode(sd.burn_token.to_repr()).into_string()
  360. );
  361. eprint!("\nThe ZK proofs and commitments inspected are ");
  362. if !valid {
  363. println!("{}", fg_red("NOT VALID"));
  364. exit(1);
  365. } else {
  366. eprintln!("{}", fg_green("VALID"));
  367. }
  368. Ok(())
  369. }
  370. async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result<Transaction> {
  371. eprintln!("Joining data into a transaction");
  372. let input0 = PartialTransactionInput { burn_proof: d0.burn_proof, revealed: d0.burn_revealed };
  373. let input1 = PartialTransactionInput { burn_proof: d1.burn_proof, revealed: d1.burn_revealed };
  374. let inputs = vec![input0, input1];
  375. let output0 = TransactionOutput {
  376. mint_proof: d0.mint_proof,
  377. revealed: d0.mint_revealed,
  378. enc_note: d0.encrypted_note.clone(),
  379. };
  380. let output1 = TransactionOutput {
  381. mint_proof: d1.mint_proof,
  382. revealed: d1.mint_revealed,
  383. enc_note: d1.encrypted_note.clone(),
  384. };
  385. let outputs = vec![output0, output1];
  386. let partial_tx = PartialTransaction { clear_inputs: vec![], inputs, outputs };
  387. let unsigned_tx_data = serialize(&partial_tx);
  388. let mut inputs = vec![];
  389. let mut signed: bool;
  390. eprint!("Trying to decrypt the note of the first half... ");
  391. let rpc_client = RpcClient::new(endpoint.clone()).await?;
  392. let rpc = Rpc { rpc_client };
  393. let note = match rpc.decrypt_note(&d0.encrypted_note).await {
  394. Ok(v) => v,
  395. Err(_) => None,
  396. };
  397. if let Some(note) = note {
  398. eprintln!("{}", fg_green("Success"));
  399. let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
  400. let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
  401. inputs.push(input);
  402. signed = true;
  403. } else {
  404. eprintln!("{}", fg_red("Failure"));
  405. let signature = schnorr::Signature::dummy();
  406. let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
  407. inputs.push(input);
  408. signed = false;
  409. }
  410. // If we have signed, we shouldn't have to look in the other one, but we might
  411. // be sending to ourself for some reason.
  412. eprint!("Trying to decrypt the note of the second half... ");
  413. let rpc_client = RpcClient::new(endpoint).await?;
  414. let rpc = Rpc { rpc_client };
  415. let note = match rpc.decrypt_note(&d1.encrypted_note).await {
  416. Ok(v) => v,
  417. Err(_) => None,
  418. };
  419. if let Some(note) = note {
  420. eprintln!("{}", fg_green("Success"));
  421. let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
  422. let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
  423. inputs.push(input);
  424. signed = true;
  425. } else {
  426. eprintln!("{}", fg_red("Failure"));
  427. let signature = schnorr::Signature::dummy();
  428. let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
  429. inputs.push(input);
  430. if !signed {
  431. eprintln!("Error: Failed to sign transaction!");
  432. exit(1);
  433. }
  434. }
  435. if !signed {
  436. eprintln!("Error: Failed to sign transaction!");
  437. exit(1);
  438. }
  439. let tx = Transaction { clear_inputs: vec![], inputs, outputs: partial_tx.outputs };
  440. Ok(tx)
  441. }
  442. async fn sign_tx(endpoint: Url, data: &str) -> Result<Transaction> {
  443. eprintln!("Trying to sign transaction");
  444. let mut tx: Transaction = deserialize(&bs58::decode(data).into_vec()?)?;
  445. // We assume our input and our output are in the same index, since this
  446. // transaction contains 2 inputs and 2 outputs, and one of each is ours,
  447. // and one of each is the other party's. So we go on and sign the input
  448. // index of the output index we can decrypt the note for.
  449. let mut idx_to_sign = 0;
  450. let mut signature = schnorr::Signature::dummy();
  451. eprintln!("Looking for an encrypted note we can decrypt...");
  452. let mut found_secret = false;
  453. for (i, output) in tx.outputs.iter().enumerate() {
  454. // TODO: FIXME: Consider not closing the RPC on failure.
  455. let rpc = Rpc { rpc_client: RpcClient::new(endpoint.clone()).await? };
  456. let note = match rpc.decrypt_note(&output.enc_note).await {
  457. Ok(v) => v,
  458. Err(_) => continue,
  459. };
  460. if let Some(note) = note {
  461. eprintln!("Successfully decrypted note in output {}", i);
  462. eprintln!("Creating signature...");
  463. let mut unsigned_tx_data = vec![];
  464. let _ = tx.encode_without_signature(&mut unsigned_tx_data)?;
  465. signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
  466. found_secret = true;
  467. idx_to_sign = i;
  468. break
  469. }
  470. eprintln!("Failed to find a note to decrypt. Signing failed.");
  471. exit(1);
  472. }
  473. if !found_secret {
  474. eprintln!("Error: Did not manage to sign transaction. Couldn't find any secret keys.");
  475. exit(1);
  476. }
  477. tx.inputs[idx_to_sign].signature = signature.clone();
  478. Ok(tx)
  479. }
  480. fn try_sign_tx(note: &Note, tx_data: &[u8]) -> Result<schnorr::Signature> {
  481. if note.memo.len() != 32 {
  482. eprintln!("Error: The note memo is not 32 bytes");
  483. exit(1);
  484. }
  485. let secret = match SecretKey::from_bytes(note.memo.clone().try_into().unwrap()) {
  486. Ok(v) => v,
  487. Err(e) => {
  488. eprintln!("Did not manage to cast bytes into SecretKey: {}", e);
  489. exit(1);
  490. }
  491. };
  492. eprintln!("Signing transaction...");
  493. let signature = secret.sign(tx_data);
  494. Ok(signature)
  495. }
  496. #[async_std::main]
  497. async fn main() -> Result<()> {
  498. let args = Args::parse();
  499. match args.command {
  500. Subcmd::Init { token_pair, value_pair } => {
  501. let token_pair = parse_token_pair(&token_pair)?;
  502. let value_pair = parse_value_pair(&value_pair)?;
  503. eprintln!("Creating half of an atomic swap");
  504. eprintln!("Send: {} {} tokens.", encode_base10(value_pair.0, 8), token_pair.0);
  505. eprintln!("Recv: {} {} tokens.", encode_base10(value_pair.1, 8), token_pair.1);
  506. let swap_data = init_swap(args.endpoint, token_pair, value_pair).await?;
  507. println!("{}", bs58::encode(serialize(&swap_data)).into_string());
  508. Ok(())
  509. }
  510. Subcmd::InspectPartial => {
  511. let mut buf = String::new();
  512. stdin().read_to_string(&mut buf)?;
  513. inspect_partial(buf.trim())
  514. }
  515. Subcmd::Join { data0, data1 } => {
  516. let d0 = std::fs::read_to_string(data0)?;
  517. let d1 = std::fs::read_to_string(data1)?;
  518. let d0 = deserialize(&bs58::decode(&d0.trim()).into_vec()?)?;
  519. let d1 = deserialize(&bs58::decode(&d1.trim()).into_vec()?)?;
  520. let tx = join(args.endpoint, d0, d1).await?;
  521. println!("{}", bs58::encode(&serialize(&tx)).into_string());
  522. eprintln!("Successfully signed transaction");
  523. Ok(())
  524. }
  525. Subcmd::SignTx => {
  526. let mut buf = String::new();
  527. stdin().read_to_string(&mut buf)?;
  528. let tx = sign_tx(args.endpoint, buf.trim()).await?;
  529. println!("{}", bs58::encode(&serialize(&tx)).into_string());
  530. eprintln!("Successfully signed transaction");
  531. Ok(())
  532. }
  533. }
  534. }