|
@@ -1,36 +1,37 @@
|
|
|
use std::{
|
|
use std::{
|
|
|
io::{stdin, Read},
|
|
io::{stdin, Read},
|
|
|
process::exit,
|
|
process::exit,
|
|
|
- str::FromStr,
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
use clap::{Parser, Subcommand};
|
|
use clap::{Parser, Subcommand};
|
|
|
-use darkfi::crypto::proof::VerifyingKey;
|
|
|
|
|
use halo2_proofs::{arithmetic::Field, pasta::group::ff::PrimeField};
|
|
use halo2_proofs::{arithmetic::Field, pasta::group::ff::PrimeField};
|
|
|
use rand::rngs::OsRng;
|
|
use rand::rngs::OsRng;
|
|
|
-use serde_json::json;
|
|
|
|
|
-use termion::color;
|
|
|
|
|
use url::Url;
|
|
use url::Url;
|
|
|
|
|
|
|
|
use darkfi::{
|
|
use darkfi::{
|
|
|
cli_desc,
|
|
cli_desc,
|
|
|
crypto::{
|
|
crypto::{
|
|
|
- address::Address,
|
|
|
|
|
burn_proof::{create_burn_proof, verify_burn_proof},
|
|
burn_proof::{create_burn_proof, verify_burn_proof},
|
|
|
keypair::{PublicKey, SecretKey},
|
|
keypair::{PublicKey, SecretKey},
|
|
|
- merkle_node::MerkleNode,
|
|
|
|
|
mint_proof::{create_mint_proof, verify_mint_proof},
|
|
mint_proof::{create_mint_proof, verify_mint_proof},
|
|
|
- proof::ProvingKey,
|
|
|
|
|
|
|
+ note::{EncryptedNote, Note},
|
|
|
|
|
+ proof::{ProvingKey, VerifyingKey},
|
|
|
|
|
+ schnorr,
|
|
|
|
|
+ schnorr::SchnorrSecret,
|
|
|
token_id,
|
|
token_id,
|
|
|
types::{DrkCoinBlind, DrkSerial, DrkTokenId, DrkValueBlind},
|
|
types::{DrkCoinBlind, DrkSerial, DrkTokenId, DrkValueBlind},
|
|
|
util::{pedersen_commitment_base, pedersen_commitment_u64},
|
|
util::{pedersen_commitment_base, pedersen_commitment_u64},
|
|
|
- BurnRevealedValues, MintRevealedValues, OwnCoin, Proof,
|
|
|
|
|
|
|
+ BurnRevealedValues, MintRevealedValues, Proof,
|
|
|
|
|
+ },
|
|
|
|
|
+ rpc::client::RpcClient,
|
|
|
|
|
+ tx::{
|
|
|
|
|
+ partial::{PartialTransaction, PartialTransactionInput},
|
|
|
|
|
+ Transaction, TransactionInput, TransactionOutput,
|
|
|
},
|
|
},
|
|
|
- rpc::{client::RpcClient, jsonrpc::JsonRequest},
|
|
|
|
|
util::{
|
|
util::{
|
|
|
- cli::progress_bar,
|
|
|
|
|
|
|
+ cli::{fg_green, fg_red, progress_bar},
|
|
|
encode_base10,
|
|
encode_base10,
|
|
|
- serial::{deserialize, serialize, SerialDecodable, SerialEncodable},
|
|
|
|
|
|
|
+ serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable},
|
|
|
},
|
|
},
|
|
|
zk::circuit::{BurnContract, MintContract},
|
|
zk::circuit::{BurnContract, MintContract},
|
|
|
Result,
|
|
Result,
|
|
@@ -38,6 +39,8 @@ use darkfi::{
|
|
|
|
|
|
|
|
mod cli_util;
|
|
mod cli_util;
|
|
|
use cli_util::{parse_token_pair, parse_value_pair};
|
|
use cli_util::{parse_token_pair, parse_value_pair};
|
|
|
|
|
+mod rpc;
|
|
|
|
|
+use rpc::Rpc;
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
#[derive(Parser)]
|
|
|
#[clap(name = "darkotc", about = cli_desc!(), version)]
|
|
#[clap(name = "darkotc", about = cli_desc!(), version)]
|
|
@@ -60,187 +63,104 @@ enum Subcmd {
|
|
|
/// Initialize an atomic swap
|
|
/// Initialize an atomic swap
|
|
|
Init {
|
|
Init {
|
|
|
#[clap(short, long)]
|
|
#[clap(short, long)]
|
|
|
- /// Pair of token IDs to swap: e.g. token_to_send:token_to_recv
|
|
|
|
|
|
|
+ /// Pair of token IDs to swap: token_to_send:token_to_recv
|
|
|
token_pair: String,
|
|
token_pair: String,
|
|
|
|
|
|
|
|
#[clap(short, long)]
|
|
#[clap(short, long)]
|
|
|
- /// Pair of values to swap: e.g. value_to_send:value_to_recv
|
|
|
|
|
|
|
+ /// Pair of values to swap: value_to_send:value_to_recv
|
|
|
value_pair: String,
|
|
value_pair: String,
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- /// Inspect swap data from stdin or file.
|
|
|
|
|
|
|
+ /// Inspect partial swap data from stdin.
|
|
|
Inspect,
|
|
Inspect,
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-struct Rpc {
|
|
|
|
|
- pub rpc_client: RpcClient,
|
|
|
|
|
|
|
+ /// Join two partial swap data files and build a tx
|
|
|
|
|
+ Join { data0: String, data1: String },
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-impl Rpc {
|
|
|
|
|
- async fn balance_of(&self, token_id: &str) -> Result<u64> {
|
|
|
|
|
- let req = JsonRequest::new("wallet.get_balances", json!([]));
|
|
|
|
|
- let rep = self.rpc_client.request(req).await?;
|
|
|
|
|
-
|
|
|
|
|
- if !rep.is_object() {
|
|
|
|
|
- eprintln!("Error: Invalid balance data received from darkfid RPC endpoint.");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- for i in rep.as_object().unwrap().keys() {
|
|
|
|
|
- if i == &token_id {
|
|
|
|
|
- if let Some(balance) = rep[i].as_u64() {
|
|
|
|
|
- return Ok(balance)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- eprintln!("Error: Invalid balance data received from darkfid RPC endpoint.");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Ok(0)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- async fn wallet_address(&self) -> Result<Address> {
|
|
|
|
|
- let req = JsonRequest::new("wallet.get_addrs", json!([0_i64]));
|
|
|
|
|
- let rep = self.rpc_client.request(req).await?;
|
|
|
|
|
-
|
|
|
|
|
- if !rep.is_array() || !rep.as_array().unwrap()[0].is_string() {
|
|
|
|
|
- eprintln!("Error: Invalid wallet address received from darkfid RPC endpoint.");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Address::from_str(rep[0].as_str().unwrap())
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- async fn get_coins_valtok(&self, value: u64, token_id: &str) -> Result<Vec<OwnCoin>> {
|
|
|
|
|
- let req = JsonRequest::new("wallet.get_coins_valtok", json!([value, token_id, true]));
|
|
|
|
|
- let rep = self.rpc_client.request(req).await?;
|
|
|
|
|
-
|
|
|
|
|
- if !rep.is_array() {
|
|
|
|
|
- eprintln!("Error: Invalid coin data received from darkfid RPC endpoint.");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let mut ret = vec![];
|
|
|
|
|
- let rep = rep.as_array().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- for i in rep {
|
|
|
|
|
- if !i.is_string() {
|
|
|
|
|
- eprintln!("Error: Invalid base58 data for OwnCoin");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let data = match bs58::decode(i.as_str().unwrap()).into_vec() {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- eprintln!("Error: Failed decoding base58 for OwnCoin: {}", e);
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let oc = match deserialize(&data) {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- eprintln!("Error: Failed deserializing OwnCoin: {}", e);
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- ret.push(oc);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Ok(ret)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- async fn get_merkle_path(&self, leaf_pos: usize) -> Result<Vec<MerkleNode>> {
|
|
|
|
|
- let req = JsonRequest::new("wallet.get_merkle_path", json!([leaf_pos as u64]));
|
|
|
|
|
- let rep = self.rpc_client.request(req).await?;
|
|
|
|
|
-
|
|
|
|
|
- if !rep.is_array() {
|
|
|
|
|
- eprintln!("Error: Invalid merkle path data received from darkfid RPC endpoint.");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let mut ret = vec![];
|
|
|
|
|
- let rep = rep.as_array().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- for i in rep {
|
|
|
|
|
- if !i.is_string() {
|
|
|
|
|
- eprintln!("Error: Invalid base58 data for MerkleNode");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let n = i.as_str().unwrap();
|
|
|
|
|
- let n = match bs58::decode(n).into_vec() {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- eprintln!("Error: Failed decoding base58 for MerkleNode: {}", e);
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- if n.len() != 32 {
|
|
|
|
|
- eprintln!("Error: MerkleNode byte length is not 32");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let n = MerkleNode::from_bytes(&n.try_into().unwrap());
|
|
|
|
|
- if n.is_some().unwrap_u8() == 0 {
|
|
|
|
|
- eprintln!("Error: Noncanonical bytes of MerkleNode");
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ret.push(n.unwrap());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+#[derive(SerialEncodable, SerialDecodable)]
|
|
|
|
|
+/// Half of the swap data, includes the coin that is supposed to be received,
|
|
|
|
|
+/// and the coin that is supposed to be sent.
|
|
|
|
|
+struct PartialSwapData {
|
|
|
|
|
+ /// Mint proof of coin to be received
|
|
|
|
|
+ mint_proof: Proof,
|
|
|
|
|
+ /// Public values for the mint proof
|
|
|
|
|
+ mint_revealed: MintRevealedValues,
|
|
|
|
|
+ /// Value of the coin to be received
|
|
|
|
|
+ mint_value: u64,
|
|
|
|
|
+ /// Token ID of the coin to be received
|
|
|
|
|
+ mint_token: DrkTokenId,
|
|
|
|
|
+ /// Blinding factor for the minted value pedersen commitment
|
|
|
|
|
+ mint_value_blind: DrkValueBlind,
|
|
|
|
|
+ /// Blinding factor for the minted token ID pedersen commitment
|
|
|
|
|
+ mint_token_blind: DrkValueBlind,
|
|
|
|
|
+ /// Burn proof of the coin to be sent
|
|
|
|
|
+ burn_proof: Proof,
|
|
|
|
|
+ /// Public values for the burn proof
|
|
|
|
|
+ burn_revealed: BurnRevealedValues,
|
|
|
|
|
+ /// Value of the coin to be sent
|
|
|
|
|
+ burn_value: u64,
|
|
|
|
|
+ /// Token ID of the coin to be sent
|
|
|
|
|
+ burn_token: DrkTokenId,
|
|
|
|
|
+ /// Blinding factor for the burned value pedersen commitment
|
|
|
|
|
+ burn_value_blind: DrkValueBlind,
|
|
|
|
|
+ /// Blinding factor for the burned token ID pedersen commitment
|
|
|
|
|
+ burn_token_blind: DrkValueBlind,
|
|
|
|
|
+ /// Encrypted note
|
|
|
|
|
+ encrypted_note: EncryptedNote,
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- Ok(ret)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+#[derive(SerialEncodable, SerialDecodable)]
|
|
|
|
|
+/// Full swap data, containing two instances of `PartialSwapData`, which
|
|
|
|
|
+/// represent an atomic swap.
|
|
|
|
|
+struct SwapData {
|
|
|
|
|
+ swap0: PartialSwapData,
|
|
|
|
|
+ swap1: PartialSwapData,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async fn init_swap(
|
|
async fn init_swap(
|
|
|
endpoint: Url,
|
|
endpoint: Url,
|
|
|
token_pair: (String, String),
|
|
token_pair: (String, String),
|
|
|
value_pair: (u64, u64),
|
|
value_pair: (u64, u64),
|
|
|
-) -> Result<()> {
|
|
|
|
|
|
|
+) -> Result<PartialSwapData> {
|
|
|
let rpc_client = RpcClient::new(endpoint).await?;
|
|
let rpc_client = RpcClient::new(endpoint).await?;
|
|
|
let rpc = Rpc { rpc_client };
|
|
let rpc = Rpc { rpc_client };
|
|
|
|
|
|
|
|
- // TODO: Think about decimals, there has to be some metadata to keep track.
|
|
|
|
|
|
|
+ // TODO: Implement metadata for decimals, don't hardcode.
|
|
|
let tp = (token_id::parse_b58(&token_pair.0)?, token_id::parse_b58(&token_pair.1)?);
|
|
let tp = (token_id::parse_b58(&token_pair.0)?, token_id::parse_b58(&token_pair.1)?);
|
|
|
- let vp: (u64, u64) =
|
|
|
|
|
- (value_pair.0.clone().try_into().unwrap(), value_pair.1.clone().try_into().unwrap());
|
|
|
|
|
|
|
+ let vp = value_pair;
|
|
|
|
|
|
|
|
// Connect to darkfid and see if there's available funds.
|
|
// Connect to darkfid and see if there's available funds.
|
|
|
let balance = rpc.balance_of(&token_pair.0).await?;
|
|
let balance = rpc.balance_of(&token_pair.0).await?;
|
|
|
if balance < vp.0 {
|
|
if balance < vp.0 {
|
|
|
eprintln!(
|
|
eprintln!(
|
|
|
- "Error: There is not enough balance for token \"{}\" in your wallet.",
|
|
|
|
|
|
|
+ "Error: There's not enough balance for token \"{}\" in your wallet.",
|
|
|
token_pair.0
|
|
token_pair.0
|
|
|
);
|
|
);
|
|
|
eprintln!("Available balance is {} ({})", encode_base10(balance, 8), balance);
|
|
eprintln!("Available balance is {} ({})", encode_base10(balance, 8), balance);
|
|
|
exit(1);
|
|
exit(1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // If not enough funds in a single coin, mint a single new coin
|
|
|
|
|
- // with the funds. We do this to minimize the size of the swap
|
|
|
|
|
- // transaction, i.e. 2 inputs and 2 outputs.
|
|
|
|
|
|
|
+ // If there's not enough funds in a single coin, mint a single new coin
|
|
|
|
|
+ // with the funds. We do this to minimize the size of the swap transaction.
|
|
|
|
|
+ // i.e. 2 inputs and 2 outputs.
|
|
|
// TODO: Implement ^
|
|
// TODO: Implement ^
|
|
|
// TODO: Maybe this should be done by the user beforehand?
|
|
// TODO: Maybe this should be done by the user beforehand?
|
|
|
|
|
|
|
|
- // Find a coin to spend
|
|
|
|
|
|
|
+ // Find a coin to spend. We can find multiple, but we'll pick the first one.
|
|
|
let coins = rpc.get_coins_valtok(vp.0, &token_pair.0).await?;
|
|
let coins = rpc.get_coins_valtok(vp.0, &token_pair.0).await?;
|
|
|
if coins.is_empty() {
|
|
if coins.is_empty() {
|
|
|
- eprintln!("Error: Did not manage to find a coin with enough value to spend");
|
|
|
|
|
|
|
+ eprintln!("Error: Did not manage to find a coin with enough value to spend.");
|
|
|
exit(1);
|
|
exit(1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
eprintln!("Initializing swap data for:");
|
|
eprintln!("Initializing swap data for:");
|
|
|
- eprintln!("Send: {} {} tokens", encode_base10(value_pair.0, 8), token_pair.0);
|
|
|
|
|
- eprintln!("Recv: {} {} tokens", encode_base10(value_pair.1, 8), token_pair.1);
|
|
|
|
|
|
|
+ eprintln!("Send: {} {} tokens", encode_base10(vp.0, 8), token_pair.0);
|
|
|
|
|
+ eprintln!("Recv: {} {} tokens", encode_base10(vp.1, 8), token_pair.1);
|
|
|
|
|
|
|
|
// Fetch our default address
|
|
// Fetch our default address
|
|
|
- let our_address = rpc.wallet_address().await?;
|
|
|
|
|
- let our_publickey = match PublicKey::try_from(our_address) {
|
|
|
|
|
|
|
+ let our_addr = rpc.wallet_address().await?;
|
|
|
|
|
+ let our_pubk = match PublicKey::try_from(our_addr) {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(e) => {
|
|
Err(e) => {
|
|
|
eprintln!("Error converting our address into PublicKey: {}", e);
|
|
eprintln!("Error converting our address into PublicKey: {}", e);
|
|
@@ -248,22 +168,22 @@ async fn init_swap(
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // Build proving keys
|
|
|
|
|
- let pb = progress_bar("Building proving key for the mint contract");
|
|
|
|
|
- let mint_pk = ProvingKey::build(8, &MintContract::default());
|
|
|
|
|
|
|
+ // Build ZK proving keys
|
|
|
|
|
+ let pb = progress_bar("Building proving key for the Mint contract");
|
|
|
|
|
+ let mint_pk = ProvingKey::build(11, &MintContract::default());
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
- let pb = progress_bar("Building proving key for the burn contract");
|
|
|
|
|
|
|
+ let pb = progress_bar("Building proving key for the Burn contract");
|
|
|
let burn_pk = ProvingKey::build(11, &BurnContract::default());
|
|
let burn_pk = ProvingKey::build(11, &BurnContract::default());
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
- // The coin we want to receive.
|
|
|
|
|
|
|
+ // The coin we want to receive
|
|
|
let recv_value_blind = DrkValueBlind::random(&mut OsRng);
|
|
let recv_value_blind = DrkValueBlind::random(&mut OsRng);
|
|
|
let recv_token_blind = DrkValueBlind::random(&mut OsRng);
|
|
let recv_token_blind = DrkValueBlind::random(&mut OsRng);
|
|
|
let recv_coin_blind = DrkCoinBlind::random(&mut OsRng);
|
|
let recv_coin_blind = DrkCoinBlind::random(&mut OsRng);
|
|
|
let recv_serial = DrkSerial::random(&mut OsRng);
|
|
let recv_serial = DrkSerial::random(&mut OsRng);
|
|
|
|
|
|
|
|
- let pb = progress_bar("Building mint proof for receiving coin");
|
|
|
|
|
|
|
+ let pb = progress_bar("Building Mint proof for the receiving coin");
|
|
|
let (mint_proof, mint_revealed) = create_mint_proof(
|
|
let (mint_proof, mint_revealed) = create_mint_proof(
|
|
|
&mint_pk,
|
|
&mint_pk,
|
|
|
vp.1,
|
|
vp.1,
|
|
@@ -272,20 +192,19 @@ async fn init_swap(
|
|
|
recv_token_blind,
|
|
recv_token_blind,
|
|
|
recv_serial,
|
|
recv_serial,
|
|
|
recv_coin_blind,
|
|
recv_coin_blind,
|
|
|
- our_publickey,
|
|
|
|
|
|
|
+ our_pubk,
|
|
|
)?;
|
|
)?;
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
// The coin we are spending.
|
|
// The coin we are spending.
|
|
|
- // We'll spend the first one we've found.
|
|
|
|
|
- let coin = coins[0];
|
|
|
|
|
|
|
+ let coin = coins[0].clone();
|
|
|
|
|
|
|
|
- let pb = progress_bar("Building burn proof for spending coin");
|
|
|
|
|
|
|
+ let pb = progress_bar("Building Burn proof for the spending coin");
|
|
|
let signature_secret = SecretKey::random(&mut OsRng);
|
|
let signature_secret = SecretKey::random(&mut OsRng);
|
|
|
let merkle_path = match rpc.get_merkle_path(usize::from(coin.leaf_position)).await {
|
|
let merkle_path = match rpc.get_merkle_path(usize::from(coin.leaf_position)).await {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(e) => {
|
|
Err(e) => {
|
|
|
- eprintln!("Failed to get merkle path for our coin from darkfid RPC: {}", e);
|
|
|
|
|
|
|
+ eprintln!("Failed to get Merkle path for our coin from darkfid RPC: {}", e);
|
|
|
exit(1);
|
|
exit(1);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -305,9 +224,22 @@ async fn init_swap(
|
|
|
)?;
|
|
)?;
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
|
|
+ // Create encrypted note
|
|
|
|
|
+ let note = Note {
|
|
|
|
|
+ serial: recv_serial,
|
|
|
|
|
+ value: vp.1,
|
|
|
|
|
+ token_id: tp.1,
|
|
|
|
|
+ coin_blind: recv_coin_blind,
|
|
|
|
|
+ value_blind: recv_value_blind,
|
|
|
|
|
+ token_blind: recv_token_blind,
|
|
|
|
|
+ // Here we store our secret key we used for signing
|
|
|
|
|
+ memo: signature_secret.to_bytes().to_vec(),
|
|
|
|
|
+ };
|
|
|
|
|
+ let encrypted_note = note.encrypt(&our_pubk)?;
|
|
|
|
|
+
|
|
|
// Pack proofs together with pedersen commitment openings so
|
|
// Pack proofs together with pedersen commitment openings so
|
|
|
// counterparty can verify correctness.
|
|
// counterparty can verify correctness.
|
|
|
- let swap_data = SwapData {
|
|
|
|
|
|
|
+ let partial_swap_data = PartialSwapData {
|
|
|
mint_proof,
|
|
mint_proof,
|
|
|
mint_revealed,
|
|
mint_revealed,
|
|
|
mint_value: vp.1,
|
|
mint_value: vp.1,
|
|
@@ -320,12 +252,10 @@ async fn init_swap(
|
|
|
burn_revealed,
|
|
burn_revealed,
|
|
|
burn_value_blind: coin.note.value_blind,
|
|
burn_value_blind: coin.note.value_blind,
|
|
|
burn_token_blind: coin.note.token_blind,
|
|
burn_token_blind: coin.note.token_blind,
|
|
|
|
|
+ encrypted_note,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // Print encoded data.
|
|
|
|
|
- println!("{}", bs58::encode(serialize(&swap_data)).into_string());
|
|
|
|
|
-
|
|
|
|
|
- Ok(())
|
|
|
|
|
|
|
+ Ok(partial_swap_data)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn inspect(data: &str) -> Result<()> {
|
|
fn inspect(data: &str) -> Result<()> {
|
|
@@ -344,38 +274,38 @@ fn inspect(data: &str) -> Result<()> {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- let sd: SwapData = match deserialize(&bytes) {
|
|
|
|
|
|
|
+ let sd: PartialSwapData = match deserialize(&bytes) {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(e) => {
|
|
Err(e) => {
|
|
|
- eprintln!("Error: Failed to deserialize swap data into struct: {}", e);
|
|
|
|
|
|
|
+ eprintln!("Error deserializing partial swap data into struct: {}", e);
|
|
|
exit(1);
|
|
exit(1);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- eprintln!("Successfully decoded data into SwapData struct");
|
|
|
|
|
|
|
+ eprintln!("Successfully decoded partial swap data");
|
|
|
|
|
|
|
|
- // Build verifying keys
|
|
|
|
|
- let pb = progress_bar("Building verifying key for the mint contract");
|
|
|
|
|
- let mint_vk = VerifyingKey::build(8, &MintContract::default());
|
|
|
|
|
|
|
+ // Build ZK verifying keys
|
|
|
|
|
+ let pb = progress_bar("Building verifying key for the Mint contract");
|
|
|
|
|
+ let mint_vk = VerifyingKey::build(11, &MintContract::default());
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
- let pb = progress_bar("Building verifying key for the burn contract");
|
|
|
|
|
|
|
+ let pb = progress_bar("Building verifying key for the Burn contract");
|
|
|
let burn_vk = VerifyingKey::build(11, &BurnContract::default());
|
|
let burn_vk = VerifyingKey::build(11, &BurnContract::default());
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
- let pb = progress_bar("Verifying burn proof");
|
|
|
|
|
|
|
+ let pb = progress_bar("Verifying Burn proof");
|
|
|
if verify_burn_proof(&burn_vk, &sd.burn_proof, &sd.burn_revealed).is_ok() {
|
|
if verify_burn_proof(&burn_vk, &sd.burn_proof, &sd.burn_revealed).is_ok() {
|
|
|
burn_valid = true;
|
|
burn_valid = true;
|
|
|
}
|
|
}
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
- let pb = progress_bar("Verifying mint proof");
|
|
|
|
|
|
|
+ let pb = progress_bar("Verifying Mint proof");
|
|
|
if verify_mint_proof(&mint_vk, &sd.mint_proof, &sd.mint_revealed).is_ok() {
|
|
if verify_mint_proof(&mint_vk, &sd.mint_proof, &sd.mint_revealed).is_ok() {
|
|
|
mint_valid = true;
|
|
mint_valid = true;
|
|
|
}
|
|
}
|
|
|
pb.finish();
|
|
pb.finish();
|
|
|
|
|
|
|
|
- eprintln!(" Verifying pedersen commitments");
|
|
|
|
|
|
|
+ eprintln!(" Verifying Pedersen commitments");
|
|
|
|
|
|
|
|
if pedersen_commitment_u64(sd.burn_value, sd.burn_value_blind) == sd.burn_revealed.value_commit
|
|
if pedersen_commitment_u64(sd.burn_value, sd.burn_value_blind) == sd.burn_revealed.value_commit
|
|
|
{
|
|
{
|
|
@@ -402,49 +332,49 @@ fn inspect(data: &str) -> Result<()> {
|
|
|
|
|
|
|
|
eprint!(" Burn proof: ");
|
|
eprint!(" Burn proof: ");
|
|
|
if burn_valid {
|
|
if burn_valid {
|
|
|
- eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_red("INVALID"));
|
|
|
valid = false;
|
|
valid = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
eprint!(" Burn proof value commitment: ");
|
|
eprint!(" Burn proof value commitment: ");
|
|
|
if burn_value_valid {
|
|
if burn_value_valid {
|
|
|
- eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_red("INVALID"));
|
|
|
valid = false;
|
|
valid = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
eprint!(" Burn proof token commitment: ");
|
|
eprint!(" Burn proof token commitment: ");
|
|
|
if burn_token_valid {
|
|
if burn_token_valid {
|
|
|
- eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_red("INVALID"));
|
|
|
valid = false;
|
|
valid = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
eprint!(" Mint proof: ");
|
|
eprint!(" Mint proof: ");
|
|
|
if mint_valid {
|
|
if mint_valid {
|
|
|
- eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_red("INVALID"));
|
|
|
valid = false;
|
|
valid = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
eprint!(" Mint proof value commitment: ");
|
|
eprint!(" Mint proof value commitment: ");
|
|
|
if mint_value_valid {
|
|
if mint_value_valid {
|
|
|
- eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_red("INVALID"));
|
|
|
valid = false;
|
|
valid = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
eprint!(" Mint proof token commitment: ");
|
|
eprint!(" Mint proof token commitment: ");
|
|
|
if mint_token_valid {
|
|
if mint_token_valid {
|
|
|
- eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
|
|
|
|
|
|
|
+ eprintln!("{}", fg_red("INVALID"));
|
|
|
valid = false;
|
|
valid = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -461,38 +391,105 @@ fn inspect(data: &str) -> Result<()> {
|
|
|
bs58::encode(sd.burn_token.to_repr()).into_string()
|
|
bs58::encode(sd.burn_token.to_repr()).into_string()
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
|
|
+ eprint!("\nThe ZK proofs and commitments inspected are ");
|
|
|
if !valid {
|
|
if !valid {
|
|
|
- eprintln!(
|
|
|
|
|
- "\nThe ZK proofs and commitments inspected are {}NOT VALID{}",
|
|
|
|
|
- color::Fg(color::Red),
|
|
|
|
|
- color::Fg(color::Reset)
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ println!("{}", fg_red("NOT VALID"));
|
|
|
exit(1);
|
|
exit(1);
|
|
|
} else {
|
|
} else {
|
|
|
- eprintln!(
|
|
|
|
|
- "\nThe ZK proofs and commitments inspected are {}VALID{}",
|
|
|
|
|
- color::Fg(color::Green),
|
|
|
|
|
- color::Fg(color::Reset)
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ eprintln!("{}", fg_green("VALID"));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-#[derive(SerialEncodable, SerialDecodable)]
|
|
|
|
|
-struct SwapData {
|
|
|
|
|
- mint_proof: Proof,
|
|
|
|
|
- mint_revealed: MintRevealedValues,
|
|
|
|
|
- mint_value: u64,
|
|
|
|
|
- mint_token: DrkTokenId,
|
|
|
|
|
- mint_value_blind: DrkValueBlind,
|
|
|
|
|
- mint_token_blind: DrkValueBlind,
|
|
|
|
|
- burn_proof: Proof,
|
|
|
|
|
- burn_revealed: BurnRevealedValues,
|
|
|
|
|
- burn_value: u64,
|
|
|
|
|
- burn_token: DrkTokenId,
|
|
|
|
|
- burn_value_blind: DrkValueBlind,
|
|
|
|
|
- burn_token_blind: DrkValueBlind,
|
|
|
|
|
|
|
+async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result<Transaction> {
|
|
|
|
|
+ let rpc_client = RpcClient::new(endpoint).await?;
|
|
|
|
|
+ let rpc = Rpc { rpc_client };
|
|
|
|
|
+
|
|
|
|
|
+ eprintln!("Joining data into a transaction");
|
|
|
|
|
+
|
|
|
|
|
+ let input0 = PartialTransactionInput { burn_proof: d0.burn_proof, revealed: d0.burn_revealed };
|
|
|
|
|
+ let input1 = PartialTransactionInput { burn_proof: d1.burn_proof, revealed: d1.burn_revealed };
|
|
|
|
|
+ let inputs = vec![input0, input1];
|
|
|
|
|
+
|
|
|
|
|
+ let output0 = TransactionOutput {
|
|
|
|
|
+ mint_proof: d0.mint_proof,
|
|
|
|
|
+ revealed: d0.mint_revealed,
|
|
|
|
|
+ enc_note: d0.encrypted_note.clone(),
|
|
|
|
|
+ };
|
|
|
|
|
+ let output1 = TransactionOutput {
|
|
|
|
|
+ mint_proof: d1.mint_proof,
|
|
|
|
|
+ revealed: d1.mint_revealed,
|
|
|
|
|
+ enc_note: d1.encrypted_note.clone(),
|
|
|
|
|
+ };
|
|
|
|
|
+ let outputs = vec![output0, output1];
|
|
|
|
|
+
|
|
|
|
|
+ let partial_tx = PartialTransaction { clear_inputs: vec![], inputs, outputs };
|
|
|
|
|
+ let mut unsigned_tx_data = vec![];
|
|
|
|
|
+ partial_tx.encode(&mut unsigned_tx_data)?;
|
|
|
|
|
+
|
|
|
|
|
+ let mut inputs = vec![];
|
|
|
|
|
+ let mut signed: bool;
|
|
|
|
|
+
|
|
|
|
|
+ eprint!("Trying to decrypt the note of the first half... ");
|
|
|
|
|
+ if let Some(note) = rpc.decrypt_note(&d0.encrypted_note).await? {
|
|
|
|
|
+ eprintln!("{}", fg_green("Success"));
|
|
|
|
|
+ let signature = try_sign_tx(¬e, &unsigned_tx_data[..])?;
|
|
|
|
|
+ let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
|
|
|
|
|
+ inputs.push(input);
|
|
|
|
|
+ signed = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ eprintln!("{}", fg_red("Failure"));
|
|
|
|
|
+ let signature = schnorr::Signature::dummy();
|
|
|
|
|
+ let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
|
|
|
|
|
+ inputs.push(input);
|
|
|
|
|
+ signed = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If we have signed, we shouldn't have to look in the other one.
|
|
|
|
|
+ if !signed {
|
|
|
|
|
+ eprint!("Trying to decrypt the note of the second half... ");
|
|
|
|
|
+ if let Some(note) = rpc.decrypt_note(&d1.encrypted_note).await? {
|
|
|
|
|
+ eprintln!("{}", fg_green("Success"));
|
|
|
|
|
+ let signature = try_sign_tx(¬e, &unsigned_tx_data[..])?;
|
|
|
|
|
+ let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
|
|
|
|
|
+ inputs.push(input);
|
|
|
|
|
+ signed = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ eprintln!("{}", fg_red("Failure"));
|
|
|
|
|
+ let signature = schnorr::Signature::dummy();
|
|
|
|
|
+ let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
|
|
|
|
|
+ inputs.push(input);
|
|
|
|
|
+ signed = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if !signed {
|
|
|
|
|
+ eprintln!("Error: Failed to sign transaction!");
|
|
|
|
|
+ exit(1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let tx = Transaction { clear_inputs: vec![], inputs, outputs: partial_tx.outputs };
|
|
|
|
|
+ Ok(tx)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn try_sign_tx(note: &Note, tx_data: &[u8]) -> Result<schnorr::Signature> {
|
|
|
|
|
+ if note.memo.len() != 32 {
|
|
|
|
|
+ eprintln!("Error: The note memo is not 32 bytes");
|
|
|
|
|
+ exit(1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let secret = match SecretKey::from_bytes(note.memo.clone().try_into().unwrap()) {
|
|
|
|
|
+ Ok(v) => v,
|
|
|
|
|
+ Err(e) => {
|
|
|
|
|
+ eprintln!("Did not manage to coerce the bytes into SecretKey: {}", e);
|
|
|
|
|
+ exit(1);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ eprintln!("Signing transaction...");
|
|
|
|
|
+ let signature = secret.sign(tx_data);
|
|
|
|
|
+ Ok(signature)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[async_std::main]
|
|
#[async_std::main]
|
|
@@ -503,12 +500,23 @@ async fn main() -> Result<()> {
|
|
|
Subcmd::Init { token_pair, value_pair } => {
|
|
Subcmd::Init { token_pair, value_pair } => {
|
|
|
let token_pair = parse_token_pair(&token_pair)?;
|
|
let token_pair = parse_token_pair(&token_pair)?;
|
|
|
let value_pair = parse_value_pair(&value_pair)?;
|
|
let value_pair = parse_value_pair(&value_pair)?;
|
|
|
- init_swap(args.endpoint, token_pair, value_pair).await
|
|
|
|
|
|
|
+ let swap_data = init_swap(args.endpoint, token_pair, value_pair).await?;
|
|
|
|
|
+ println!("{}", bs58::encode(serialize(&swap_data)).into_string());
|
|
|
|
|
+ Ok(())
|
|
|
}
|
|
}
|
|
|
Subcmd::Inspect => {
|
|
Subcmd::Inspect => {
|
|
|
let mut buf = String::new();
|
|
let mut buf = String::new();
|
|
|
stdin().read_to_string(&mut buf)?;
|
|
stdin().read_to_string(&mut buf)?;
|
|
|
inspect(&buf.trim())
|
|
inspect(&buf.trim())
|
|
|
}
|
|
}
|
|
|
|
|
+ Subcmd::Join { data0, data1 } => {
|
|
|
|
|
+ let d0 = std::fs::read_to_string(data0)?;
|
|
|
|
|
+ let d1 = std::fs::read_to_string(data1)?;
|
|
|
|
|
+ let d0 = deserialize(&bs58::decode(&d0).into_vec()?)?;
|
|
|
|
|
+ let d1 = deserialize(&bs58::decode(&d1).into_vec()?)?;
|
|
|
|
|
+ let tx = join(args.endpoint, d0, d1).await?;
|
|
|
|
|
+ println!("{}", bs58::encode(&serialize(&tx)).into_string());
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|