Просмотр исходного кода

Merge branch 'master' of github.com:narodnik/sapvi

ghassmo 5 лет назад
Родитель
Сommit
ddb177d3b6

+ 18 - 1
Cargo.toml

@@ -27,6 +27,8 @@ rand_core = "0.5.1"
 sha2 = "0.9.1"
 sha2 = "0.9.1"
 rand_xorshift = "0.2"
 rand_xorshift = "0.2"
 blake2s_simd = "0.5"
 blake2s_simd = "0.5"
+blake2b_simd = "0.5.11"
+crypto_api_chachapoly = "0.4"
 bitvec = "0.18"
 bitvec = "0.18"
 bimap = "0.5.2"
 bimap = "0.5.2"
 async-trait = "0.1.42"
 async-trait = "0.1.42"
@@ -80,6 +82,12 @@ glob = "0.3"
 
 
 async_zmq = "0.3.2"
 async_zmq = "0.3.2"
 
 
+# wallet deps
+rocksdb = "0.16.0"
+dirs = "2.0.2"
+[dependencies.rusqlite]
+version = "0.25.1"
+features = ["bundled", "sqlcipher"]
 
 
 [[bin]]
 [[bin]]
 name = "lisp"
 name = "lisp"
@@ -105,6 +113,10 @@ path = "src/bin/mint-classic.rs"
 name = "spend-classic"
 name = "spend-classic"
 path = "src/bin/spend-classic.rs"
 path = "src/bin/spend-classic.rs"
 
 
+[[bin]]
+name = "tx"
+path = "src/bin/tx.rs"
+
 [[bin]]
 [[bin]]
 name = "dfg"
 name = "dfg"
 path = "src/bin/dfg.rs"
 path = "src/bin/dfg.rs"
@@ -116,10 +128,15 @@ path = "src/bin/compile-shaders.rs"
 [[bin]]
 [[bin]]
 name = "services"
 name = "services"
 path = "src/bin/services.rs"
 path = "src/bin/services.rs"
+
 [[bin]]
 [[bin]]
-name = "wallet"
+name = "demowallet"
 path = "src/bin/demowallet.rs"
 path = "src/bin/demowallet.rs"
 
 
+[[bin]]
+name = "wallet"
+path = "src/bin/wallet/test.rs"
+
 [profile.release]
 [profile.release]
 debug = 1
 debug = 1
 
 

+ 2 - 2
lisp/core.rs

@@ -628,8 +628,8 @@ fn scalar_is_zero(a: MalArgs) -> MalRet {
     }
     }
 }
 }
 
 
-fn add_scalar(a: MalArgs) -> MalRet {  
-    println!("add_scalar {:?}", a);  
+fn add_scalar(a: MalArgs) -> MalRet {
+    println!("add_scalar {:?}", a);
     match (a[0].clone(), a[1].clone()) {
     match (a[0].clone(), a[1].clone()) {
         (Func(_, _), ZKScalar(a1)) => {
         (Func(_, _), ZKScalar(a1)) => {
             if let Vector(ref values, _) = a[0].apply(vec![]).unwrap() {
             if let Vector(ref values, _) = a[0].apply(vec![]).unwrap() {

+ 8 - 10
lisp/lisp.rs

@@ -11,11 +11,9 @@ use bls12_381::Bls12;
 // use fnv::FnvHashMap;
 // use fnv::FnvHashMap;
 use itertools::Itertools;
 use itertools::Itertools;
 use rand::rngs::OsRng;
 use rand::rngs::OsRng;
+use std::borrow::{Borrow, BorrowMut};
 use std::rc::Rc;
 use std::rc::Rc;
 use std::time::Instant;
 use std::time::Instant;
-use std::{
-    borrow::{Borrow, BorrowMut},    
-};
 use std::{cell::RefCell, collections::HashMap};
 use std::{cell::RefCell, collections::HashMap};
 use types::EnforceAllocation;
 use types::EnforceAllocation;
 
 
@@ -353,7 +351,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
                     }
                     }
                     Sym(ref a0sym) if a0sym == "kill" => {
                     Sym(ref a0sym) if a0sym == "kill" => {
                         error(&format!("KILL at: {:?}", ast).to_string())
                         error(&format!("KILL at: {:?}", ast).to_string())
-                    }                
+                    }
                     Sym(ref a0sym) if a0sym == "alloc-const" => {
                     Sym(ref a0sym) if a0sym == "alloc-const" => {
                         let start = Instant::now();
                         let start = Instant::now();
                         let a1 = l[1].clone();
                         let a1 = l[1].clone();
@@ -652,7 +650,7 @@ pub fn setup(_ast: MalVal, env: Env) -> Result<VerifyKeyParams, MalErr> {
     })
     })
 }
 }
 
 
-pub fn prove(_ast: MalVal, env: Env) -> MalRet {    
+pub fn prove(_ast: MalVal, env: Env) -> MalRet {
     let start = Instant::now();
     let start = Instant::now();
     let allocs_input = get_allocations(&env, "AllocationsInput");
     let allocs_input = get_allocations(&env, "AllocationsInput");
     let allocs = get_allocations(&env, "Allocations");
     let allocs = get_allocations(&env, "Allocations");
@@ -691,12 +689,12 @@ pub fn prove(_ast: MalVal, env: Env) -> MalRet {
         };
         };
     }
     }
     println!("groth16::create_random_proof: {:?}", start.elapsed());
     println!("groth16::create_random_proof: {:?}", start.elapsed());
-    // verification process 
+    // verification process
     let start = Instant::now();
     let start = Instant::now();
     let result = groth16::verify_proof(verifying_key.as_ref().unwrap(), &proof, &vec_input);
     let result = groth16::verify_proof(verifying_key.as_ref().unwrap(), &proof, &vec_input);
     println!("groth16::verify_proof: {:?}", start.elapsed());
     println!("groth16::verify_proof: {:?}", start.elapsed());
     println!("vec public {:?}", vec_input);
     println!("vec public {:?}", vec_input);
-    println!("result {:?}", result);    
+    println!("result {:?}", result);
     Ok(MalVal::Nil)
     Ok(MalVal::Nil)
 }
 }
 
 
@@ -766,13 +764,13 @@ fn repl_load(file: String) -> Result<(), ()> {
     match rep(&format!("(load-file \"{}\")", file), &repl_env) {
     match rep(&format!("(load-file \"{}\")", file), &repl_env) {
         Ok(_) => {
         Ok(_) => {
             println!("lisp end \t {:?}", start.elapsed());
             println!("lisp end \t {:?}", start.elapsed());
-            std::process::exit(0) 
-        },
+            std::process::exit(0)
+        }
         Err(e) => {
         Err(e) => {
             println!("Error: {}", format_error(e));
             println!("Error: {}", format_error(e));
             std::process::exit(1);
             std::process::exit(1);
         }
         }
-    }    
+    }
 }
 }
 
 
 #[cfg(test)]
 #[cfg(test)]

+ 10 - 7
lisp/types.rs

@@ -1,8 +1,11 @@
 use bellman::{gadgets::Assignment, groth16, Circuit, ConstraintSystem, SynthesisError};
 use bellman::{gadgets::Assignment, groth16, Circuit, ConstraintSystem, SynthesisError};
 use sapvi::bls_extensions::BlsStringConversion;
 use sapvi::bls_extensions::BlsStringConversion;
-use std::{ops::{Add, AddAssign, MulAssign, SubAssign}, time::Instant};
 use std::rc::Rc;
 use std::rc::Rc;
 use std::{cell::RefCell, collections::HashMap};
 use std::{cell::RefCell, collections::HashMap};
+use std::{
+    ops::{Add, AddAssign, MulAssign, SubAssign},
+    time::Instant,
+};
 // use fnv::FnvHashMap;
 // use fnv::FnvHashMap;
 use itertools::Itertools;
 use itertools::Itertools;
 
 
