main.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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. tx::{
  29. partial::{PartialTransaction, PartialTransactionInput},
  30. Transaction, TransactionInput, TransactionOutput,
  31. },
  32. util::{
  33. cli::{fg_green, fg_red, progress_bar},
  34. encode_base10,
  35. serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable},
  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 = RpcClient::new(endpoint).await?;
  119. let rpc = Rpc { rpc_client };
  120. // TODO: Implement metadata for decimals, don't hardcode.
  121. let tp = (token_id::parse_b58(&token_pair.0)?, token_id::parse_b58(&token_pair.1)?);
  122. let vp = value_pair;
  123. // Connect to darkfid and see if there's available funds.
  124. let balance = rpc.balance_of(&token_pair.0).await?;
  125. if balance < vp.0 {
  126. eprintln!(
  127. "Error: There's not enough balance for token \"{}\" in your wallet.",
  128. token_pair.0
  129. );
  130. eprintln!("Available balance is {} ({})", encode_base10(balance, 8), balance);
  131. exit(1);
  132. }
  133. // If there's not enough funds in a single coin, mint a single new coin
  134. // with the funds. We do this to minimize the size of the swap transaction.
  135. // i.e. 2 inputs and 2 outputs.
  136. // TODO: Implement ^
  137. // TODO: Maybe this should be done by the user beforehand?
  138. // Find a coin to spend. We can find multiple, but we'll pick the first one.
  139. let coins = rpc.get_coins_valtok(vp.0, &token_pair.0).await?;
  140. if coins.is_empty() {
  141. eprintln!("Error: Did not manage to find a coin with enough value to spend.");
  142. exit(1);
  143. }
  144. eprintln!("Initializing swap data for:");
  145. eprintln!("Send: {} {} tokens", encode_base10(vp.0, 8), token_pair.0);
  146. eprintln!("Recv: {} {} tokens", encode_base10(vp.1, 8), token_pair.1);
  147. // Fetch our default address
  148. let our_addr = rpc.wallet_address().await?;
  149. let our_pubk = match PublicKey::try_from(our_addr) {
  150. Ok(v) => v,
  151. Err(e) => {
  152. eprintln!("Error converting our address into PublicKey: {}", e);
  153. exit(1);
  154. }
  155. };
  156. // Build ZK proving keys
  157. let pb = progress_bar("Building proving key for the Mint contract");
  158. let mint_pk = ProvingKey::build(11, &MintContract::default());
  159. pb.finish();
  160. let pb = progress_bar("Building proving key for the Burn contract");
  161. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  162. pb.finish();
  163. // The coin we want to receive
  164. let recv_value_blind = DrkValueBlind::random(&mut OsRng);
  165. let recv_token_blind = DrkValueBlind::random(&mut OsRng);
  166. let recv_coin_blind = DrkCoinBlind::random(&mut OsRng);
  167. let recv_serial = DrkSerial::random(&mut OsRng);
  168. // Spend hook and user data disabled
  169. let spend_hook = DrkSpendHook::from(0);
  170. let user_data = DrkUserData::from(0);
  171. let pb = progress_bar("Building Mint proof for the receiving coin");
  172. let (mint_proof, mint_revealed) = create_mint_proof(
  173. &mint_pk,
  174. vp.1,
  175. tp.1,
  176. recv_value_blind,
  177. recv_token_blind,
  178. recv_serial,
  179. spend_hook,
  180. user_data,
  181. recv_coin_blind,
  182. our_pubk,
  183. )?;
  184. pb.finish();
  185. // The coin we are spending.
  186. let coin = coins[0].clone();
  187. let pb = progress_bar("Building Burn proof for the spending coin");
  188. let signature_secret = SecretKey::random(&mut OsRng);
  189. let merkle_path = match rpc.get_merkle_path(usize::from(coin.leaf_position)).await {
  190. Ok(v) => v,
  191. Err(e) => {
  192. eprintln!("Failed to get Merkle path for our coin from darkfid RPC: {}", e);
  193. exit(1);
  194. }
  195. };
  196. // Spend hook and user data disabled
  197. let spend_hook = DrkSpendHook::from(0);
  198. let user_data = DrkUserData::from(0);
  199. let user_data_blind = DrkUserDataBlind::random(&mut OsRng);
  200. let (burn_proof, burn_revealed) = create_burn_proof(
  201. &burn_pk,
  202. vp.0,
  203. tp.0,
  204. coin.note.value_blind,
  205. coin.note.token_blind,
  206. coin.note.serial,
  207. spend_hook,
  208. user_data,
  209. user_data_blind,
  210. coin.note.coin_blind,
  211. coin.secret,
  212. coin.leaf_position,
  213. merkle_path,
  214. signature_secret,
  215. )?;
  216. pb.finish();
  217. // Create encrypted note
  218. let note = Note {
  219. serial: recv_serial,
  220. value: vp.1,
  221. token_id: tp.1,
  222. coin_blind: recv_coin_blind,
  223. value_blind: recv_value_blind,
  224. token_blind: recv_token_blind,
  225. // Here we store our secret key we used for signing
  226. memo: signature_secret.to_bytes().to_vec(),
  227. };
  228. let encrypted_note = note.encrypt(&our_pubk)?;
  229. // Pack proofs together with pedersen commitment openings so
  230. // counterparty can verify correctness.
  231. let partial_swap_data = PartialSwapData {
  232. mint_proof,
  233. mint_revealed,
  234. mint_value: vp.1,
  235. mint_token: tp.1,
  236. mint_value_blind: recv_value_blind,
  237. mint_token_blind: recv_token_blind,
  238. burn_proof,
  239. burn_value: vp.0,
  240. burn_token: tp.0,
  241. burn_revealed,
  242. burn_value_blind: coin.note.value_blind,
  243. burn_token_blind: coin.note.token_blind,
  244. encrypted_note,
  245. };
  246. Ok(partial_swap_data)
  247. }
  248. fn inspect_partial(data: &str) -> Result<()> {
  249. let mut mint_valid = false;
  250. let mut burn_valid = false;
  251. let mut mint_value_valid = false;
  252. let mut mint_token_valid = false;
  253. let mut burn_value_valid = false;
  254. let mut burn_token_valid = false;
  255. let bytes = match bs58::decode(data).into_vec() {
  256. Ok(v) => v,
  257. Err(e) => {
  258. eprintln!("Error decoding base58 data from input: {}", e);
  259. exit(1);
  260. }
  261. };
  262. let sd: PartialSwapData = match deserialize(&bytes) {
  263. Ok(v) => v,
  264. Err(e) => {
  265. eprintln!("Error deserializing partial swap data into struct: {}", e);
  266. exit(1);
  267. }
  268. };
  269. eprintln!("Successfully decoded partial swap data");
  270. // Build ZK verifying keys
  271. let pb = progress_bar("Building verifying key for the Mint contract");
  272. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  273. pb.finish();
  274. let pb = progress_bar("Building verifying key for the Burn contract");
  275. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  276. pb.finish();
  277. let pb = progress_bar("Verifying Burn proof");
  278. if verify_burn_proof(&burn_vk, &sd.burn_proof, &sd.burn_revealed).is_ok() {
  279. burn_valid = true;
  280. }
  281. pb.finish();
  282. let pb = progress_bar("Verifying Mint proof");
  283. if verify_mint_proof(&mint_vk, &sd.mint_proof, &sd.mint_revealed).is_ok() {
  284. mint_valid = true;
  285. }
  286. pb.finish();
  287. eprintln!(" Verifying Pedersen commitments");
  288. if pedersen_commitment_u64(sd.burn_value, sd.burn_value_blind) == sd.burn_revealed.value_commit
  289. {
  290. burn_value_valid = true;
  291. }
  292. if pedersen_commitment_base(sd.burn_token, sd.burn_token_blind) == sd.burn_revealed.token_commit
  293. {
  294. burn_token_valid = true;
  295. }
  296. if pedersen_commitment_u64(sd.mint_value, sd.mint_value_blind) == sd.mint_revealed.value_commit
  297. {
  298. mint_value_valid = true;
  299. }
  300. if pedersen_commitment_base(sd.mint_token, sd.mint_token_blind) == sd.mint_revealed.token_commit
  301. {
  302. mint_token_valid = true;
  303. }
  304. let mut valid = true;
  305. eprintln!("Summary:");
  306. eprint!(" Burn proof: ");
  307. if burn_valid {
  308. eprintln!("{}", fg_green("VALID"));
  309. } else {
  310. eprintln!("{}", fg_red("INVALID"));
  311. valid = false;
  312. }
  313. eprint!(" Burn proof value commitment: ");
  314. if burn_value_valid {
  315. eprintln!("{}", fg_green("VALID"));
  316. } else {
  317. eprintln!("{}", fg_red("INVALID"));
  318. valid = false;
  319. }
  320. eprint!(" Burn proof token commitment: ");
  321. if burn_token_valid {
  322. eprintln!("{}", fg_green("VALID"));
  323. } else {
  324. eprintln!("{}", fg_red("INVALID"));
  325. valid = false;
  326. }
  327. eprint!(" Mint proof: ");
  328. if mint_valid {
  329. eprintln!("{}", fg_green("VALID"));
  330. } else {
  331. eprintln!("{}", fg_red("INVALID"));
  332. valid = false;
  333. }
  334. eprint!(" Mint proof value commitment: ");
  335. if mint_value_valid {
  336. eprintln!("{}", fg_green("VALID"));
  337. } else {
  338. eprintln!("{}", fg_red("INVALID"));
  339. valid = false;
  340. }
  341. eprint!(" Mint proof token commitment: ");
  342. if mint_token_valid {
  343. eprintln!("{}", fg_green("VALID"));
  344. } else {
  345. eprintln!("{}", fg_red("INVALID"));
  346. valid = false;
  347. }
  348. eprintln!("========================================");
  349. eprintln!(
  350. "Mint: {} {}",
  351. encode_base10(sd.mint_value, 8),
  352. bs58::encode(sd.mint_token.to_repr()).into_string()
  353. );
  354. eprintln!(
  355. "Burn: {} {}",
  356. encode_base10(sd.burn_value, 8),
  357. bs58::encode(sd.burn_token.to_repr()).into_string()
  358. );
  359. eprint!("\nThe ZK proofs and commitments inspected are ");
  360. if !valid {
  361. println!("{}", fg_red("NOT VALID"));
  362. exit(1);
  363. } else {
  364. eprintln!("{}", fg_green("VALID"));
  365. }
  366. Ok(())
  367. }
  368. async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result<Transaction> {
  369. eprintln!("Joining data into a transaction");
  370. let input0 = PartialTransactionInput { burn_proof: d0.burn_proof, revealed: d0.burn_revealed };
  371. let input1 = PartialTransactionInput { burn_proof: d1.burn_proof, revealed: d1.burn_revealed };
  372. let inputs = vec![input0, input1];
  373. let output0 = TransactionOutput {
  374. mint_proof: d0.mint_proof,
  375. revealed: d0.mint_revealed,
  376. enc_note: d0.encrypted_note.clone(),
  377. };
  378. let output1 = TransactionOutput {
  379. mint_proof: d1.mint_proof,
  380. revealed: d1.mint_revealed,
  381. enc_note: d1.encrypted_note.clone(),
  382. };
  383. let outputs = vec![output0, output1];
  384. let partial_tx = PartialTransaction { clear_inputs: vec![], inputs, outputs };
  385. let mut unsigned_tx_data = vec![];
  386. partial_tx.encode(&mut unsigned_tx_data)?;
  387. let mut inputs = vec![];
  388. let mut signed: bool;
  389. eprint!("Trying to decrypt the note of the first half... ");
  390. let rpc_client = RpcClient::new(endpoint.clone()).await?;
  391. let rpc = Rpc { rpc_client };
  392. let note = match rpc.decrypt_note(&d0.encrypted_note).await {
  393. Ok(v) => v,
  394. Err(_) => None,
  395. };
  396. if let Some(note) = note {
  397. eprintln!("{}", fg_green("Success"));
  398. let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
  399. let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
  400. inputs.push(input);
  401. signed = true;
  402. } else {
  403. eprintln!("{}", fg_red("Failure"));
  404. let signature = schnorr::Signature::dummy();
  405. let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
  406. inputs.push(input);
  407. signed = false;
  408. }
  409. // If we have signed, we shouldn't have to look in the other one, but we might
  410. // be sending to ourself for some reason.
  411. eprint!("Trying to decrypt the note of the second half... ");
  412. let rpc_client = RpcClient::new(endpoint).await?;
  413. let rpc = Rpc { rpc_client };
  414. let note = match rpc.decrypt_note(&d1.encrypted_note).await {
  415. Ok(v) => v,
  416. Err(_) => None,
  417. };
  418. if let Some(note) = note {
  419. eprintln!("{}", fg_green("Success"));
  420. let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
  421. let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
  422. inputs.push(input);
  423. signed = true;
  424. } else {
  425. eprintln!("{}", fg_red("Failure"));
  426. let signature = schnorr::Signature::dummy();
  427. let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
  428. inputs.push(input);
  429. if !signed {
  430. eprintln!("Error: Failed to sign transaction!");
  431. exit(1);
  432. }
  433. }
  434. if !signed {
  435. eprintln!("Error: Failed to sign transaction!");
  436. exit(1);
  437. }
  438. let tx = Transaction { clear_inputs: vec![], inputs, outputs: partial_tx.outputs };
  439. Ok(tx)
  440. }
  441. async fn sign_tx(endpoint: Url, data: &str) -> Result<Transaction> {
  442. eprintln!("Trying to sign transaction");
  443. let mut tx: Transaction = deserialize(&bs58::decode(data).into_vec()?)?;
  444. let mut input_idxs = vec![];
  445. let mut signature = schnorr::Signature::dummy();
  446. // Find dummy signatures to fill. We assume we're using the same
  447. // signature everywhere.
  448. eprintln!("Looking for dummy signatures...");
  449. for (i, input) in tx.inputs.iter().enumerate() {
  450. if input.signature == schnorr::Signature::dummy() {
  451. eprintln!("Found dummy signature in input {}", i);
  452. input_idxs.push(i);
  453. }
  454. }
  455. if input_idxs.is_empty() {
  456. eprintln!("Error: Did not find any dummy signatures in the transaction.");
  457. exit(1);
  458. }
  459. // Find a note to decrypt that holds our secret key.
  460. let mut found_secret = false;
  461. for (i, output) in tx.outputs.iter().enumerate() {
  462. // TODO: FIXME: Consider not closing the RPC on failure.
  463. let rpc_client = RpcClient::new(endpoint.clone()).await?;
  464. let rpc = Rpc { rpc_client };
  465. let note = match rpc.decrypt_note(&output.enc_note).await {
  466. Ok(v) => v,
  467. Err(_) => continue,
  468. };
  469. if let Some(note) = note {
  470. eprintln!("Successfully decrypted note in output {}", i);
  471. eprintln!("Creating signature...");
  472. let mut unsigned_tx_data = vec![];
  473. let _ = tx.encode_without_signature(&mut unsigned_tx_data)?;
  474. signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
  475. found_secret = true;
  476. break
  477. }
  478. eprintln!("Failed to find a note to decrypt. Signing failed.");
  479. exit(1);
  480. }
  481. if !found_secret {
  482. eprintln!("Error: Did not manage to sign transaction. Couldn't find any secret keys.");
  483. exit(1);
  484. }
  485. for i in input_idxs {
  486. tx.inputs[i].signature = signature.clone();
  487. }
  488. Ok(tx)
  489. }
  490. fn try_sign_tx(note: &Note, tx_data: &[u8]) -> Result<schnorr::Signature> {
  491. if note.memo.len() != 32 {
  492. eprintln!("Error: The note memo is not 32 bytes");
  493. exit(1);
  494. }
  495. let secret = match SecretKey::from_bytes(note.memo.clone().try_into().unwrap()) {
  496. Ok(v) => v,
  497. Err(e) => {
  498. eprintln!("Did not manage to cast bytes into SecretKey: {}", e);
  499. exit(1);
  500. }
  501. };
  502. eprintln!("Signing transaction...");
  503. let signature = secret.sign(tx_data);
  504. Ok(signature)
  505. }
  506. #[async_std::main]
  507. async fn main() -> Result<()> {
  508. let args = Args::parse();
  509. match args.command {
  510. Subcmd::Init { token_pair, value_pair } => {
  511. let token_pair = parse_token_pair(&token_pair)?;
  512. let value_pair = parse_value_pair(&value_pair)?;
  513. let swap_data = init_swap(args.endpoint, token_pair, value_pair).await?;
  514. println!("{}", bs58::encode(serialize(&swap_data)).into_string());
  515. Ok(())
  516. }
  517. Subcmd::InspectPartial => {
  518. let mut buf = String::new();
  519. stdin().read_to_string(&mut buf)?;
  520. inspect_partial(&buf.trim())
  521. }
  522. Subcmd::Join { data0, data1 } => {
  523. let d0 = std::fs::read_to_string(data0)?;
  524. let d1 = std::fs::read_to_string(data1)?;
  525. let d0 = deserialize(&bs58::decode(&d0.trim()).into_vec()?)?;
  526. let d1 = deserialize(&bs58::decode(&d1.trim()).into_vec()?)?;
  527. let tx = join(args.endpoint, d0, d1).await?;
  528. println!("{}", bs58::encode(&serialize(&tx)).into_string());
  529. eprintln!("Successfully signed transaction");
  530. Ok(())
  531. }
  532. Subcmd::SignTx => {
  533. let mut buf = String::new();
  534. stdin().read_to_string(&mut buf)?;
  535. let tx = sign_tx(args.endpoint, &buf.trim()).await?;
  536. println!("{}", bs58::encode(&serialize(&tx)).into_string());
  537. eprintln!("Successfully signed transaction");
  538. Ok(())
  539. }
  540. }
  541. }