Selaa lähdekoodia

cherry picked fullwidth pedersen

parazyd 4 vuotta sitten
vanhempi
sitoutus
3686c32591

+ 1 - 1
Makefile

@@ -10,7 +10,7 @@ CARGO = cargo
 #RUSTFLAGS = -C target-cpu=native
 
 # Binaries to be built
-BINS = zkas drk darkfid tau taud ircd dnetview
+BINS = zkas drk darkfid tau taud ircd dnetview darkotc
 
 # Common dependencies which should force the binaries to be rebuilt
 BINDEPS = \

+ 514 - 0
bin/darkotc/src/main.rs

@@ -0,0 +1,514 @@
+use std::{
+    io::{stdin, Read},
+    process::exit,
+    str::FromStr,
+};
+
+use clap::{Parser, Subcommand};
+use darkfi::crypto::proof::VerifyingKey;
+use halo2_proofs::{arithmetic::Field, pasta::group::ff::PrimeField};
+use rand::rngs::OsRng;
+use serde_json::json;
+use termion::color;
+use url::Url;
+
+use darkfi::{
+    cli_desc,
+    crypto::{
+        address::Address,
+        burn_proof::{create_burn_proof, verify_burn_proof},
+        keypair::{PublicKey, SecretKey},
+        merkle_node::MerkleNode,
+        mint_proof::{create_mint_proof, verify_mint_proof},
+        proof::ProvingKey,
+        token_id,
+        types::{DrkCoinBlind, DrkSerial, DrkTokenId, DrkValueBlind},
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
+        BurnRevealedValues, MintRevealedValues, OwnCoin, Proof,
+    },
+    rpc::{client::RpcClient, jsonrpc::JsonRequest},
+    util::{
+        cli::progress_bar,
+        encode_base10,
+        serial::{deserialize, serialize, SerialDecodable, SerialEncodable},
+    },
+    zk::circuit::{BurnContract, MintContract},
+    Result,
+};
+
+mod cli_util;
+use cli_util::{parse_token_pair, parse_value_pair};
+
+#[derive(Parser)]
+#[clap(name = "darkotc", about = cli_desc!(), version)]
+#[clap(arg_required_else_help(true))]
+struct Args {
+    #[clap(short, parse(from_occurrences))]
+    /// Increase verbosity (-vvv supported)
+    verbose: u8,
+
+    #[clap(short, long, default_value = "tcp://127.0.0.1:8340")]
+    /// darkfid JSON-RPC endpoint
+    endpoint: Url,
+
+    #[clap(subcommand)]
+    command: Subcmd,
+}
+
+#[derive(Subcommand)]
+enum Subcmd {
+    /// Initialize an atomic swap
+    Init {
+        #[clap(short, long)]
+        /// Pair of token IDs to swap: e.g. token_to_send:token_to_recv
+        token_pair: String,
+
+        #[clap(short, long)]
+        /// Pair of values to swap: e.g. value_to_send:value_to_recv
+        value_pair: String,
+    },
+
+    /// Inspect swap data from stdin or file.
+    Inspect,
+}
+
+struct Rpc {
+    pub rpc_client: RpcClient,
+}
+
+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());
+        }
+
+        Ok(ret)
+    }
+}
+
+async fn init_swap(
+    endpoint: Url,
+    token_pair: (String, String),
+    value_pair: (u64, u64),
+) -> Result<()> {
+    let rpc_client = RpcClient::new(endpoint).await?;
+    let rpc = Rpc { rpc_client };
+
+    // TODO: Think about decimals, there has to be some metadata to keep track.
+    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());
+
+    // Connect to darkfid and see if there's available funds.
+    let balance = rpc.balance_of(&token_pair.0).await?;
+    if balance < vp.0 {
+        eprintln!(
+            "Error: There is not enough balance for token \"{}\" in your wallet.",
+            token_pair.0
+        );
+        eprintln!("Available balance is {} ({})", encode_base10(balance, 8), balance);
+        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.
+    // TODO: Implement ^
+    // TODO: Maybe this should be done by the user beforehand?
+
+    // Find a coin to spend
+    let coins = rpc.get_coins_valtok(vp.0, &token_pair.0).await?;
+    if coins.is_empty() {
+        eprintln!("Error: Did not manage to find a coin with enough value to spend");
+        exit(1);
+    }
+
+    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);
+
+    // Fetch our default address
+    let our_address = rpc.wallet_address().await?;
+    let our_publickey = match PublicKey::try_from(our_address) {
+        Ok(v) => v,
+        Err(e) => {
+            eprintln!("Error converting our address into PublicKey: {}", e);
+            exit(1);
+        }
+    };
+
+    // Build proving keys
+    let pb = progress_bar("Building proving key for the mint contract");
+    let mint_pk = ProvingKey::build(8, &MintContract::default());
+    pb.finish();
+
+    let pb = progress_bar("Building proving key for the burn contract");
+    let burn_pk = ProvingKey::build(11, &BurnContract::default());
+    pb.finish();
+
+    // The coin we want to receive.
+    let recv_value_blind = DrkValueBlind::random(&mut OsRng);
+    let recv_token_blind = DrkValueBlind::random(&mut OsRng);
+    let recv_coin_blind = DrkCoinBlind::random(&mut OsRng);
+    let recv_serial = DrkSerial::random(&mut OsRng);
+
+    let pb = progress_bar("Building mint proof for receiving coin");
+    let (mint_proof, mint_revealed) = create_mint_proof(
+        &mint_pk,
+        vp.1,
+        tp.1,
+        recv_value_blind,
+        recv_token_blind,
+        recv_serial,
+        recv_coin_blind,
+        our_publickey,
+    )?;
+    pb.finish();
+
+    // The coin we are spending.
+    // We'll spend the first one we've found.
+    let coin = coins[0];
+
+    let pb = progress_bar("Building burn proof for spending coin");
+    let signature_secret = SecretKey::random(&mut OsRng);
+    let merkle_path = match rpc.get_merkle_path(usize::from(coin.leaf_position)).await {
+        Ok(v) => v,
+        Err(e) => {
+            eprintln!("Failed to get merkle path for our coin from darkfid RPC: {}", e);
+            exit(1);
+        }
+    };
+
+    let (burn_proof, burn_revealed) = create_burn_proof(
+        &burn_pk,
+        vp.0,
+        tp.0,
+        coin.note.value_blind,
+        coin.note.token_blind,
+        coin.note.serial,
+        coin.note.coin_blind,
+        coin.secret,
+        coin.leaf_position,
+        merkle_path,
+        signature_secret,
+    )?;
+    pb.finish();
+
+    // Pack proofs together with pedersen commitment openings so
+    // counterparty can verify correctness.
+    let swap_data = SwapData {
+        mint_proof,
+        mint_revealed,
+        mint_value: vp.1,
+        mint_token: tp.1,
+        mint_value_blind: recv_value_blind,
+        mint_token_blind: recv_token_blind,
+        burn_proof,
+        burn_value: vp.0,
+        burn_token: tp.0,
+        burn_revealed,
+        burn_value_blind: coin.note.value_blind,
+        burn_token_blind: coin.note.token_blind,
+    };
+
+    // Print encoded data.
+    println!("{}", bs58::encode(serialize(&swap_data)).into_string());
+
+    Ok(())
+}
+
+fn inspect(data: &str) -> Result<()> {
+    let mut mint_valid = false;
+    let mut burn_valid = false;
+    let mut mint_value_valid = false;
+    let mut mint_token_valid = false;
+    let mut burn_value_valid = false;
+    let mut burn_token_valid = false;
+
+    let bytes = match bs58::decode(data).into_vec() {
+        Ok(v) => v,
+        Err(e) => {
+            eprintln!("Error decoding base58 data from input: {}", e);
+            exit(1);
+        }
+    };
+
+    let sd: SwapData = match deserialize(&bytes) {
+        Ok(v) => v,
+        Err(e) => {
+            eprintln!("Error: Failed to deserialize swap data into struct: {}", e);
+            exit(1);
+        }
+    };
+
+    eprintln!("Successfully decoded data into SwapData struct");
+
+    // Build verifying keys
+    let pb = progress_bar("Building verifying key for the mint contract");
+    let mint_vk = VerifyingKey::build(8, &MintContract::default());
+    pb.finish();
+
+    let pb = progress_bar("Building verifying key for the burn contract");
+    let burn_vk = VerifyingKey::build(11, &BurnContract::default());
+    pb.finish();
+
+    let pb = progress_bar("Verifying burn proof");
+    if verify_burn_proof(&burn_vk, &sd.burn_proof, &sd.burn_revealed).is_ok() {
+        burn_valid = true;
+    }
+    pb.finish();
+
+    let pb = progress_bar("Verifying mint proof");
+    if verify_mint_proof(&mint_vk, &sd.mint_proof, &sd.mint_revealed).is_ok() {
+        mint_valid = true;
+    }
+    pb.finish();
+
+    eprintln!("  Verifying pedersen commitments");
+
+    if pedersen_commitment_u64(sd.burn_value, sd.burn_value_blind) == sd.burn_revealed.value_commit
+    {
+        burn_value_valid = true;
+    }
+
+    if pedersen_commitment_base(sd.burn_token, sd.burn_token_blind) == sd.burn_revealed.token_commit
+    {
+        burn_token_valid = true;
+    }
+
+    if pedersen_commitment_u64(sd.mint_value, sd.mint_value_blind) == sd.mint_revealed.value_commit
+    {
+        mint_value_valid = true;
+    }
+
+    if pedersen_commitment_base(sd.mint_token, sd.mint_token_blind) == sd.mint_revealed.token_commit
+    {
+        mint_token_valid = true;
+    }
+
+    let mut valid = true;
+    eprintln!("Summary:");
+
+    eprint!("  Burn proof: ");
+    if burn_valid {
+        eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
+    } else {
+        eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
+        valid = false;
+    }
+
+    eprint!("  Burn proof value commitment: ");
+    if burn_value_valid {
+        eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
+    } else {
+        eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
+        valid = false;
+    }
+
+    eprint!("  Burn proof token commitment: ");
+    if burn_token_valid {
+        eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
+    } else {
+        eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
+        valid = false;
+    }
+
+    eprint!("  Mint proof: ");
+    if mint_valid {
+        eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
+    } else {
+        eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
+        valid = false;
+    }
+
+    eprint!("  Mint proof value commitment: ");
+    if mint_value_valid {
+        eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
+    } else {
+        eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
+        valid = false;
+    }
+
+    eprint!("  Mint proof token commitment: ");
+    if mint_token_valid {
+        eprintln!("{}VALID{}", color::Fg(color::Green), color::Fg(color::Reset));
+    } else {
+        eprintln!("{}INVALID{}", color::Fg(color::Red), color::Fg(color::Reset));
+        valid = false;
+    }
+
+    eprintln!("========================================");
+
+    eprintln!(
+        "Mint: {} {}",
+        encode_base10(sd.mint_value, 8),
+        bs58::encode(sd.mint_token.to_repr()).into_string()
+    );
+    eprintln!(
+        "Burn: {} {}",
+        encode_base10(sd.burn_value, 8),
+        bs58::encode(sd.burn_token.to_repr()).into_string()
+    );
+
+    if !valid {
+        eprintln!(
+            "\nThe ZK proofs and commitments inspected are {}NOT VALID{}",
+            color::Fg(color::Red),
+            color::Fg(color::Reset)
+        );
+        exit(1);
+    } else {
+        eprintln!(
+            "\nThe ZK proofs and commitments inspected are {}VALID{}",
+            color::Fg(color::Green),
+            color::Fg(color::Reset)
+        );
+    }
+
+    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_std::main]
+async fn main() -> Result<()> {
+    let args = Args::parse();
+
+    match args.command {
+        Subcmd::Init { token_pair, value_pair } => {
+            let token_pair = parse_token_pair(&token_pair)?;
+            let value_pair = parse_value_pair(&value_pair)?;
+            init_swap(args.endpoint, token_pair, value_pair).await
+        }
+        Subcmd::Inspect => {
+            let mut buf = String::new();
+            stdin().read_to_string(&mut buf)?;
+            inspect(&buf.trim())
+        }
+    }
+}