@@ -77,7 +80,7 @@ impl Circuit<bls12_381::Scalar> for LispCircuit {
         let circuitTime = Instant::now();
         let circuitTime = Instant::now();
         let start = Instant::now();
         let start = Instant::now();
         // println!("Allocations\n");
         // println!("Allocations\n");
-        // TODO is the private and params 
+        // TODO is the private and params
         for (k, v) in &self.allocs {
         for (k, v) in &self.allocs {
             match v {
             match v {
                 MalVal::ZKScalar(val) => {
                 MalVal::ZKScalar(val) => {
@@ -101,7 +104,7 @@ impl Circuit<bls12_381::Scalar> for LispCircuit {
             }
             }
         }
         }
         println!("circuit alloc \t {:?}", start.elapsed());
         println!("circuit alloc \t {:?}", start.elapsed());
-        let start = Instant::now();        
+        let start = Instant::now();
         // println!("Allocations Input\n");
         // println!("Allocations Input\n");
         // TODO alloc-input is the public value
         // TODO alloc-input is the public value
         for (k, v) in &self.alloc_inputs {
         for (k, v) in &self.alloc_inputs {
@@ -122,7 +125,7 @@ impl Circuit<bls12_381::Scalar> for LispCircuit {
                 }
                 }
             }
             }
         }
         }
-        println!("circuit alloc input \t {:?}", start.elapsed());        
+        println!("circuit alloc input \t {:?}", start.elapsed());
         let start = Instant::now();
         let start = Instant::now();
         let mut enforce_sorted = self.constraints.clone();
         let mut enforce_sorted = self.constraints.clone();
         // enforce_sorted.sort_by(|a, b| a.idx.cmp(&b.idx));
         // enforce_sorted.sort_by(|a, b| a.idx.cmp(&b.idx));
@@ -249,9 +252,9 @@ impl Circuit<bls12_381::Scalar> for LispCircuit {
                 |_| right.clone(),
                 |_| right.clone(),
                 |_| output.clone(),
                 |_| output.clone(),
             );
             );
-        }     
-        println!("circuit enforce \t {:?}", start.elapsed());        
-        println!("end circuit \t {:?}", circuitTime.elapsed());        
+        }
+        println!("circuit enforce \t {:?}", start.elapsed());
+        println!("end circuit \t {:?}", circuitTime.elapsed());
         Ok(())
         Ok(())
     }
     }
 }
 }

+ 11 - 3
src/bin/mint-classic.rs

@@ -5,7 +5,9 @@ use bls12_381::Bls12;
 use ff::Field;
 use ff::Field;
 use group::{Curve, Group, GroupEncoding};
 use group::{Curve, Group, GroupEncoding};
 
 
-use sapvi::crypto::{save_params, load_params, setup_mint_prover, create_mint_proof, verify_mint_proof};
+use sapvi::crypto::{
+    create_mint_proof, load_params, save_params, setup_mint_prover, verify_mint_proof,
+};
 
 
 fn main() {
 fn main() {
     use rand::rngs::OsRng;
     use rand::rngs::OsRng;
@@ -25,8 +27,14 @@ fn main() {
     }
     }
     let (params, pvk) = load_params("mint.params").expect("params should load");
     let (params, pvk) = load_params("mint.params").expect("params should load");
 
 
-    let (proof, revealed) = create_mint_proof(&params, value, randomness_value, serial, randomness_coin,
-                                              public);
+    let (proof, revealed) = create_mint_proof(
+        &params,
+        value,
+        randomness_value,
+        serial,
+        randomness_coin,
+        public,
+    );
 
 
     assert!(verify_mint_proof(&pvk, &proof, &revealed));
     assert!(verify_mint_proof(&pvk, &proof, &revealed));
 }
 }

+ 17 - 3
src/bin/spend-classic.rs

@@ -7,7 +7,9 @@ use ff::{Field, PrimeField};
 use group::{Curve, GroupEncoding};
 use group::{Curve, GroupEncoding};
 
 
 use sapvi::circuit::spend_contract::SpendContract;
 use sapvi::circuit::spend_contract::SpendContract;
-use sapvi::crypto::{save_params, load_params, setup_spend_prover, create_spend_proof, verify_spend_proof};
+use sapvi::crypto::{
+    create_spend_proof, load_params, save_params, setup_spend_prover, verify_spend_proof,
+};
 
 
 // This thing is nasty lol
 // This thing is nasty lol
 pub fn merkle_hash(
 pub fn merkle_hash(
@@ -176,6 +178,7 @@ fn main() {
     let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let randomness_coin: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let randomness_coin: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+    let signature_secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
 
 
     let merkle_path = [
     let merkle_path = [
         (bls12_381::Scalar::random(&mut OsRng), true),
         (bls12_381::Scalar::random(&mut OsRng), true),
@@ -190,8 +193,19 @@ fn main() {
     }
     }
     let (params, pvk) = load_params("spend.params").expect("params should load");
     let (params, pvk) = load_params("spend.params").expect("params should load");
 
 
-    let (proof, revealed) = create_spend_proof(&params, value, randomness_value, serial, randomness_coin,
-                                              secret, merkle_path);
+    let signature_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature_secret;
+
+    let (proof, revealed) = create_spend_proof(
+        &params,
+        value,
+        randomness_value,
+        serial,
+        randomness_coin,
+        secret,
+        merkle_path,
+        signature_secret
+    );
 
 
     assert!(verify_spend_proof(&pvk, &proof, &revealed));
     assert!(verify_spend_proof(&pvk, &proof, &revealed));
+    assert_eq!(revealed.signature_public, signature_public);
 }
 }

+ 58 - 0
src/bin/tx.rs

@@ -0,0 +1,58 @@
+use std::io;
+use bellman::groth16;
+use bls12_381::Bls12;
+use ff::Field;
+use group::Group;
+use rand::rngs::OsRng;
+
+use sapvi::crypto::{
+    create_mint_proof, load_params, save_params, setup_mint_prover, verify_mint_proof,
+    MintRevealedValues,
+    note::Note
+};
+use sapvi::serial::{Decodable, Encodable, VarInt};
+use sapvi::error::{Error, Result};
+use sapvi::tx;
+
+fn txbuilding() {
+    {
+        let params = setup_mint_prover();
+        save_params("mint.params", &params);
+    }
+    let (mint_params, mint_pvk) = load_params("mint.params").expect("params should load");
+
+    let public = jubjub::SubgroupPoint::random(&mut OsRng);
+
+    let builder = tx::TransactionBuilder {
+        clear_inputs: vec![tx::TransactionBuilderClearInputInfo { value: 110 }],
+        outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, public }],
+    };
+
+    let mut tx_data = vec![];
+    {
+        let tx = builder.build(&mint_params);
+        tx.encode(&mut tx_data).expect("encode tx");
+    }
+    {
+        let tx = tx::Transaction::decode(&tx_data[..]).unwrap();
+        assert!(tx.verify(&mint_pvk));
+    }
+}
+
+fn main() {
+    txbuilding();
+    /*let note = Note {
+        serial: jubjub::Fr::random(&mut OsRng),
+        value: 110,
+        coin_blind: jubjub::Fr::random(&mut OsRng),
+        valcom_blind: jubjub::Fr::random(&mut OsRng),
+    };
+
+    let secret = jubjub::Fr::random(&mut OsRng);
+    let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
+
+    let encrypted_note = note.encrypt(&public).unwrap();
+    let note2 = encrypted_note.decrypt(&secret).unwrap();
+    assert_eq!(note.value, note2.value);*/
+}
+

+ 87 - 0
src/bin/wallet/test.rs

@@ -0,0 +1,87 @@
+// rocksdb is the blockchain database
+// it is a key value store
+// sqlite is the encrypted wallet
+
+use rocksdb::DB;
+use rusqlite::{Connection, Result};
+use std::path::{Path, PathBuf};
+
+fn main() -> Result<()> {
+    wallet()?;
+    blockchain()?;
+    Ok(())
+}
+
+fn wallet() -> Result<()> {
+    let connector = connect()?;
+    encrypt(&connector)?;
+    println!("Created encrypted database.");
+    decrypt(&connector)?;
+    println!("Decrypted database.");
+    Ok(())
+}
+
+fn connect() -> Result<Connection> {
+    println!("Attempting to establish a connection...");
+    let path = dirs::home_dir()
+        .expect("Cannot find home directory!")
+        .as_path()
+        .join(".config/darkfi/wallet.db");
+    let connector = Connection::open(&path);
+    println!("Connection established");
+    connector
+}
+
+fn encrypt(conn: &Connection) -> Result<()> {
+    println!("Attempting to create an encrypted database...");
+    conn.execute_batch(
+        "ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';
+                SELECT sqlcipher_export('encrypted');
+                DETACH DATABASE encrypted;",
+    )
+}
+
+fn decrypt(conn: &Connection) -> Result<()> {
+    println!("Attempting to decrypt database...");
+    conn.execute_batch(
+        "ATTACH DATABASE 'plaintext.db' AS plaintext KEY 'testkey';
+                SELECT sqlcipher_export('plaintext');
+                DETACH DATABASE plaintext;",
+    )
+}
+
+fn blockchain() -> Result<()> {
+    let db = create_db();
+    write_db(&db)?;
+    test_db(&db);
+    Ok(())
+}
+
+fn create_db() -> DB {
+    println!("Creating a blockchain database...");
+    let path = dirs::home_dir()
+        .expect("Cannot find home directory!")
+        .as_path()
+        .join(".config/darkfi/chain");
+    let db = DB::open_default(path).unwrap();
+    db
+}
+
+fn write_db(db: &DB) -> Result<()> {
+    println!("Writing to the blockchain...");
+    db.put(b"test-value", b"test-key").unwrap();
+    Ok(())
+}
+
+fn test_db(db: &DB) {
+    println!("Testing if write was successful...");
+    match db.get(b"test-value") {
+        Ok(Some(value)) => println!("retrieved value {}", String::from_utf8(value).unwrap()),
+        Ok(None) => println!("value not found"),
+        Err(e) => println!("operational problem encountered: {}", e),
+    }
+}
+
+// TODO: macro to load file as a string. load wallet tables in sqlite at run
+// Table includes: maintain a list of coins and whether they are spent
+

