cherry picked fullwidth pedersen

This commit is contained in:
parazyd
2022-08-08 15:00:41 +02:00
committed by mohab metwally
parent 95a048ab23
commit 3686c32591
17 changed files with 631 additions and 97 deletions

View File

@@ -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
bin/darkotc/src/main.rs Normal file
View File

@@ -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())
}
}
}

View File

@@ -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)?;

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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));

View File

@@ -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 =

View File

@@ -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);

View File

@@ -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
}
}

View File

@@ -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 = NullifierK.generator();
let R = hasher(&VALUE_COMMITMENT_R_BYTES);
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 {
let hasher = DrkValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let V = hasher(&VALUE_COMMITMENT_V_BYTES);
let R = hasher(&VALUE_COMMITMENT_R_BYTES);
V * value + R * blind
}
pub fn pedersen_commitment_u64(value: u64, blind: DrkValueBlind) -> DrkValueCommit {
pedersen_commitment_scalar(mod_r_p(DrkValue::from(value)), blind)
V * mod_r_p(DrkValue::from(value)) + R * blind
}
#[allow(non_snake_case)]

View File

@@ -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
}

View File

@@ -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),

View File

@@ -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)?;

View File

@@ -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>,

View File

@@ -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);

View File

@@ -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 =