+ 4 - 6
example/tx.rs

@@ -1,5 +1,6 @@
 // Example transaction flow
 use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
+use pasta_curves::{group::ff::Field, pallas};
 use rand::rngs::OsRng;
 
 use darkfi::{
@@ -10,7 +11,6 @@ use darkfi::{
         note::{EncryptedNote, Note},
         nullifier::Nullifier,
         proof::{ProvingKey, VerifyingKey},
-        token_id::generate_id,
         OwnCoin, OwnCoins,
     },
     node::state::{state_transition, ProgramState, StateUpdate},
@@ -18,7 +18,6 @@ use darkfi::{
         TransactionBuilder, TransactionBuilderClearInputInfo, TransactionBuilderInputInfo,
         TransactionBuilderOutputInfo,
     },
-    util::NetworkName,
     zk::circuit::{BurnContract, MintContract},
     Result,
 };
@@ -129,7 +128,7 @@ fn main() -> Result<()> {
 
     let keypair = Keypair::random(&mut OsRng);
 
-    let mint_vk = VerifyingKey::build(8, &MintContract::default());
+    let mint_vk = VerifyingKey::build(11, &MintContract::default());
     let burn_vk = VerifyingKey::build(11, &BurnContract::default());
 
     let mut state = MemoryState {
@@ -144,8 +143,7 @@ fn main() -> Result<()> {
         secrets: vec![keypair.secret],
     };
 
-    let token_id =
-        generate_id(&NetworkName::Solana, "So11111111111111111111111111111111111111112")?;
+    let token_id = pallas::Base::random(&mut OsRng);
 
     let builder = TransactionBuilder {
         clear_inputs: vec![TransactionBuilderClearInputInfo {
@@ -161,7 +159,7 @@ fn main() -> Result<()> {
         }],
     };
 
-    let mint_pk = ProvingKey::build(8, &MintContract::default());
+    let mint_pk = ProvingKey::build(11, &MintContract::default());
     let burn_pk = ProvingKey::build(11, &BurnContract::default());
     let tx = builder.build(&mint_pk, &burn_pk)?;
 

+ 1 - 1
proof/burn.zk

@@ -34,7 +34,7 @@ circuit "Burn" {
 	constrain_instance(value_commit_y);
 
 	# Pedersen commitment for coin's token ID
-	tcv = ec_mul_short(token, VALUE_COMMIT_VALUE);
+	tcv = ec_mul_base(token, NULLIFIER_K);
 	tcr = ec_mul(token_blind, VALUE_COMMIT_RANDOM);
 	token_commit = ec_add(tcv, tcr);
 	# Since token_commit is also a curve point, we'll do the same

+ 2 - 1
proof/mint.zk

@@ -1,6 +1,7 @@
 constant "Mint" {
 	EcFixedPointShort VALUE_COMMIT_VALUE,
 	EcFixedPoint VALUE_COMMIT_RANDOM,
+	EcFixedPointBase NULLIFIER_K,
 }
 
 contract "Mint" {
@@ -31,7 +32,7 @@ circuit "Mint" {
 	constrain_instance(value_commit_y);
 
 	# Pedersen commitment for coin's token ID
-	tcv = ec_mul_short(token, VALUE_COMMIT_VALUE);
+	tcv = ec_mul_base(token, NULLIFIER_K);
 	tcr = ec_mul(token_blind, VALUE_COMMIT_RANDOM);
 	token_commit = ec_add(tcv, tcr);
 	# Since token_commit is also a curve point, we'll do the same

+ 2 - 2
src/crypto/burn_proof.rs

@@ -10,7 +10,7 @@ use rand::rngs::OsRng;
 use super::{
     nullifier::Nullifier,
     proof::{Proof, ProvingKey, VerifyingKey},
-    util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
+    util::{pedersen_commitment_base, pedersen_commitment_u64},
 };
 use crate::{
     crypto::{
@@ -76,7 +76,7 @@ impl BurnRevealedValues {
         };
 
         let value_commit = pedersen_commitment_u64(value, value_blind);
-        let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
+        let token_commit = pedersen_commitment_base(token_id, token_blind);
 
         BurnRevealedValues {
             value_commit,

+ 5 - 5
src/crypto/keypair.rs

@@ -263,7 +263,7 @@ impl<'de> serde::Deserialize<'de> for PublicKey {
 mod tests {
     use super::*;
     use crate::{
-        crypto::util::pedersen_commitment_scalar,
+        crypto::util::pedersen_commitment_base,
         util::serial::{deserialize, serialize},
     };
 
@@ -291,15 +291,15 @@ mod tests {
         );
         assert_eq!(deserialize(&serialized).ok(), Some(fourtwenty));
 
-        let a = pallas::Scalar::from(420);
+        let a = pallas::Base::from(420);
         let b = pallas::Scalar::from(69);
-        let pc: pallas::Point = pedersen_commitment_scalar(a, b);
+        let pc: pallas::Point = pedersen_commitment_base(a, b);
         let serialized = serialize(&pc);
         assert_eq!(
             serialized,
             vec![
-                55, 48, 126, 42, 114, 27, 18, 55, 155, 141, 83, 75, 44, 50, 244, 223, 254, 216, 22,
-                167, 208, 59, 212, 201, 150, 149, 96, 207, 216, 74, 60, 131
+                57, 232, 32, 239, 229, 119, 41, 70, 218, 174, 237, 25, 122, 81, 81, 252, 54, 192,
+                225, 207, 145, 124, 177, 46, 28, 37, 55, 70, 6, 33, 51, 42,
             ]
         );
         assert_eq!(deserialize(&serialized).ok(), Some(pc));

+ 2 - 2
src/crypto/mint_proof.rs

@@ -12,7 +12,7 @@ use crate::{
         keypair::PublicKey,
         proof::{Proof, ProvingKey, VerifyingKey},
         types::{DrkCoinBlind, DrkSerial, DrkTokenId, DrkValue, DrkValueBlind, DrkValueCommit},
-        util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
     },
     util::serial::{SerialDecodable, SerialEncodable},
     zk::circuit::mint_contract::MintContract,
@@ -37,7 +37,7 @@ impl MintRevealedValues {
         public_key: PublicKey,
     ) -> Self {
         let value_commit = pedersen_commitment_u64(value, value_blind);
-        let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
+        let token_commit = pedersen_commitment_base(token_id, token_blind);
 
         let coords = public_key.0.to_affine().coordinates().unwrap();
         let messages =

+ 1 - 1
src/crypto/proof.rs

@@ -119,7 +119,7 @@ mod tests {
     #[test]
     fn test_proof_serialization() -> Result<()> {
         let value = 110_u64;
-        let token_id = DrkTokenId::from(42);
+        let token_id = DrkTokenId::random(&mut OsRng);
         let value_blind = DrkValueBlind::random(&mut OsRng);
         let token_blind = DrkValueBlind::random(&mut OsRng);
         let serial = DrkSerial::random(&mut OsRng);

+ 2 - 4
src/crypto/schnorr.rs

@@ -34,8 +34,7 @@ pub trait SchnorrPublic {
 impl SchnorrSecret for SecretKey {
     fn sign(&self, message: &[u8]) -> Signature {
         let mask = pallas::Scalar::random(&mut OsRng);
-        let nfk = NullifierK;
-        let commit = nfk.generator() * mask;
+        let commit = NullifierK.generator() * mask;
 
         let challenge = hash_to_scalar(DRK_SCHNORR_DOMAIN, &commit.to_bytes(), message);
         let response = mask + challenge * mod_r_p(self.0);
@@ -47,8 +46,7 @@ impl SchnorrSecret for SecretKey {
 impl SchnorrPublic for PublicKey {
     fn verify(&self, message: &[u8], signature: &Signature) -> bool {
         let challenge = hash_to_scalar(DRK_SCHNORR_DOMAIN, &signature.commit.to_bytes(), message);
-        let nfk = NullifierK;
-        nfk.generator() * signature.response - self.0 * challenge == signature.commit
+        NullifierK.generator() * signature.response - self.0 * challenge == signature.commit
     }
 }
 

+ 21 - 8
src/crypto/util.rs

@@ -1,14 +1,21 @@
 use blake2b_simd::Params;
+use halo2_gadgets::ecc::chip::FixedPoint;
 use pasta_curves::{
     arithmetic::{CurveExt, FieldExt},
     group::ff::PrimeField,
     pallas,
 };
 
-use super::constants::fixed_bases::{
-    VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
+use super::{
+    constants::{
+        fixed_bases::{
+            VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
+        },
+        util::gen_const_array,
+        NullifierK,
+    },
+    types::*,
 };
-use crate::crypto::{constants::util::gen_const_array, types::*};
 
 pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> pallas::Scalar {
     let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
@@ -18,18 +25,24 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> pallas::Scalar {
     pallas::Scalar::from_bytes_wide(ret.as_array())
 }
 
-
+/// Pedersen commitment for a full-width base field element.
 #[allow(non_snake_case)]
-pub fn pedersen_commitment_scalar(value: pallas::Scalar, blind: DrkValueBlind) -> DrkValueCommit {
+pub fn pedersen_commitment_base(value: pallas::Base, blind: DrkValueBlind) -> DrkValueCommit {
     let hasher = DrkValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
-    let V = hasher(&VALUE_COMMITMENT_V_BYTES);
+    let V = NullifierK.generator();
     let R = hasher(&VALUE_COMMITMENT_R_BYTES);
 
-    V * value + R * blind
+    V * mod_r_p(value) + R * blind
 }
 
+/// Pedersen commitment for a 64-bit value, in the base field.
+#[allow(non_snake_case)]
 pub fn pedersen_commitment_u64(value: u64, blind: DrkValueBlind) -> DrkValueCommit {
-    pedersen_commitment_scalar(mod_r_p(DrkValue::from(value)), blind)
+    let hasher = DrkValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
+    let V = hasher(&VALUE_COMMITMENT_V_BYTES);
+    let R = hasher(&VALUE_COMMITMENT_R_BYTES);
+
+    V * mod_r_p(DrkValue::from(value)) + R * blind
 }
 
 #[allow(non_snake_case)]

+ 2 - 3
src/tx/mod.rs

@@ -19,7 +19,7 @@ use crate::{
         schnorr,
         schnorr::SchnorrPublic,
         types::{DrkTokenId, DrkValueBlind, DrkValueCommit},
-        util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
         BurnRevealedValues, MintRevealedValues, Proof,
     },
     impl_vec,
@@ -169,8 +169,7 @@ impl Transaction {
 
         failed = failed ||
             self.clear_inputs.iter().any(|input| {
-                pedersen_commitment_scalar(mod_r_p(input.token_id), input.token_blind) !=
-                    token_commit_value
+                pedersen_commitment_base(input.token_id, input.token_blind) != token_commit_value
             });
         !failed
     }

+ 16 - 22
src/zk/circuit/burn_contract.rs

@@ -369,8 +369,7 @@ impl Circuit<pallas::Base> for BurnContract {
 
         // v * G_1
         let (commitment, _) = {
-            let value_commit_v = ValueCommitV;
-            let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), value_commit_v);
+            let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), ValueCommitV);
             let value = ScalarFixedShort::new(
                 ecc_chip.clone(),
                 layouter.namespace(|| "value"),
@@ -386,8 +385,8 @@ impl Circuit<pallas::Base> for BurnContract {
                 layouter.namespace(|| "value_blind"),
                 self.value_blind,
             )?;
-            let value_commit_r = OrchardFixedBasesFull::ValueCommitR;
-            let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
+            let value_commit_r =
+                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
             value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
         };
 
@@ -412,15 +411,9 @@ impl Circuit<pallas::Base> for BurnContract {
             assign_free_advice(layouter.namespace(|| "load token"), config.advices[0], self.token)?;
 
         // a * G_1
-        let (commitment, _) = {
-            let token_commit_v = ValueCommitV;
-            let token_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), token_commit_v);
-            let token = ScalarFixedShort::new(
-                ecc_chip.clone(),
-                layouter.namespace(|| "token"),
-                (token, one),
-            )?;
-            token_commit_v.mul(layouter.namespace(|| "[token] ValueCommitV"), token)?
+        let commitment = {
+            let token_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
+            token_commit_v.mul(layouter.namespace(|| "[token] NullifierK"), token)?
         };
 
         // r_A * G_2
@@ -430,8 +423,8 @@ impl Circuit<pallas::Base> for BurnContract {
                 layouter.namespace(|| "token_blind"),
                 self.token_blind,
             )?;
-            let token_commit_r = OrchardFixedBasesFull::ValueCommitR;
-            let token_commit_r = FixedPoint::from_inner(ecc_chip.clone(), token_commit_r);
+            let token_commit_r =
+                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
             token_commit_r.mul(layouter.namespace(|| "[token_blind] ValueCommitR"), rca)?
         };
 
@@ -488,7 +481,7 @@ mod tests {
         crypto::{
             keypair::{PublicKey, SecretKey},
             proof::{ProvingKey, VerifyingKey},
-            util::{mod_r_p, pedersen_commitment_scalar},
+            util::{pedersen_commitment_base, pedersen_commitment_u64},
             Proof,
         },
         Result,
@@ -506,8 +499,8 @@ mod tests {
 
     #[test]
     fn burn_circuit_assert() -> Result<()> {
-        let value = pallas::Base::from(42);
-        let token_id = pallas::Base::from(22);
+        let value = 42;
+        let token_id = pallas::Base::random(&mut OsRng);
         let value_blind = pallas::Scalar::random(&mut OsRng);
         let token_blind = pallas::Scalar::random(&mut OsRng);
         let serial = pallas::Base::random(&mut OsRng);
@@ -517,7 +510,8 @@ mod tests {
 
         let coin2 = {
             let coords = PublicKey::from_secret(secret).0.to_affine().coordinates().unwrap();
-            let msg = [*coords.x(), *coords.y(), value, token_id, serial, coin_blind];
+            let msg =
+                [*coords.x(), *coords.y(), pallas::Base::from(value), token_id, serial, coin_blind];
             poseidon::Hash::<_, P128Pow5T3, ConstantLength<6>, 3, 2>::init().hash(msg)
         };
 
@@ -542,10 +536,10 @@ mod tests {
         let nullifier =
             poseidon::Hash::<_, P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(nullifier);
 
-        let value_commit = pedersen_commitment_scalar(mod_r_p(value), value_blind);
+        let value_commit = pedersen_commitment_u64(value, value_blind);
         let value_coords = value_commit.to_affine().coordinates().unwrap();
 
-        let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
+        let token_commit = pedersen_commitment_base(token_id, token_blind);
         let token_coords = token_commit.to_affine().coordinates().unwrap();
 
         let sig_pubkey = PublicKey::from_secret(sig_secret);
@@ -565,7 +559,7 @@ mod tests {
         let circuit = BurnContract {
             secret_key: Value::known(secret.0),
             serial: Value::known(serial),
-            value: Value::known(value),
+            value: Value::known(pallas::Base::from(value)),
             token: Value::known(token_id),
             coin_blind: Value::known(coin_blind),
             value_blind: Value::known(value_blind),

+ 48 - 32
src/zk/circuit/mint_contract.rs

@@ -1,23 +1,27 @@
 use halo2_gadgets::{
     ecc::{
         chip::{EccChip, EccConfig},
-        FixedPoint, FixedPointShort, ScalarFixed, ScalarFixedShort,
+        FixedPoint, FixedPointBaseField, FixedPointShort, ScalarFixed, ScalarFixedShort,
     },
     poseidon::{
         primitives as poseidon, Hash as PoseidonHash, Pow5Chip as PoseidonChip,
         Pow5Config as PoseidonConfig,
     },
+    sinsemilla::chip::{SinsemillaChip, SinsemillaConfig},
     utilities::lookup_range_check::LookupRangeCheckConfig,
 };
 use halo2_proofs::{
     circuit::{floor_planner, AssignedCell, Layouter, Value},
+    pasta::{pallas, Fp},
     plonk,
     plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn},
 };
-use pasta_curves::{pallas, Fp};
 
 use crate::{
-    crypto::constants::{OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV},
+    crypto::constants::{
+        sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
+        NullifierK, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV,
+    },
     zk::assign_free_advice,
 };
 
@@ -27,6 +31,8 @@ pub struct MintConfig {
     advices: [Column<Advice>; 10],
     ecc_config: EccConfig<OrchardFixedBases>,
     poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
+    sinsemilla_config:
+        SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
 }
 
 impl MintConfig {
@@ -89,7 +95,9 @@ impl Circuit<pallas::Base> for MintContract {
             meta.advice_column(),
         ];
 
+        // Fixed columns for the Sinsemilla generator lookup table
         let table_idx = meta.lookup_table_column();
+        let lookup = (table_idx, meta.lookup_table_column(), meta.lookup_table_column());
 
         // Instance column used for public inputs
         let primary = meta.instance_column();
@@ -147,7 +155,16 @@ impl Circuit<pallas::Base> for MintContract {
             rc_b,
         );
 
-        MintConfig { primary, advices, ecc_config, poseidon_config }
+        let sinsemilla_config = SinsemillaChip::configure(
+            meta,
+            advices[..5].try_into().unwrap(),
+            advices[6],
+            ecc_lagrange_coeffs[0],
+            lookup,
+            range_check,
+        );
+
+        MintConfig { primary, advices, ecc_config, poseidon_config, sinsemilla_config }
     }
 
     fn synthesize(
@@ -155,6 +172,9 @@ impl Circuit<pallas::Base> for MintContract {
         config: Self::Config,
         mut layouter: impl Layouter<pallas::Base>,
     ) -> Result<(), plonk::Error> {
+        // Load the Sinsemilla generator lookup table used by the whole circuit.
+        SinsemillaChip::load(config.sinsemilla_config.clone(), &mut layouter)?;
+
         let ecc_chip = config.ecc_chip();
 
         let pub_x = assign_free_advice(
@@ -227,8 +247,7 @@ impl Circuit<pallas::Base> for MintContract {
 
         // v * G_1
         let (commitment, _) = {
-            let value_commit_v = ValueCommitV;
-            let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), value_commit_v);
+            let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), ValueCommitV);
             let value = ScalarFixedShort::new(
                 ecc_chip.clone(),
                 layouter.namespace(|| "value"),
@@ -244,8 +263,8 @@ impl Circuit<pallas::Base> for MintContract {
                 layouter.namespace(|| "value_blind"),
                 self.value_blind,
             )?;
-            let value_commit_r = OrchardFixedBasesFull::ValueCommitR;
-            let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
+            let value_commit_r =
+                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
             value_commit_r.mul(layouter.namespace(|| "[value_blind] ValueCommitR"), rcv)?
         };
 
@@ -268,15 +287,9 @@ impl Circuit<pallas::Base> for MintContract {
         // Token commitment
         // ================
         // a * G_1
-        let (commitment, _) = {
-            let token_commit_v = ValueCommitV;
-            let token_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), token_commit_v);
-            let token = ScalarFixedShort::new(
-                ecc_chip.clone(),
-                layouter.namespace(|| "token"),
-                (token, one),
-            )?;
-            token_commit_v.mul(layouter.namespace(|| "[token] ValueCommitV"), token)?
+        let commitment = {
+            let token_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
+            token_commit_v.mul(layouter.namespace(|| "[token] NullifierK"), token)?
         };
 
         // r_A * G_2
@@ -286,8 +299,8 @@ impl Circuit<pallas::Base> for MintContract {
                 layouter.namespace(|| "token_blind"),
                 self.token_blind,
             )?;
-            let token_commit_r = OrchardFixedBasesFull::ValueCommitR;
-            let token_commit_r = FixedPoint::from_inner(ecc_chip, token_commit_r);
+            let token_commit_r =
+                FixedPoint::from_inner(ecc_chip, OrchardFixedBasesFull::ValueCommitR);
             token_commit_r.mul(layouter.namespace(|| "[token_blind] ValueCommitR"), rca)?
         };
 
@@ -318,12 +331,11 @@ mod tests {
         crypto::{
             keypair::PublicKey,
             proof::{ProvingKey, VerifyingKey},
-            util::{mod_r_p, pedersen_commitment_scalar},
+            util::{pedersen_commitment_base, pedersen_commitment_u64},
             Proof,
         },
         Result,
     };
-    use group::{ff::Field, Curve};
     use halo2_gadgets::poseidon::{
         primitives as poseidon,
         primitives::{ConstantLength, P128Pow5T3},
@@ -332,14 +344,17 @@ mod tests {
         circuit::Value,
         dev::{CircuitLayout, MockProver},
     };
-    use pasta_curves::arithmetic::CurveAffine;
+    use pasta_curves::{
+        arithmetic::CurveAffine,
+        group::{ff::Field, Curve},
+    };
     use rand::rngs::OsRng;
     use std::time::Instant;
 
     #[test]
     fn mint_circuit_assert() -> Result<()> {
-        let value = pallas::Base::from(42);
-        let token_id = pallas::Base::from(22);
+        let value = 42;
+        let token_id = pallas::Base::random(&mut OsRng);
         let value_blind = pallas::Scalar::random(&mut OsRng);
         let token_blind = pallas::Scalar::random(&mut OsRng);
         let serial = pallas::Base::random(&mut OsRng);
@@ -347,13 +362,14 @@ mod tests {
         let public_key = PublicKey::random(&mut OsRng);
         let coords = public_key.0.to_affine().coordinates().unwrap();
 
-        let msg = [*coords.x(), *coords.y(), value, token_id, serial, coin_blind];
+        let msg =
+            [*coords.x(), *coords.y(), pallas::Base::from(value), token_id, serial, coin_blind];
         let coin = poseidon::Hash::<_, P128Pow5T3, ConstantLength<6>, 3, 2>::init().hash(msg);
 
-        let value_commit = pedersen_commitment_scalar(mod_r_p(value), value_blind);
+        let value_commit = pedersen_commitment_u64(value, value_blind);
         let value_coords = value_commit.to_affine().coordinates().unwrap();
 
-        let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
+        let token_commit = pedersen_commitment_base(token_id, token_blind);
         let token_coords = token_commit.to_affine().coordinates().unwrap();
 
         let public_inputs =
@@ -362,7 +378,7 @@ mod tests {
         let circuit = MintContract {
             pub_x: Value::known(*coords.x()),
             pub_y: Value::known(*coords.y()),
-            value: Value::known(value),
+            value: Value::known(pallas::Base::from(value)),
             token: Value::known(token_id),
             serial: Value::known(serial),
             coin_blind: Value::known(coin_blind),
@@ -374,13 +390,13 @@ mod tests {
         let root = BitMapBackend::new("mint_circuit_layout.png", (3840, 2160)).into_drawing_area();
         root.fill(&WHITE).unwrap();
         let root = root.titled("Mint Circuit Layout", ("sans-serif", 60)).unwrap();
-        CircuitLayout::default().render(8, &circuit, &root).unwrap();
+        CircuitLayout::default().render(11, &circuit, &root).unwrap();
 
-        let prover = MockProver::run(8, &circuit, vec![public_inputs.clone()])?;
+        let prover = MockProver::run(11, &circuit, vec![public_inputs.clone()])?;
         prover.assert_satisfied();
 
         let now = Instant::now();
-        let proving_key = ProvingKey::build(8, &circuit);
+        let proving_key = ProvingKey::build(11, &circuit);
         println!("ProvingKey built [{:?}]", now.elapsed());
         let now = Instant::now();
         let proof = Proof::create(&proving_key, &[circuit], &public_inputs, &mut OsRng)?;
@@ -388,7 +404,7 @@ mod tests {
 
         let circuit = MintContract::default();
         let now = Instant::now();
-        let verifying_key = VerifyingKey::build(8, &circuit);
+        let verifying_key = VerifyingKey::build(11, &circuit);
         println!("VerifyingKey built [{:?}]", now.elapsed());
         let now = Instant::now();
         proof.verify(&verifying_key, &public_inputs)?;

+ 2 - 1
src/zk/mod.rs

@@ -15,7 +15,8 @@ use halo2_proofs::{
     plonk::{Advice, Assigned, Column},
 };
 
-pub(in crate::zk) fn assign_free_advice<F: Field, V: Copy>(
+//pub(in crate::zk) fn assign_free_advice<F: Field, V: Copy>(
+pub fn assign_free_advice<F: Field, V: Copy>(
     mut layouter: impl Layouter<F>,
     column: Column<Advice>,
     value: Value<V>,

+ 3 - 3
tests/burn_proof.rs

@@ -3,7 +3,7 @@ use darkfi::{
         keypair::{PublicKey, SecretKey},
         merkle_node::MerkleNode,
         proof::{ProvingKey, VerifyingKey},
-        util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
         Proof,
     },
     zk::{
@@ -35,7 +35,7 @@ fn burn_proof() -> Result<()> {
 
     // Witness values
     let value = 42;
-    let token_id = pallas::Base::from(22);
+    let token_id = pallas::Base::random(&mut OsRng);
     let value_blind = pallas::Scalar::random(&mut OsRng);
     let token_blind = pallas::Scalar::random(&mut OsRng);
     let serial = pallas::Base::random(&mut OsRng);
@@ -94,7 +94,7 @@ fn burn_proof() -> Result<()> {
     let value_commit = pedersen_commitment_u64(value, value_blind);
     let value_coords = value_commit.to_affine().coordinates().unwrap();
 
-    let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
+    let token_commit = pedersen_commitment_base(token_id, token_blind);
     let token_coords = token_commit.to_affine().coordinates().unwrap();
 
     let sig_pubkey = PublicKey::from_secret(sig_secret);

+ 3 - 3
tests/mint_proof.rs

@@ -2,7 +2,7 @@ use darkfi::{
     crypto::{
         keypair::PublicKey,
         proof::{ProvingKey, VerifyingKey},
-        util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_u64},
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
         Proof,
     },
     zk::{
@@ -33,7 +33,7 @@ fn mint_proof() -> Result<()> {
 
     // Witness values
     let value = 42;
-    let token_id = pallas::Base::from(22);
+    let token_id = pallas::Base::random(&mut OsRng);
     let value_blind = pallas::Scalar::random(&mut OsRng);
     let token_blind = pallas::Scalar::random(&mut OsRng);
     let serial = pallas::Base::random(&mut OsRng);
@@ -60,7 +60,7 @@ fn mint_proof() -> Result<()> {
     let value_commit = pedersen_commitment_u64(value, value_blind);
     let value_coords = value_commit.to_affine().coordinates().unwrap();
 
-    let token_commit = pedersen_commitment_scalar(mod_r_p(token_id), token_blind);
+    let token_commit = pedersen_commitment_base(token_id, token_blind);
     let token_coords = token_commit.to_affine().coordinates().unwrap();
 
     let public_inputs =