+ 9 - 0
src/bin/wallet/wallet.sql

@@ -0,0 +1,9 @@
+ATTACH DATABASE 'wallet.db' AS wallet KEY 'testkey';
+SELECT sqlcipher_export('wallet');
+CREATE TABLE IF NOT EXISTS keys(
+    key_id INT PRIMARY KEY NOT NULL,
+    key_public BLOB NOT NULL,
+    key_private BLOB NOT NULL
+);
+CREATE INDEX IF NOT EXISTS key_public on keys(key_public);
+

+ 0 - 1
src/circuit/mod.rs

@@ -1,3 +1,2 @@
 pub mod mint_contract;
 pub mod mint_contract;
 pub mod spend_contract;
 pub mod spend_contract;
-

+ 12 - 0
src/circuit/spend_contract.rs

@@ -27,6 +27,7 @@ pub struct SpendContract {
     pub is_right_2: Option<bool>,
     pub is_right_2: Option<bool>,
     pub branch_3: Option<bls12_381::Scalar>,
     pub branch_3: Option<bls12_381::Scalar>,
     pub is_right_3: Option<bool>,
     pub is_right_3: Option<bool>,
+    pub signature_secret: Option<jubjub::Fr>,
 }
 }
 impl Circuit<bls12_381::Scalar> for SpendContract {
 impl Circuit<bls12_381::Scalar> for SpendContract {
     fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
     fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
@@ -435,6 +436,17 @@ impl Circuit<bls12_381::Scalar> for SpendContract {
         // Line 253: emit_scalar current
         // Line 253: emit_scalar current
         current.inputize(cs.namespace(|| "Line 253: emit_scalar current"))?;
         current.inputize(cs.namespace(|| "Line 253: emit_scalar current"))?;
 
 
+        let signature_secret = boolean::field_into_boolean_vec_le(
+            cs.namespace(|| "Signature secret"),
+            self.signature_secret,
+        )?;
+        let signature_public = ecc::fixed_base_multiplication(
+            cs.namespace(|| "Signature public"),
+            &zcash_proofs::constants::SPENDING_KEY_GENERATOR,
+            &signature_secret,
+        )?;
+        signature_public.inputize(cs.namespace(|| "Signature public inputize"))?;
+
         Ok(())
         Ok(())
     }
     }
 }
 }

+ 98 - 0
src/crypto/coin.rs

@@ -0,0 +1,98 @@
+use std::io;
+use group::Curve;
+use bitvec::{order::Lsb0, view::AsBits};
+use lazy_static::lazy_static;
+use ff::PrimeField;
+
+use super::merkle::Hashable;
+
+pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 4;
+
+/// Compute a parent node in the Sapling commitment tree given its two children.
+pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> bls12_381::Scalar {
+    // This thing is nasty lol
+    let lhs = {
+        let mut tmp = [false; 256];
+        for (a, b) in tmp.iter_mut().zip(lhs.as_bits::<Lsb0>()) {
+            *a = *b;
+        }
+        tmp
+    };
+
+    let rhs = {
+        let mut tmp = [false; 256];
+        for (a, b) in tmp.iter_mut().zip(rhs.as_bits::<Lsb0>()) {
+            *a = *b;
+        }
+        tmp
+    };
+
+    jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash(
+        zcash_primitives::pedersen_hash::Personalization::MerkleTree(depth),
+        lhs.iter()
+            .copied()
+            .take(bls12_381::Scalar::NUM_BITS as usize)
+            .chain(
+                rhs.iter()
+                    .copied()
+                    .take(bls12_381::Scalar::NUM_BITS as usize),
+            ),
+    ))
+    .to_affine()
+    .get_u()
+}
+
+/// A node within the Sapling commitment tree.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Node {
+    repr: [u8; 32],
+}
+
+impl Node {
+    pub fn new(repr: [u8; 32]) -> Self {
+        Node { repr }
+    }
+}
+
+impl Hashable for Node {
+    fn read<R: io::Read>(mut reader: R) -> io::Result<Self> {
+        let mut repr = [0u8; 32];
+        reader.read_exact(&mut repr)?;
+        Ok(Node::new(repr))
+    }
+
+    fn write<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
+        writer.write_all(self.repr.as_ref())
+    }
+
+    fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self {
+        Node {
+            repr: merkle_hash(depth, &lhs.repr, &rhs.repr).to_repr(),
+        }
+    }
+
+    fn blank() -> Self {
+        // The smallest u-coordinate that is not on the curve
+        // is one.
+        let uncommitted_note = bls12_381::Scalar::one();
+        Node {
+            repr: uncommitted_note.to_repr(),
+        }
+    }
+
+    fn empty_root(depth: usize) -> Self {
+        EMPTY_ROOTS[depth]
+    }
+}
+
+lazy_static! {
+    static ref EMPTY_ROOTS: Vec<Node> = {
+        let mut v = vec![Node::blank()];
+        for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
+            let next = Node::combine(d, &v[d], &v[d]);
+            v.push(next);
+        }
+        v
+    };
+}
+

+ 35 - 0
src/crypto/diffie_hellman.rs

@@ -0,0 +1,35 @@
+use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
+use group::{cofactor::CofactorGroup, GroupEncoding};
+
+pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"DarkFiSaplingKDF";
+
+/// Functions used for encrypting the note in transaction outputs.
+
+/// Sapling key agreement for note encryption.
+///
+/// Implements section 5.4.4.3 of the Zcash Protocol Specification.
+pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint {
+    // [8 esk] pk_d
+    // <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
+    // ExtendedPoint::mul_by_cofactor in the jubjub crate.
+
+    // ExtendedPoint::multiply currently just implements double-and-add,
+    // so using wNAF is a concrete speed improvement (as it operates over a window of bits
+    // instead of individual bits).
+    // We want that to be fast because it's in the hot path for trial decryption of notes on chain.
+    let mut wnaf = group::Wnaf::new();
+    wnaf.scalar(esk).base(*pk_d).clear_cofactor()
+}
+
+/// Sapling KDF for note encryption.
+///
+/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
+pub fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::ExtendedPoint) -> Blake2bHash {
+    Blake2bParams::new()
+        .hash_length(32)
+        .personal(KDF_SAPLING_PERSONALIZATION)
+        .to_state()
+        .update(&dhsecret.to_bytes())
+        .update(&epk.to_bytes())
+        .finalize()
+}

+ 46 - 0
src/crypto/fr_serial.rs

@@ -0,0 +1,46 @@
+use std::io;
+use group::GroupEncoding;
+
+use crate::serial::{Encodable, Decodable, ReadExt, WriteExt};
+use crate::error::{Error, Result};
+
+impl Encodable for jubjub::Fr {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        s.write_slice(&self.to_bytes()[..])?;
+        Ok(32)
+    }
+}
+
+impl Decodable for jubjub::Fr {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let mut bytes = [0u8; 32];
+        d.read_slice(&mut bytes)?;
+        let result = Self::from_bytes(&bytes);
+        if result.is_some().into() {
+            Ok(result.unwrap())
+        } else {
+            Err(Error::BadOperationType)
+        }
+    }
+}
+
+impl Encodable for jubjub::SubgroupPoint {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        s.write_slice(&self.to_bytes()[..])?;
+        Ok(32)
+    }
+}
+
+impl Decodable for jubjub::SubgroupPoint {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let mut bytes = [0u8; 32];
+        d.read_slice(&mut bytes)?;
+        let result = Self::from_bytes(&bytes);
+        if result.is_some().into() {
+            Ok(result.unwrap())
+        } else {
+            Err(Error::BadOperationType)
+        }
+    }
+}
+

