Minor cleanup.

This commit is contained in:
parazyd
2021-12-02 19:46:32 +01:00
parent dce2a3294c
commit 2894c9fc7d
5 changed files with 85 additions and 457 deletions

View File

@@ -1,27 +1,24 @@
use bellman::groth16;
use bls12_381::Bls12;
use ff::{Field, PrimeField};
use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Tree};
use pasta_curves::pallas;
use rand::rngs::OsRng;
use std::path::Path;
use drk::{
circuit::{mint_contract::MintContract, spend_contract::SpendContract},
crypto::{
coin::Coin,
load_params,
merkle::{CommitmentTree, IncrementalWitness},
keypair::{Keypair, PublicKey, SecretKey},
merkle_node::MerkleNode,
note::{EncryptedNote, Note},
nullifier::Nullifier,
save_params, setup_mint_prover, setup_spend_prover,
proof::{ProvingKey, VerifyingKey},
},
serial::{Decodable, Encodable},
state::{ProgramState, StateUpdate},
tx,
state::{state_transition, ProgramState, StateUpdate},
tx, Result,
};
struct MemoryState {
// The entire merkle tree state
tree: CommitmentTree<MerkleNode>,
tree: BridgeTree<MerkleNode, 32>,
// List of all previous and the current merkle roots
// This is the hashed value of all the children.
merkle_roots: Vec<MerkleNode>,
@@ -32,35 +29,35 @@ struct MemoryState {
// Maybe the spend field links to a tx hash:input index
// We should also keep track of the tx hash:output index where this
// coin was received
own_coins: Vec<(Coin, Note, jubjub::Fr, IncrementalWitness<MerkleNode>)>,
// Mint verifying key used by ZK
mint_pvk: groth16::PreparedVerifyingKey<Bls12>,
// Spend verifying key used by ZK
spend_pvk: groth16::PreparedVerifyingKey<Bls12>,
own_coins: Vec<(Coin, Note)>,
mint_vk: VerifyingKey,
spend_vk: VerifyingKey,
// Public key of the cashier
cashier_public: jubjub::SubgroupPoint,
cashier_signature_public: PublicKey,
// List of all our secret keys
secrets: Vec<jubjub::Fr>,
secrets: Vec<SecretKey>,
}
impl ProgramState for MemoryState {
fn is_valid_cashier_public_key(&self, public: &jubjub::SubgroupPoint) -> bool {
public == &self.cashier_public
}
fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool {
self.merkle_roots.iter().any(|m| *m == *merkle_root)
}
fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
self.nullifiers.iter().any(|n| n.repr == nullifier.repr)
fn is_valid_cashier_public_key(&self, public: &PublicKey) -> bool {
public == &self.cashier_signature_public
}
fn mint_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12> {
&self.mint_pvk
fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool {
self.merkle_roots.iter().any(|m| m == merkle_root)
}
fn spend_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12> {
&self.spend_pvk
fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
self.nullifiers.iter().any(|n| n == nullifier)
}
fn mint_vk(&self) -> &VerifyingKey {
&self.mint_vk
}
fn spend_vk(&self) -> &VerifyingKey {
&self.spend_vk
}
}
@@ -72,35 +69,20 @@ impl MemoryState {
// Update merkle tree and witnesses
for (coin, enc_note) in update.coins.into_iter().zip(update.enc_notes.into_iter()) {
// Add the new coins to the merkle tree
let node = MerkleNode::from_coin(&coin);
self.tree.append(node).expect("Append to merkle tree");
let node = MerkleNode(coin.0);
self.tree.append(&node);
// Keep track of all merkle roots that have existed
self.merkle_roots.push(self.tree.root());
// Also update all the coin witnesses
for (_, _, _, witness) in self.own_coins.iter_mut() {
witness.append(node).expect("append to witness");
}
if let Some((note, secret)) = self.try_decrypt_note(enc_note) {
// We need to keep track of the witness for this coin.
// This allows us to prove inclusion of the coin in the merkle tree with ZK.
// Just as we update the merkle tree with every new coin, so we do the same with
// the witness.
// Derive the current witness from the current tree.
// This is done right after we add our coin to the tree (but before any other
// coins are added)
// Make a new witness for this coin
let witness = IncrementalWitness::from_tree(&self.tree);
self.own_coins.push((coin, note, secret, witness));
if let Some((note, _secret)) = self.try_decrypt_note(enc_note) {
self.own_coins.push((coin, note));
self.tree.witness();
}
}
}
fn try_decrypt_note(&self, ciphertext: EncryptedNote) -> Option<(Note, jubjub::Fr)> {
fn try_decrypt_note(&self, ciphertext: EncryptedNote) -> Option<(Note, SecretKey)> {
// Loop through all our secret keys...
for secret in &self.secrets {
// ... attempt to decrypt the note ...
@@ -114,245 +96,78 @@ impl MemoryState {
}
}
#[async_std::main]
async fn main() {
// Auto create trusted ceremony parameters if they don't exist
if !Path::new("mint.params").exists() {
let params = setup_mint_prover();
save_params("mint.params", &params).unwrap();
}
if !Path::new("spend.params").exists() {
let params = setup_spend_prover();
save_params("spend.params", &params).unwrap();
}
fn main() -> Result<()> {
let cashier_signature_secret = SecretKey::random(&mut OsRng);
let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
// Load trusted setup parameters
let (mint_params, mint_pvk) = load_params("mint.params").expect("params should load");
let (spend_params, spend_pvk) = load_params("spend.params").expect("params should load");
let keypair = Keypair::random(&mut OsRng);
// Cashier creates a secret key
let cashier_secret = jubjub::Fr::random(&mut OsRng);
// This is their public key
let cashier_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * cashier_secret;
// Wallet 1 creates a secret key
let secret = jubjub::Fr::random(&mut OsRng);
// This is their public key
let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
const K: u32 = 11;
let mint_vk = VerifyingKey::build(K, MintContract::default());
let spend_vk = VerifyingKey::build(K, SpendContract::default());
let mut state = MemoryState {
tree: CommitmentTree::empty(),
tree: BridgeTree::<MerkleNode, 32>::new(100),
merkle_roots: vec![],
nullifiers: vec![],
own_coins: vec![],
mint_pvk,
spend_pvk,
cashier_public,
secrets: vec![secret],
mint_vk,
spend_vk,
cashier_signature_public,
secrets: vec![keypair.secret],
};
// Step 1: Cashier deposits to wallet1's address
let token_id = pallas::Base::from(110);
// Create the deposit for 110 BTC
// Clear inputs are visible to everyone on the network
let token_id = jubjub::Fr::random(&mut OsRng);
let builder = tx::TransactionBuilder {
clear_inputs: vec![tx::TransactionBuilderClearInputInfo {
value: 110,
token_id,
signature_secret: cashier_secret,
signature_secret: cashier_signature_secret,
}],
inputs: vec![],
outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, token_id, public }],
outputs: vec![tx::TransactionBuilderOutputInfo {
value: 110,
token_id,
public: keypair.public,
}],
};
// We will 'compile' the tx, and then serialize it to this Vec<u8>
let mut tx_data = vec![];
{
// Build the tx
let tx = builder.build(&mint_params, &spend_params);
// Now serialize it
tx.encode(&mut tx_data).expect("encode tx");
}
let mint_pk = ProvingKey::build(K, MintContract::default());
let spend_pk = ProvingKey::build(K, SpendContract::default());
let tx = builder.build(&mint_pk, &spend_pk)?;
// Step 1 is completed.
// Tx data is posted to the blockchain
tx.verify(&state.mint_vk, &state.spend_vk).expect("tx verify");
// Step 2: wallet1 receive's payment from the cashier
let _note = tx.outputs[0].enc_note.decrypt(&keypair.secret)?;
// Wallet1 is receiving tx, and for every new coin it finds, it adds to its
// merkle tree
{
// Here we simulate 5 fake random coins, adding them to our tree.
let tree = &mut state.tree;
for _i in 0..5 {
// Don't worry about any of the code in this block
// We're just filling the tree with fake coins
let cmu = MerkleNode::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
tree.append(cmu).unwrap();
let update = state_transition(&state, tx)?;
state.apply(update);
let root = tree.root();
state.merkle_roots.push(root);
}
}
// Now spend
let (coin, note) = &state.own_coins[0];
let node = MerkleNode(coin.0.clone());
let (leaf_position, merkle_path) = state.tree.authentication_path(&node).unwrap();
// Now we receive the tx data
{
let tx = tx::Transaction::decode(&tx_data[..]).unwrap();
let update = state_transition(&state, tx).expect("step 2 state transition failed");
// Our state impl is memory online for this demo
// but in the real version, this function will be async
// and using the databases.
state.apply(update);
}
//// Wallet1 has received payment from the cashier.
//// Step 2 is complete.
assert_eq!(state.own_coins.len(), 1);
////let (coin, note, secret, witness) = &mut state.own_coins[0];
let merkle_path = {
let tree = &mut state.tree;
let (coin, _, _, witness) = &mut state.own_coins[0];
// Check this is the 6th coin we added
assert_eq!(witness.position(), 5);
assert_eq!(tree.root(), witness.root());
// Add some more random coins in
for _i in 0..10 {
// Don't worry about any of the code in this block
// We're just filling the tree with fake coins
let cmu = MerkleNode::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
tree.append(cmu).unwrap();
witness.append(cmu).unwrap();
assert_eq!(tree.root(), witness.root());
let root = tree.root();
state.merkle_roots.push(root);
}
assert_eq!(state.merkle_roots.len(), 16);
// This is the value we need to spend the coin
// We use the witness and the merkle root (both in sync with each other)
// to prove our coin exists inside the tree.
// The coin is not revealed publicly but is proved to exist inside
// a merkle tree. Only the root will be revealed, and then the
// verifier checks that merkle root actually existed before.
let merkle_path = witness.path().unwrap();
// Just test the path is good because we just added a bunch of fake coins
let node = MerkleNode::from_coin(coin);
let root = tree.root();
//drop(tree);
//drop(witness);
assert_eq!(merkle_path.root(node), root);
let root = root;
assert!(state.is_valid_merkle(&root));
merkle_path
};
// Step 3: wallet1 sends payment to wallet2
// Wallet1 now wishes to send the coin to wallet2
// The receiving wallet has a secret key
let secret2 = jubjub::Fr::random(&mut OsRng);
// This is their public key to receive payment
let public2 = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret2;
// Make a spend tx
//let inputs: Vec<tx::TransactionBuilderInputInfo> = vec![];
// Construct a new tx spending the coin
// We need the decrypted note and our private key
let builder = tx::TransactionBuilder {
clear_inputs: vec![],
inputs: vec![tx::TransactionBuilderInputInfo {
leaf_position,
merkle_path,
secret,
note: state.own_coins[0].1.clone(),
secret: keypair.secret,
note: note.clone(),
}],
outputs: vec![tx::TransactionBuilderOutputInfo {
value: 110,
token_id,
public: keypair.public,
}],
// We can add more outputs to this list.
// The only constraint is that sum(value in) == sum(value out)
outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, token_id, public: public2 }],
};
// Build the tx
let mut tx_data = vec![];
{
let tx = builder.build(&mint_params, &spend_params);
tx.encode(&mut tx_data).expect("encode tx");
}
// Verify it's valid
{
let tx = tx::Transaction::decode(&tx_data[..]).unwrap();
let update = state_transition(&state, tx).expect("step 3 state transition failed");
state.apply(update);
}
}
use drk::state::{VerifyFailed, VerifyResult};
use log::*;
pub fn state_transition<S: ProgramState>(
state: &S,
tx: tx::Transaction,
) -> VerifyResult<StateUpdate> {
// Check deposits are legit
debug!(target: "STATE TRANSITION", "iterate clear_inputs");
for (i, input) in tx.clear_inputs.iter().enumerate() {
// Check the public key in the clear inputs
// It should be a valid public key for the cashier
if !state.is_valid_cashier_public_key(&input.signature_public) {
log::error!(target: "STATE TRANSITION", "Not valid cashier public key");
return Err(VerifyFailed::InvalidCashierKey(i))
}
}
debug!(target: "STATE TRANSITION", "iterate inputs");
for (i, input) in tx.inputs.iter().enumerate() {
// Check merkle roots
let merkle = &input.revealed.merkle_root;
// Merkle is used to know whether this is a coin that existed
// in a previous state.
if !state.is_valid_merkle(merkle) {
return Err(VerifyFailed::InvalidMerkle(i))
}
// The nullifiers should not already exist
// It is double spend protection.
let nullifier = &input.revealed.nullifier;
if state.nullifier_exists(nullifier) {
return Err(VerifyFailed::DuplicateNullifier(i))
}
}
debug!(target: "STATE TRANSITION", "Check the tx Verifies correctly");
// Check the tx verifies correctly
tx.verify(state.mint_pvk(), state.spend_pvk())?;
let mut nullifiers = vec![];
for input in tx.inputs {
nullifiers.push(input.revealed.nullifier);
}
// Newly created coins for this tx
let mut coins = vec![];
let mut enc_notes = vec![];
for output in tx.outputs {
// Gather all the coins
coins.push(Coin::new(output.revealed.coin));
enc_notes.push(output.enc_note);
}
Ok(StateUpdate { nullifiers, coins, enc_notes })
let tx = builder.build(&mint_pk, &spend_pk)?;
let update = state_transition(&state, tx)?;
state.apply(update);
Ok(())
}

View File

@@ -1,173 +0,0 @@
use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Tree};
use pasta_curves::pallas;
use rand::rngs::OsRng;
use drk::{
circuit::{mint_contract::MintContract, spend_contract::SpendContract},
crypto::{
coin::Coin,
keypair::{Keypair, PublicKey, SecretKey},
merkle_node::MerkleNode,
note::{EncryptedNote, Note},
nullifier::Nullifier,
proof::{ProvingKey, VerifyingKey},
},
state::{state_transition, ProgramState, StateUpdate},
tx, Result,
};
struct MemoryState {
// The entire merkle tree state
tree: BridgeTree<MerkleNode, 32>,
// List of all previous and the current merkle roots
// This is the hashed value of all the children.
merkle_roots: Vec<MerkleNode>,
// Nullifiers prevent double spending
nullifiers: Vec<Nullifier>,
// All received coins
// NOTE: we need maybe a flag to keep track of which ones are spent
// Maybe the spend field links to a tx hash:input index
// We should also keep track of the tx hash:output index where this
// coin was received
own_coins: Vec<(Coin, Note)>,
mint_vk: VerifyingKey,
spend_vk: VerifyingKey,
// Public key of the cashier
cashier_signature_public: PublicKey,
// List of all our secret keys
secrets: Vec<SecretKey>,
}
impl ProgramState for MemoryState {
fn is_valid_cashier_public_key(&self, public: &PublicKey) -> bool {
public == &self.cashier_signature_public
}
fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool {
self.merkle_roots.iter().any(|m| m == merkle_root)
}
fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
self.nullifiers.iter().any(|n| n == nullifier)
}
fn mint_vk(&self) -> &VerifyingKey {
&self.mint_vk
}
fn spend_vk(&self) -> &VerifyingKey {
&self.spend_vk
}
}
impl MemoryState {
fn apply(&mut self, mut update: StateUpdate) {
// Extend our list of nullifiers with the ones from the update
self.nullifiers.append(&mut update.nullifiers);
// Update merkle tree and witnesses
for (coin, enc_note) in update.coins.into_iter().zip(update.enc_notes.into_iter()) {
// Add the new coins to the merkle tree
let node = MerkleNode(coin.0);
self.tree.append(&node);
// Keep track of all merkle roots that have existed
self.merkle_roots.push(self.tree.root());
if let Some((note, _secret)) = self.try_decrypt_note(enc_note) {
self.own_coins.push((coin, note));
self.tree.witness();
}
}
}
fn try_decrypt_note(&self, ciphertext: EncryptedNote) -> Option<(Note, SecretKey)> {
// Loop through all our secret keys...
for secret in &self.secrets {
// ... attempt to decrypt the note ...
if let Ok(note) = ciphertext.decrypt(secret) {
// ... and return the decrypted note for this coin.
return Some((note, *secret))
}
}
// We weren't able to decrypt the note with any of our keys.
None
}
}
fn main() -> Result<()> {
let cashier_signature_secret = SecretKey::random(&mut OsRng);
let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
let keypair = Keypair::random(&mut OsRng);
const K: u32 = 11;
let mint_vk = VerifyingKey::build(K, MintContract::default());
let spend_vk = VerifyingKey::build(K, SpendContract::default());
let mut state = MemoryState {
tree: BridgeTree::<MerkleNode, 32>::new(100),
merkle_roots: vec![],
nullifiers: vec![],
own_coins: vec![],
mint_vk,
spend_vk,
cashier_signature_public,
secrets: vec![keypair.secret],
};
let token_id = pallas::Base::from(110);
let builder = tx::TransactionBuilder {
clear_inputs: vec![tx::TransactionBuilderClearInputInfo {
value: 110,
token_id,
signature_secret: cashier_signature_secret,
}],
inputs: vec![],
outputs: vec![tx::TransactionBuilderOutputInfo {
value: 110,
token_id,
public: keypair.public,
}],
};
let mint_pk = ProvingKey::build(K, MintContract::default());
let spend_pk = ProvingKey::build(K, SpendContract::default());
let tx = builder.build(&mint_pk, &spend_pk)?;
tx.verify(&state.mint_vk, &state.spend_vk).expect("tx verify");
let _note = tx.outputs[0].enc_note.decrypt(&keypair.secret)?;
let update = state_transition(&state, tx)?;
state.apply(update);
// Now spend
let (coin, note) = &state.own_coins[0];
let node = MerkleNode(coin.0.clone());
let (leaf_position, merkle_path) = state.tree.authentication_path(&node).unwrap();
let builder = tx::TransactionBuilder {
clear_inputs: vec![],
inputs: vec![tx::TransactionBuilderInputInfo {
leaf_position,
merkle_path,
secret: keypair.secret,
note: note.clone(),
}],
outputs: vec![tx::TransactionBuilderOutputInfo {
value: 110,
token_id,
public: keypair.public,
}],
};
let tx = builder.build(&mint_pk, &spend_pk)?;
let update = state_transition(&state, tx)?;
state.apply(update);
Ok(())
}

