main.rs 19 KB

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