+ 501 - 0
src/crypto/merkle.rs

@@ -0,0 +1,501 @@
+//! Implementation of a Merkle tree of commitments used to prove the existence of notes.
+
+//use byteorder::{LittleEndian, ReadBytesExt};
+use std::collections::VecDeque;
+use std::io::{self, Read, Write};
+
+//use crate::serialize::{Optional, Vector};
+use super::coin::SAPLING_COMMITMENT_TREE_DEPTH;
+
+/// A hashable node within a Merkle tree.
+pub trait Hashable: Clone + Copy {
+    /// Parses a node from the given byte source.
+    fn read<R: Read>(reader: R) -> io::Result<Self>;
+
+    /// Serializes this node.
+    fn write<W: Write>(&self, writer: W) -> io::Result<()>;
+
+    /// Returns the parent node within the tree of the two given nodes.
+    fn combine(_: usize, _: &Self, _: &Self) -> Self;
+
+    /// Returns a blank leaf node.
+    fn blank() -> Self;
+
+    /// Returns the empty root for the given depth.
+    fn empty_root(_: usize) -> Self;
+}
+
+struct PathFiller<Node: Hashable> {
+    queue: VecDeque<Node>,
+}
+
+impl<Node: Hashable> PathFiller<Node> {
+    fn empty() -> Self {
+        PathFiller {
+            queue: VecDeque::new(),
+        }
+    }
+
+    fn next(&mut self, depth: usize) -> Node {
+        self.queue
+            .pop_front()
+            .unwrap_or_else(|| Node::empty_root(depth))
+    }
+}
+
+/// A Merkle tree of note commitments.
+///
+/// The depth of the Merkle tree is fixed at 32, equal to the depth of the Sapling
+/// commitment tree.
+#[derive(Clone)]
+pub struct CommitmentTree<Node: Hashable> {
+    left: Option<Node>,
+    right: Option<Node>,
+    parents: Vec<Option<Node>>,
+}
+
+impl<Node: Hashable> CommitmentTree<Node> {
+    /// Creates an empty tree.
+    pub fn empty() -> Self {
+        CommitmentTree {
+            left: None,
+            right: None,
+            parents: vec![],
+        }
+    }
+
+    /*
+    /// Reads a `CommitmentTree` from its serialized form.
+    #[allow(clippy::redundant_closure)]
+    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
+        let left = Optional::read(&mut reader, |r| Node::read(r))?;
+        let right = Optional::read(&mut reader, |r| Node::read(r))?;
+        let parents = Vector::read(&mut reader, |r| Optional::read(r, |r| Node::read(r)))?;
+
+        Ok(CommitmentTree {
+            left,
+            right,
+            parents,
+        })
+    }
+
+    /// Serializes this tree as an array of bytes.
+    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
+        Optional::write(&mut writer, &self.left, |w, n| n.write(w))?;
+        Optional::write(&mut writer, &self.right, |w, n| n.write(w))?;
+        Vector::write(&mut writer, &self.parents, |w, e| {
+            Optional::write(w, e, |w, n| n.write(w))
+        })
+    }
+    */
+
+    /// Returns the number of leaf nodes in the tree.
+    pub fn size(&self) -> usize {
+        self.parents.iter().enumerate().fold(
+            match (self.left, self.right) {
+                (None, None) => 0,
+                (Some(_), None) => 1,
+                (Some(_), Some(_)) => 2,
+                (None, Some(_)) => unreachable!(),
+            },
+            |acc, (i, p)| {
+                // Treat occupation of parents array as a binary number
+                // (right-shifted by 1)
+                acc + if p.is_some() { 1 << (i + 1) } else { 0 }
+            },
+        )
+    }
+
+    fn is_complete(&self, depth: usize) -> bool {
+        self.left.is_some()
+            && self.right.is_some()
+            && self.parents.len() == depth - 1
+            && self.parents.iter().all(|p| p.is_some())
+    }
+
+    /// Adds a leaf node to the tree.
+    ///
+    /// Returns an error if the tree is full.
+    pub fn append(&mut self, node: Node) -> Result<(), ()> {
+        self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH)
+    }
+
+    fn append_inner(&mut self, node: Node, depth: usize) -> Result<(), ()> {
+        if self.is_complete(depth) {
+            // Tree is full
+            return Err(());
+        }
+
+        match (self.left, self.right) {
+            (None, _) => self.left = Some(node),
+            (_, None) => self.right = Some(node),
+            (Some(l), Some(r)) => {
+                let mut combined = Node::combine(0, &l, &r);
+                self.left = Some(node);
+                self.right = None;
+
+                for i in 0..depth {
+                    if i < self.parents.len() {
+                        if let Some(p) = self.parents[i] {
+                            combined = Node::combine(i + 1, &p, &combined);
+                            self.parents[i] = None;
+                        } else {
+                            self.parents[i] = Some(combined);
+                            break;
+                        }
+                    } else {
+                        self.parents.push(Some(combined));
+                        break;
+                    }
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Returns the current root of the tree.
+    pub fn root(&self) -> Node {
+        self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH, PathFiller::empty())
+    }
+
+    fn root_inner(&self, depth: usize, mut filler: PathFiller<Node>) -> Node {
+        assert!(depth > 0);
+
+        // 1) Hash left and right leaves together.
+        //    - Empty leaves are used as needed.
+        let leaf_root = Node::combine(
+            0,
+            &self.left.unwrap_or_else(|| filler.next(0)),
+            &self.right.unwrap_or_else(|| filler.next(0)),
+        );
+
+        // 2) Hash in parents up to the currently-filled depth.
+        //    - Roots of the empty subtrees are used as needed.
+        let mid_root = self
+            .parents
+            .iter()
+            .enumerate()
+            .fold(leaf_root, |root, (i, p)| match p {
+                Some(node) => Node::combine(i + 1, node, &root),
+                None => Node::combine(i + 1, &root, &filler.next(i + 1)),
+            });
+
+        // 3) Hash in roots of the empty subtrees up to the final depth.
+        ((self.parents.len() + 1)..depth)
+            .fold(mid_root, |root, d| Node::combine(d, &root, &filler.next(d)))
+    }
+}
+
+/// An updatable witness to a path from a position in a particular [`CommitmentTree`].
+///
+/// Appending the same commitments in the same order to both the original
+/// [`CommitmentTree`] and this `IncrementalWitness` will result in a witness to the path
+/// from the target position to the root of the updated tree.
+///
+/// # Examples
+///
+/// ```
+/// use ff::{Field, PrimeField};
+/// use rand_core::OsRng;
+/// use zcash_primitives::{
+///     merkle_tree::{CommitmentTree, IncrementalWitness},
+///     sapling::Node,
+/// };
+///
+/// let mut rng = OsRng;
+///
+/// let mut tree = CommitmentTree::<Node>::empty();
+///
+/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
+/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
+/// let mut witness = IncrementalWitness::from_tree(&tree);
+/// assert_eq!(witness.position(), 1);
+/// assert_eq!(tree.root(), witness.root());
+///
+/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr());
+/// tree.append(cmu);
+/// witness.append(cmu);
+/// assert_eq!(tree.root(), witness.root());
+/// ```
+#[derive(Clone)]
+pub struct IncrementalWitness<Node: Hashable> {
+    tree: CommitmentTree<Node>,
+    filled: Vec<Node>,
+    cursor_depth: usize,
+    cursor: Option<CommitmentTree<Node>>,
+}
+
+impl<Node: Hashable> IncrementalWitness<Node> {
+    /// Creates an `IncrementalWitness` for the most recent commitment added to the given
+    /// [`CommitmentTree`].
+    pub fn from_tree(tree: &CommitmentTree<Node>) -> IncrementalWitness<Node> {
+        IncrementalWitness {
+            tree: tree.clone(),
+            filled: vec![],
+            cursor_depth: 0,
+            cursor: None,
+        }
+    }
+
+    /*
+    /// Reads an `IncrementalWitness` from its serialized form.
+    #[allow(clippy::redundant_closure)]
+    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
+        let tree = CommitmentTree::read(&mut reader)?;
+        let filled = Vector::read(&mut reader, |r| Node::read(r))?;
+        let cursor = Optional::read(&mut reader, |r| CommitmentTree::read(r))?;
+
+        let mut witness = IncrementalWitness {
+            tree,
+            filled,
+            cursor_depth: 0,
+            cursor,
+        };
+
+        witness.cursor_depth = witness.next_depth();
+
+        Ok(witness)
+    }
+
+    /// Serializes this `IncrementalWitness` as an array of bytes.
+    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
+        self.tree.write(&mut writer)?;
+        Vector::write(&mut writer, &self.filled, |w, n| n.write(w))?;
+        Optional::write(&mut writer, &self.cursor, |w, t| t.write(w))
+    }
+    */
+
+    /// Returns the position of the witnessed leaf node in the commitment tree.
+    pub fn position(&self) -> usize {
+        self.tree.size() - 1
+    }
+
+    fn filler(&self) -> PathFiller<Node> {
+        let cursor_root = self
+            .cursor
+            .as_ref()
+            .map(|c| c.root_inner(self.cursor_depth, PathFiller::empty()));
+
+        PathFiller {
+            queue: self.filled.iter().cloned().chain(cursor_root).collect(),
+        }
+    }
+
+    /// Finds the next "depth" of an unfilled subtree.
+    fn next_depth(&self) -> usize {
+        let mut skip = self.filled.len();
+
+        if self.tree.left.is_none() {
+            if skip > 0 {
+                skip -= 1;
+            } else {
+                return 0;
+            }
+        }
+
+        if self.tree.right.is_none() {
+            if skip > 0 {
+                skip -= 1;
+            } else {
+                return 0;
+            }
+        }
+
+        let mut d = 1;
+        for p in &self.tree.parents {
+            if p.is_none() {
+                if skip > 0 {
+                    skip -= 1;
+                } else {
+                    return d;
+                }
+            }
+            d += 1;
+        }
+
+        d + skip
+    }
+
+    /// Tracks a leaf node that has been added to the underlying tree.
+    ///
+    /// Returns an error if the tree is full.
+    pub fn append(&mut self, node: Node) -> Result<(), ()> {
+        self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH)
+    }
+
+    fn append_inner(&mut self, node: Node, depth: usize) -> Result<(), ()> {
+        if let Some(mut cursor) = self.cursor.take() {
+            cursor
+                .append_inner(node, depth)
+                .expect("cursor should not be full");
+            if cursor.is_complete(self.cursor_depth) {
+                self.filled
+                    .push(cursor.root_inner(self.cursor_depth, PathFiller::empty()));
+            } else {
+                self.cursor = Some(cursor);
+            }
+        } else {
+            self.cursor_depth = self.next_depth();
+            if self.cursor_depth >= depth {
+                // Tree is full
+                return Err(());
+            }
+
+            if self.cursor_depth == 0 {
+                self.filled.push(node);
+            } else {
+                let mut cursor = CommitmentTree::empty();
+                cursor
+                    .append_inner(node, depth)
+                    .expect("cursor should not be full");
+                self.cursor = Some(cursor);
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Returns the current root of the tree corresponding to the witness.
+    pub fn root(&self) -> Node {
+        self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH)
+    }
+
+    fn root_inner(&self, depth: usize) -> Node {
+        self.tree.root_inner(depth, self.filler())
+    }
+
+    /// Returns the current witness, or None if the tree is empty.
+    pub fn path(&self) -> Option<MerklePath<Node>> {
+        self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH)
+    }
+
+    fn path_inner(&self, depth: usize) -> Option<MerklePath<Node>> {
+        let mut filler = self.filler();
+        let mut auth_path = Vec::new();
+
+        if let Some(node) = self.tree.left {
+            if self.tree.right.is_some() {
+                auth_path.push((node, true));
+            } else {
+                auth_path.push((filler.next(0), false));
+            }
+        } else {
+            // Can't create an authentication path for the beginning of the tree
+            return None;
+        }
+
+        for (i, p) in self.tree.parents.iter().enumerate() {
+            auth_path.push(match p {
+                Some(node) => (*node, true),
+                None => (filler.next(i + 1), false),
+            });
+        }
+
+        for i in self.tree.parents.len()..(depth - 1) {
+            auth_path.push((filler.next(i + 1), false));
+        }
+        assert_eq!(auth_path.len(), depth);
+
+        Some(MerklePath::from_path(auth_path, self.position() as u64))
+    }
+}
+
+/// A path from a position in a particular commitment tree to the root of that tree.
+#[derive(Clone, Debug, PartialEq)]
+pub struct MerklePath<Node: Hashable> {
+    pub auth_path: Vec<(Node, bool)>,
+    pub position: u64,
+}
+
+impl<Node: Hashable> MerklePath<Node> {
+    /// Constructs a Merkle path directly from a path and position.
+    pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self {
+        MerklePath {
+            auth_path,
+            position,
+        }
+    }
+
+    /*
+    /// Reads a Merkle path from its serialized form.
+    pub fn from_slice(witness: &[u8]) -> Result<Self, ()> {
+        Self::from_slice_with_depth(witness, SAPLING_COMMITMENT_TREE_DEPTH)
+    }
+
+    fn from_slice_with_depth(mut witness: &[u8], depth: usize) -> Result<Self, ()> {
+        // Skip the first byte, which should be "depth" to signify the length of
+        // the following vector of Pedersen hashes.
+        if witness[0] != depth as u8 {
+            return Err(());
+        }
+        witness = &witness[1..];
+
+        // Begin to construct the authentication path
+        let iter = witness.chunks_exact(33);
+        witness = iter.remainder();
+
+        // The vector works in reverse
+        let mut auth_path = iter
+            .rev()
+            .map(|bytes| {
+                // Length of inner vector should be the length of a Pedersen hash
+                if bytes[0] == 32 {
+                    // Sibling node should be an element of Fr
+                    Node::read(&bytes[1..])
+                        .map(|sibling| {
+                            // Set the value in the auth path; we put false here
+                            // for now (signifying the position bit) which we'll
+                            // fill in later.
+                            (sibling, false)
+                        })
+                        .map_err(|_| ())
+                } else {
+                    Err(())
+                }
+            })
+            .collect::<Result<Vec<_>, _>>()?;
+        if auth_path.len() != depth {
+            return Err(());
+        }
+
+        // Read the position from the witness
+        let position = witness.read_u64::<LittleEndian>().map_err(|_| ())?;
+
+        // Given the position, let's finish constructing the authentication
+        // path
+        let mut tmp = position;
+        for entry in auth_path.iter_mut() {
+            entry.1 = (tmp & 1) == 1;
+            tmp >>= 1;
+        }
+
+        // The witness should be empty now; if it wasn't, the caller would
+        // have provided more information than they should have, indicating
+        // a bug downstream
+        if witness.is_empty() {
+            Ok(MerklePath {
+                auth_path,
+                position,
+            })
+        } else {
+            Err(())
+        }
+    }
+    */
+
+    /// Returns the root of the tree corresponding to this path applied to `leaf`.
+    pub fn root(&self, leaf: Node) -> Node {
+        self.auth_path
+            .iter()
+            .enumerate()
+            .fold(
+                leaf,
+                |root, (i, (p, leaf_is_on_right))| match leaf_is_on_right {
+                    false => Node::combine(i, &root, p),
+                    true => Node::combine(i, p, &root),
+                },
+            )
+    }
+}
+

+ 27 - 8
src/crypto/mint_proof.rs

@@ -1,14 +1,16 @@
-use rand::rngs::OsRng;
-use std::time::Instant;
 use bellman::gadgets::multipack;
 use bellman::gadgets::multipack;
 use bellman::groth16;
 use bellman::groth16;
 use blake2s_simd::Params as Blake2sParams;
 use blake2s_simd::Params as Blake2sParams;
 use bls12_381::Bls12;
 use bls12_381::Bls12;
 use ff::Field;
 use ff::Field;
+use std::io;
 use group::{Curve, Group, GroupEncoding};
 use group::{Curve, Group, GroupEncoding};
+use rand::rngs::OsRng;
+use std::time::Instant;
 
 
-use crate::error::Result;
 use crate::circuit::mint_contract::MintContract;
 use crate::circuit::mint_contract::MintContract;
+use crate::error::{Error, Result};
+use crate::serial::{Decodable, Encodable};
 
 
 pub struct MintRevealedValues {
 pub struct MintRevealedValues {
     pub value_commit: jubjub::SubgroupPoint,
     pub value_commit: jubjub::SubgroupPoint,
@@ -74,6 +76,24 @@ impl MintRevealedValues {
     }
     }
 }
 }
 
 