View File

@@ -10,16 +10,16 @@ use super::constants::fixed_bases::{
};
use crate::types::*;
pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> DrkScalar {
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();
hasher.update(a);
hasher.update(b);
let ret = hasher.finalize();
DrkScalar::from_bytes_wide(ret.as_array())
pallas::Scalar::from_bytes_wide(ret.as_array())
}
#[allow(non_snake_case)]
pub fn pedersen_commitment_scalar(value: DrkScalar, blind: DrkValueBlind) -> DrkValueCommit {
pub fn pedersen_commitment_scalar(value: pallas::Scalar, 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);

View File

@@ -1,29 +1,13 @@
//! Type aliases used in the codebase.
// Helpful for changing the curve and crypto we're using.
use halo2_gadgets::ecc::FixedPoints;
use pasta_curves::pallas;
use crate::crypto::{constants::OrchardFixedBases, util::mod_r_p};
pub type DrkCircuitField = pallas::Base;
pub type DrkTokenId = pallas::Base;
pub type DrkValue = pallas::Base;
pub type DrkSerial = pallas::Base;
pub type DrkCoin = pallas::Base;
pub type DrkCoinBlind = pallas::Base;
pub type DrkNullifier = pallas::Base;
pub type DrkValue = pallas::Base;
pub type DrkScalar = pallas::Scalar;
pub type DrkValueBlind = pallas::Scalar;
pub type DrkValueCommit = pallas::Point;
pub type DrkPublicKey = pallas::Point;
pub type DrkSecretKey = pallas::Base;
// TODO: move this elsewhere
pub fn derive_public_key(s: DrkSecretKey) -> DrkPublicKey {
OrchardFixedBases::NullifierK.generator() * mod_r_p(s)
}