+impl Encodable for MintRevealedValues {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.value_commit.encode(&mut s)?;
+        len += self.coin.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for MintRevealedValues {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            value_commit: Decodable::decode(&mut d)?,
+            coin: Decodable::decode(d)?
+        })
+    }
+}
+
 pub fn setup_mint_prover() -> groth16::Parameters<Bls12> {
 pub fn setup_mint_prover() -> groth16::Parameters<Bls12> {
     println!("Making random params...");
     println!("Making random params...");
     let start = Instant::now();
     let start = Instant::now();
@@ -97,8 +117,8 @@ pub fn create_mint_proof(
     randomness_value: jubjub::Fr,
     randomness_value: jubjub::Fr,
     serial: jubjub::Fr,
     serial: jubjub::Fr,
     randomness_coin: jubjub::Fr,
     randomness_coin: jubjub::Fr,
-    public: jubjub::SubgroupPoint
-    ) -> (groth16::Proof<Bls12>, MintRevealedValues) {
+    public: jubjub::SubgroupPoint,
+) -> (groth16::Proof<Bls12>, MintRevealedValues) {
     let revealed =
     let revealed =
         MintRevealedValues::compute(value, &randomness_value, &serial, &randomness_coin, &public);
         MintRevealedValues::compute(value, &randomness_value, &serial, &randomness_coin, &public);
 
 
@@ -120,8 +140,8 @@ pub fn create_mint_proof(
 pub fn verify_mint_proof(
 pub fn verify_mint_proof(
     pvk: &groth16::PreparedVerifyingKey<Bls12>,
     pvk: &groth16::PreparedVerifyingKey<Bls12>,
     proof: &groth16::Proof<Bls12>,
     proof: &groth16::Proof<Bls12>,
-    revealed: &MintRevealedValues
-    ) -> bool {
+    revealed: &MintRevealedValues,
+) -> bool {
     let public_input = revealed.make_outputs();
     let public_input = revealed.make_outputs();
 
 
     let start = Instant::now();
     let start = Instant::now();
@@ -129,4 +149,3 @@ pub fn verify_mint_proof(
     println!("Verify: [{:?}]", start.elapsed());
     println!("Verify: [{:?}]", start.elapsed());
     result
     result
 }
 }
-

+ 17 - 4
src/crypto/mod.rs

@@ -1,12 +1,21 @@
+pub mod coin;
+pub mod diffie_hellman;
+pub mod fr_serial;
+pub mod merkle;
 pub mod mint_proof;
 pub mod mint_proof;
+pub mod note;
+pub mod schnorr;
 pub mod spend_proof;
 pub mod spend_proof;
+pub mod util;
 
 
 use bellman::groth16;
 use bellman::groth16;
 use bls12_381::Bls12;
 use bls12_381::Bls12;
 
 
 use crate::error::Result;
 use crate::error::Result;
-pub use mint_proof::{setup_mint_prover, create_mint_proof, verify_mint_proof};
-pub use spend_proof::{setup_spend_prover, create_spend_proof, verify_spend_proof};
+pub use mint_proof::{create_mint_proof, setup_mint_prover, verify_mint_proof, MintRevealedValues};
+pub use spend_proof::{
+    create_spend_proof, setup_spend_prover, verify_spend_proof, SpendRevealedValues,
+};
 
 
 pub fn save_params(filename: &str, params: &groth16::Parameters<Bls12>) -> Result<()> {
 pub fn save_params(filename: &str, params: &groth16::Parameters<Bls12>) -> Result<()> {
     let buffer = std::fs::File::create(filename)?;
     let buffer = std::fs::File::create(filename)?;
@@ -14,10 +23,14 @@ pub fn save_params(filename: &str, params: &groth16::Parameters<Bls12>) -> Resul
     Ok(())
     Ok(())
 }
 }
 
 
-pub fn load_params(filename: &str) -> Result<(groth16::Parameters<Bls12>, groth16::PreparedVerifyingKey<Bls12>)> {
+pub fn load_params(
+    filename: &str,
+) -> Result<(
+    groth16::Parameters<Bls12>,
+    groth16::PreparedVerifyingKey<Bls12>,
+)> {
     let buffer = std::fs::File::open(filename)?;
     let buffer = std::fs::File::open(filename)?;
     let params = groth16::Parameters::<Bls12>::read(buffer, false)?;
     let params = groth16::Parameters::<Bls12>::read(buffer, false)?;
     let pvk = groth16::prepare_verifying_key(&params.vk);
     let pvk = groth16::prepare_verifying_key(&params.vk);
     Ok((params, pvk))
     Ok((params, pvk))
 }
 }
-

+ 116 - 0
src/crypto/note.rs

@@ -0,0 +1,116 @@
+use crypto_api_chachapoly::ChachaPolyIetf;
+use ff::Field;
+use std::io;
+use rand::rngs::OsRng;
+
+use crate::serial::{Encodable, Decodable, ReadExt, WriteExt};
+use crate::error::{Error, Result};
+use super::diffie_hellman::{sapling_ka_agree, kdf_sapling};
+
+pub const NOTE_PLAINTEXT_SIZE: usize =
+    32 + // serial
+    8 + // value
+    32 + // coin_blind
+    32; // valcom_blind
+pub const AEAD_TAG_SIZE: usize = 16;
+pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
+
+pub struct Note {
+    pub serial: jubjub::Fr,
+    pub value: u64,
+    pub coin_blind: jubjub::Fr,
+    pub valcom_blind: jubjub::Fr,
+}
+
+impl Encodable for Note {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.serial.encode(&mut s)?;
+        len += self.value.encode(&mut s)?;
+        len += self.coin_blind.encode(&mut s)?;
+        len += self.valcom_blind.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for Note {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            serial: Decodable::decode(&mut d)?,
+            value: Decodable::decode(&mut d)?,
+            coin_blind: Decodable::decode(&mut d)?,
+            valcom_blind: Decodable::decode(d)?
+        })
+    }
+}
+
+impl Note {
+    pub fn encrypt(&self, public: &jubjub::SubgroupPoint) -> Result<EncryptedNote> {
+        let ephem_secret = jubjub::Fr::random(&mut OsRng);
+        let ephem_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * ephem_secret;
+        let shared_secret = sapling_ka_agree(&ephem_secret, public.into());
+        let key = kdf_sapling(shared_secret, &ephem_public.into());
+
+        let mut input = Vec::new();
+        self.encode(&mut input)?;
+
+        let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
+        assert_eq!(
+            ChachaPolyIetf::aead_cipher()
+                .seal_to(&mut ciphertext, &input, &[], key.as_ref(), &[0u8; 12])
+                .unwrap(),
+            ENC_CIPHERTEXT_SIZE
+        );
+
+        Ok(EncryptedNote {
+            ciphertext,
+            ephem_public
+        })
+    }
+}
+
+pub struct EncryptedNote {
+    ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
+    ephem_public: jubjub::SubgroupPoint
+}
+
+impl EncryptedNote {
+    pub fn decrypt(&self, secret: &jubjub::Fr) -> Result<Note> {
+        let shared_secret = sapling_ka_agree(&secret, &self.ephem_public.into());
+        let key = kdf_sapling(shared_secret, &self.ephem_public.into());
+
+        let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
+        assert_eq!(
+            ChachaPolyIetf::aead_cipher()
+                .open_to(
+                    &mut plaintext,
+                    &self.ciphertext,
+                    &[],
+                    key.as_ref(),
+                    &[0u8; 12]
+                )
+                .map_err(|_| Error::NoteDecryptionFailed)?,
+            NOTE_PLAINTEXT_SIZE
+        );
+
+        Note::decode(&plaintext[..])
+    }
+}
+
+#[test]
+fn test_note_encdec() {
+    let note = Note {
+        serial: jubjub::Fr::random(&mut OsRng),
+        value: 110,
+        coin_blind: jubjub::Fr::random(&mut OsRng),
+        valcom_blind: jubjub::Fr::random(&mut OsRng),
+    };
+
+    let secret = jubjub::Fr::random(&mut OsRng);
+    let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
+
+    let encrypted_note = note.encrypt(&public).unwrap();
+    let note2 = encrypted_note.decrypt(&secret).unwrap();
+    assert_eq!(note.value, note2.value);
+}
+

+ 55 - 0
src/crypto/schnorr.rs

@@ -0,0 +1,55 @@
+use ff::Field;
+use group::{Group, GroupEncoding};
+use rand::rngs::OsRng;
+
+use super::util::hash_to_scalar;
+
+pub struct SecretKey(pub jubjub::Fr);
+
+impl SecretKey {
+    pub fn random() -> Self {
+        Self(jubjub::Fr::random(&mut OsRng))
+    }
+
+    pub fn sign(&self, message: &[u8]) -> Signature {
+        let mask = jubjub::Fr::random(&mut OsRng);
+        let commit = zcash_primitives::constants::SPENDING_KEY_GENERATOR * mask;
+
+        let challenge = hash_to_scalar(b"DarkFi_Schnorr", &commit.to_bytes(), message);
+
+        let response = mask + challenge * self.0;
+
+        Signature { commit, response }
+    }
+
+    pub fn public_key(&self) -> PublicKey {
+        let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * self.0;
+        PublicKey(public)
+    }
+}
+
+pub struct PublicKey(pub jubjub::SubgroupPoint);
+
+pub struct Signature {
+    commit: jubjub::SubgroupPoint,
+    response: jubjub::Fr,
+}
+
+impl PublicKey {
+    pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
+        let challenge = hash_to_scalar(b"DarkFi_Schnorr", &signature.commit.to_bytes(), message);
+        zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature.response
+            - self.0 * challenge
+            == signature.commit
+    }
+}
+
+#[test]
+fn test_schnorr() {
+    let secret = SecretKey::random();
+    let message = b"Foo bar";
+    let signature = secret.sign(&message[..]);
+    let public = secret.public_key();
+    assert!(public.verify(&message[..], &signature));
+}
+

+ 31 - 49
src/crypto/spend_proof.rs

@@ -1,5 +1,3 @@
-use rand::rngs::OsRng;
-use std::time::Instant;
 use bellman::gadgets::multipack;
 use bellman::gadgets::multipack;
 use bellman::groth16;
 use bellman::groth16;
 use bitvec::{order::Lsb0, view::AsBits};
 use bitvec::{order::Lsb0, view::AsBits};
@@ -7,46 +5,12 @@ use blake2s_simd::Params as Blake2sParams;
 use bls12_381::Bls12;
 use bls12_381::Bls12;
 use ff::{Field, PrimeField};
 use ff::{Field, PrimeField};
 use group::{Curve, GroupEncoding};
 use group::{Curve, GroupEncoding};
+use rand::rngs::OsRng;
+use std::time::Instant;
 
 
-use crate::error::Result;
 use crate::circuit::spend_contract::SpendContract;
 use crate::circuit::spend_contract::SpendContract;
-
-// This thing is nasty lol
-pub fn merkle_hash(
-    depth: usize,
-    lhs: &bls12_381::Scalar,
-    rhs: &bls12_381::Scalar,
-) -> bls12_381::Scalar {
-    let lhs = {
-        let mut tmp = [false; 256];
-        for (a, b) in tmp.iter_mut().zip(lhs.to_repr().as_bits::<Lsb0>()) {
-            *a = *b;
-        }
-        tmp
-    };
-
-    let rhs = {
-        let mut tmp = [false; 256];
-        for (a, b) in tmp.iter_mut().zip(rhs.to_repr().as_bits::<Lsb0>()) {
-            *a = *b;
-        }
-        tmp
-    };
-
-    jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash(
-        zcash_primitives::pedersen_hash::Personalization::MerkleTree(depth),
-        lhs.iter()
-            .copied()
-            .take(bls12_381::Scalar::NUM_BITS as usize)
-            .chain(
-                rhs.iter()
-                    .copied()
-                    .take(bls12_381::Scalar::NUM_BITS as usize),
-            ),
-    ))
-    .to_affine()
-    .get_u()
-}
+use super::coin::merkle_hash;
+use crate::error::Result;
 
 
 pub struct SpendRevealedValues {
 pub struct SpendRevealedValues {
     pub value_commit: jubjub::SubgroupPoint,
     pub value_commit: jubjub::SubgroupPoint,
@@ -54,6 +18,7 @@ pub struct SpendRevealedValues {
     // This should not be here, we just have it for debugging
     // This should not be here, we just have it for debugging
     //coin: [u8; 32],
     //coin: [u8; 32],
     pub merkle_root: bls12_381::Scalar,
     pub merkle_root: bls12_381::Scalar,
+    pub signature_public: jubjub::SubgroupPoint
 }
 }
 
 
 impl SpendRevealedValues {
 impl SpendRevealedValues {
@@ -64,6 +29,7 @@ impl SpendRevealedValues {
         randomness_coin: &jubjub::Fr,
         randomness_coin: &jubjub::Fr,
         secret: &jubjub::Fr,
         secret: &jubjub::Fr,
         merkle_path: &[(bls12_381::Scalar, bool)],
         merkle_path: &[(bls12_381::Scalar, bool)],
+        signature_secret: &jubjub::Fr,
     ) -> Self {
     ) -> Self {
         let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
         let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
             * jubjub::Fr::from(value))
             * jubjub::Fr::from(value))
@@ -83,6 +49,7 @@ impl SpendRevealedValues {
         );
         );
 
 
         let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
         let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
+        let signature_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature_secret;
 
 
         let mut coin = [0; 32];
         let mut coin = [0; 32];
         coin.copy_from_slice(
         coin.copy_from_slice(
@@ -108,9 +75,9 @@ impl SpendRevealedValues {
 
 
         for (i, (right, is_right)) in merkle_path.iter().enumerate() {
         for (i, (right, is_right)) in merkle_path.iter().enumerate() {
             if *is_right {
             if *is_right {
-                merkle_root = merkle_hash(i, &right, &merkle_root);
+                merkle_root = merkle_hash(i, &right.to_repr(), &merkle_root.to_repr());
             } else {
             } else {
-                merkle_root = merkle_hash(i, &merkle_root, &right);
+                merkle_root = merkle_hash(i, &merkle_root.to_repr(), &right.to_repr());
             }
             }
         }
         }
 
 
@@ -118,11 +85,12 @@ impl SpendRevealedValues {
             value_commit,
             value_commit,
             nullifier,
             nullifier,
             merkle_root,
             merkle_root,
+            signature_public
         }
         }
     }
     }
 
 
-    fn make_outputs(&self) -> [bls12_381::Scalar; 5] {
-        let mut public_input = [bls12_381::Scalar::zero(); 5];
+    fn make_outputs(&self) -> [bls12_381::Scalar; 7] {
+        let mut public_input = [bls12_381::Scalar::zero(); 7];
 
 
         // CV
         // CV
         {
         {
@@ -164,6 +132,16 @@ impl SpendRevealedValues {
 
 
         public_input[4] = self.merkle_root;
         public_input[4] = self.merkle_root;
 
 
+        {
+            let result = jubjub::ExtendedPoint::from(self.signature_public);
+            let affine = result.to_affine();
+            //let (u, v) = (affine.get_u(), affine.get_v());
+            let u = affine.get_u();
+            let v = affine.get_v();
+            public_input[5] = u;
+            public_input[6] = v;
+        }
+
         public_input
         public_input
     }
     }
 }
 }
@@ -187,6 +165,8 @@ pub fn setup_spend_prover() -> groth16::Parameters<Bls12> {
             is_right_2: None,
             is_right_2: None,
             branch_3: None,
             branch_3: None,
             is_right_3: None,
             is_right_3: None,
+
+            signature_secret: None,
         };
         };
         groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
         groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
     };
     };
@@ -201,8 +181,9 @@ pub fn create_spend_proof(
     serial: jubjub::Fr,
     serial: jubjub::Fr,
     randomness_coin: jubjub::Fr,
     randomness_coin: jubjub::Fr,
     secret: jubjub::Fr,
     secret: jubjub::Fr,
-    merkle_path: [(bls12_381::Scalar, bool); 4]
-    ) -> (groth16::Proof<Bls12>, SpendRevealedValues) {
+    merkle_path: [(bls12_381::Scalar, bool); 4],
+    signature_secret: jubjub::Fr,
+) -> (groth16::Proof<Bls12>, SpendRevealedValues) {
     let c = SpendContract {
     let c = SpendContract {
         value: Some(value),
         value: Some(value),
         randomness_value: Some(randomness_value),
         randomness_value: Some(randomness_value),
@@ -218,6 +199,7 @@ pub fn create_spend_proof(
         is_right_2: Some(merkle_path[2].1),
         is_right_2: Some(merkle_path[2].1),
         branch_3: Some(merkle_path[3].0),
         branch_3: Some(merkle_path[3].0),
         is_right_3: Some(merkle_path[3].1),
         is_right_3: Some(merkle_path[3].1),
+        signature_secret: Some(signature_secret),
     };
     };
 
 
     let start = Instant::now();
     let start = Instant::now();
@@ -231,6 +213,7 @@ pub fn create_spend_proof(
         &randomness_coin,
         &randomness_coin,
         &secret,
         &secret,
         &merkle_path,
         &merkle_path,
+        &signature_secret
     );
     );
 
 
     (proof, revealed)
     (proof, revealed)
@@ -239,8 +222,8 @@ pub fn create_spend_proof(
 pub fn verify_spend_proof(
 pub fn verify_spend_proof(
     pvk: &groth16::PreparedVerifyingKey<Bls12>,
     pvk: &groth16::PreparedVerifyingKey<Bls12>,
     proof: &groth16::Proof<Bls12>,
     proof: &groth16::Proof<Bls12>,
-    revealed: &SpendRevealedValues
-    ) -> bool {
+    revealed: &SpendRevealedValues,
+) -> bool {
     let public_input = revealed.make_outputs();
     let public_input = revealed.make_outputs();
 
 
     let start = Instant::now();
     let start = Instant::now();
@@ -248,4 +231,3 @@ pub fn verify_spend_proof(
     println!("Verify: [{:?}]", start.elapsed());
     println!("Verify: [{:?}]", start.elapsed());
     result
     result
 }
 }
-

+ 9 - 0
src/crypto/util.rs

@@ -0,0 +1,9 @@
+use blake2b_simd::Params;
+
+pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr {
+    let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
+    hasher.update(a);
+    hasher.update(b);
+    let ret = hasher.finalize();
+    jubjub::Fr::from_bytes_wide(ret.as_array())
+}

+ 2 - 0
src/error.rs

@@ -43,6 +43,7 @@ pub enum Error {
     ChannelTimeout,
     ChannelTimeout,
     ServiceStopped,
     ServiceStopped,
     Utf8Error,
     Utf8Error,
+    NoteDecryptionFailed,
 }
 }
 
 
 impl std::error::Error for Error {}
 impl std::error::Error for Error {}
@@ -86,6 +87,7 @@ impl fmt::Display for Error {
             Error::ChannelTimeout => f.write_str("Channel timed out"),
             Error::ChannelTimeout => f.write_str("Channel timed out"),
             Error::ServiceStopped => f.write_str("Service stopped"),
             Error::ServiceStopped => f.write_str("Service stopped"),
             Error::Utf8Error => f.write_str("Malformed UTF8"),
             Error::Utf8Error => f.write_str("Malformed UTF8"),
+            Error::NoteDecryptionFailed => f.write_str("Unable to decrypt mint note"),
         }
         }
     }
     }
 }
 }

+ 1 - 0
src/lib.rs

@@ -13,6 +13,7 @@ pub mod gui;
 pub mod net;
 pub mod net;
 pub mod serial;
 pub mod serial;
 pub mod system;
 pub mod system;
+pub mod tx;
 pub mod vm;
 pub mod vm;
 pub mod vm_serial;
 pub mod vm_serial;
 pub mod service;
 pub mod service;

+ 191 - 0
src/tx.rs

@@ -0,0 +1,191 @@
+use std::io;
+use bellman::groth16;
+use bls12_381::Bls12;
+use ff::Field;
+use group::Group;
+use rand::rngs::OsRng;
+
+use crate::crypto::{
+    create_mint_proof, load_params, save_params, setup_mint_prover, verify_mint_proof,
+    MintRevealedValues,
+    note::Note
+};
+use crate::serial::{Decodable, Encodable, VarInt};
+use crate::error::{Error, Result};
+use crate::impl_vec;
+
+pub struct TransactionBuilder {
+    pub clear_inputs: Vec<TransactionBuilderClearInputInfo>,
+    pub outputs: Vec<TransactionBuilderOutputInfo>,
+}
+
+impl TransactionBuilder {
+    fn compute_remainder_blind(
+        clear_inputs: &Vec<TransactionClearInput>,
+        output_blinds: &Vec<jubjub::Fr>,
+    ) -> jubjub::Fr {
+        let mut lhs_total = jubjub::Fr::zero();
+        for input in clear_inputs {
+            lhs_total += input.valcom_blind;
+        }
+
+        let mut rhs_total = jubjub::Fr::zero();
+        for output_blind in output_blinds {
+            rhs_total += output_blind;
+        }
+
+        lhs_total - rhs_total
+    }
+
+    pub fn build(self, mint_params: &groth16::Parameters<Bls12>) -> Transaction {
+        let mut clear_inputs = vec![];
+        for input in &self.clear_inputs {
+            let valcom_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+            let clear_input = TransactionClearInput {
+                value: input.value,
+                valcom_blind,
+            };
+            clear_inputs.push(clear_input);
+        }
+
+        let mut outputs = vec![];
+        let mut output_blinds = vec![];
+        for (i, output) in self.outputs.iter().enumerate() {
+            let valcom_blind = if i == self.outputs.len() - 1 {
+                Self::compute_remainder_blind(&clear_inputs, &output_blinds)
+            } else {
+                jubjub::Fr::random(&mut OsRng)
+            };
+            output_blinds.push(valcom_blind);
+
+            let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+            let coin_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+
+            let (mint_proof, revealed) = create_mint_proof(
+                mint_params,
+                output.value,
+                valcom_blind,
+                serial,
+                coin_blind,
+                output.public,
+            );
+            let output = TransactionOutput {
+                mint_proof,
+                revealed,
+            };
+            outputs.push(output);
+        }
+
+        Transaction {
+            clear_inputs,
+            outputs,
+        }
+    }
+}
+
+pub struct TransactionBuilderClearInputInfo {
+    pub value: u64,
+}
+
+pub struct TransactionBuilderOutputInfo {
+    pub value: u64,
+    pub public: jubjub::SubgroupPoint,
+}
+
+pub struct Transaction {
+    pub clear_inputs: Vec<TransactionClearInput>,
+    pub outputs: Vec<TransactionOutput>,
+}
+
+impl Encodable for Transaction {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.clear_inputs.encode(&mut s)?;
+        len += self.outputs.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for Transaction {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            clear_inputs: Decodable::decode(&mut d)?,
+            outputs: Decodable::decode(d)?
+        })
+    }
+}
+
+impl Transaction {
+    fn compute_value_commit(value: u64, blind: &jubjub::Fr) -> jubjub::SubgroupPoint {
+        let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
+            * jubjub::Fr::from(value))
+            + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * blind);
+        value_commit
+    }
+
+    pub fn verify(&self, pvk: &groth16::PreparedVerifyingKey<Bls12>) -> bool {
+        let mut valcom_total = jubjub::SubgroupPoint::identity();
+        for input in &self.clear_inputs {
+            valcom_total += Self::compute_value_commit(input.value, &input.valcom_blind);
+        }
+        for output in &self.outputs {
+            if !verify_mint_proof(pvk, &output.mint_proof, &output.revealed) {
+                return false;
+            }
+            valcom_total -= &output.revealed.value_commit;
+        }
+
+        valcom_total == jubjub::SubgroupPoint::identity()
+    }
+}
+
+pub struct TransactionClearInput {
+    pub value: u64,
+    pub valcom_blind: jubjub::Fr,
+}
+
+impl_vec!(TransactionClearInput);
+
+impl Encodable for TransactionClearInput {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.value.encode(&mut s)?;
+        len += self.valcom_blind.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for TransactionClearInput {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            value: Decodable::decode(&mut d)?,
+            valcom_blind: Decodable::decode(d)?
+        })
+    }
+}
+
+pub struct TransactionOutput {
+    pub mint_proof: groth16::Proof<Bls12>,
+    pub revealed: MintRevealedValues,
+}
+
+impl_vec!(TransactionOutput);
+
+impl Encodable for TransactionOutput {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.mint_proof.encode(&mut s)?;
+        len += self.revealed.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for TransactionOutput {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            mint_proof: Decodable::decode(&mut d)?,
+            revealed: Decodable::decode(d)?
+        })
+    }
+}
+