فهرست منبع

daod: add support for native zk contracts, and migrate money::transfer() to new FuncCall structure

narodnik 4 سال پیش
والد
کامیت
4e7f05b2af

+ 9 - 1
bin/daod/src/dao_contract/mint/builder.rs

@@ -11,7 +11,10 @@ use halo2_proofs::circuit::Value;
 use pasta_curves::{arithmetic::CurveAffine, group::Curve, pallas};
 use rand::rngs::OsRng;
 
-use crate::{dao_contract::mint::validate::CallData, demo::FuncCall, CallDataBase, ZkBinaryTable};
+use crate::{
+    dao_contract::mint::validate::CallData, demo::FuncCall, CallDataBase, ZkBinaryTable,
+    ZkContractInfo,
+};
 
 pub struct Builder {
     dao_proposer_limit: u64,
@@ -70,6 +73,11 @@ impl Builder {
 
         // Now create the mint proof
         let zk_info = zk_bins.lookup(&"dao-mint".to_string()).unwrap();
+        let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
+            info
+        } else {
+            panic!("Not binary info")
+        };
         let zk_bin = zk_info.bincode.clone();
         let prover_witnesses = vec![
             Witness::Base(Value::known(dao_proposer_limit)),

+ 58 - 26
bin/daod/src/demo.rs

@@ -44,14 +44,27 @@ use darkfi::{
 
 use crate::{dao_contract, money_contract};
 
+// TODO: reenable unused vars warning and fix it
+// TODO: strategize and cleanup Result/Error usage
+// TODO: fix up code doc
+
 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
 
-pub struct ZkContractInfo {
+pub struct ZkBinaryContractInfo {
     pub k_param: u32,
     pub bincode: ZkBinary,
     pub proving_key: ProvingKey,
     pub verifying_key: VerifyingKey,
 }
+pub struct ZkNativeContractInfo {
+    pub proving_key: ProvingKey,
+    pub verifying_key: VerifyingKey,
+}
+
+pub enum ZkContractInfo {
+    Binary(ZkBinaryContractInfo),
+    Native(ZkNativeContractInfo),
+}
 
 pub struct ZkBinaryTable {
     // Key will be a hash of zk binary contract on chain
@@ -68,10 +81,22 @@ impl ZkBinaryTable {
         let circuit = ZkCircuit::new(witnesses, bincode.clone());
         let proving_key = ProvingKey::build(k_param, &circuit);
         let verifying_key = VerifyingKey::build(k_param, &circuit);
-        let info = ZkContractInfo { k_param, bincode, proving_key, verifying_key };
+        let info = ZkContractInfo::Binary(ZkBinaryContractInfo {
+            k_param,
+            bincode,
+            proving_key,
+            verifying_key,
+        });
         self.table.insert(key, info);
     }
 
+    fn add_native(&mut self, key: String, proving_key: ProvingKey, verifying_key: VerifyingKey) {
+        self.table.insert(
+            key,
+            ZkContractInfo::Native(ZkNativeContractInfo { proving_key, verifying_key }),
+        );
+    }
+
     pub fn lookup(&self, key: &String) -> Option<&ZkContractInfo> {
         self.table.get(key)
     }
@@ -90,9 +115,9 @@ pub struct Transaction {
 }
 
 impl Transaction {
-    /// TODO: what should this return? plonk error?
     /// Verify ZK contracts for the entire tx
     /// In real code, we could parallelize this for loop
+    /// TODO: fix use of unwrap with Result type stuff
     fn zk_verify(&self, zk_bins: &ZkBinaryTable) {
         for func_call in &self.func_calls {
             let proofs_public_vals = &func_call.call_data.zk_public_values();
@@ -102,9 +127,16 @@ impl Transaction {
             for (key, (proof, public_vals)) in
                 zip!(proofs_keys, &func_call.proofs, proofs_public_vals)
             {
-                let zk_info = zk_bins.lookup(key).unwrap();
-                let verifying_key = &zk_info.verifying_key;
-                proof.verify(&verifying_key, public_vals).expect("verify DAO::mint() failed!");
+                match zk_bins.lookup(key).unwrap() {
+                    ZkContractInfo::Binary(info) => {
+                        let verifying_key = &info.verifying_key;
+                        proof.verify(&verifying_key, public_vals).expect("verify zk proof failed!");
+                    }
+                    ZkContractInfo::Native(info) => {
+                        let verifying_key = &info.verifying_key;
+                        proof.verify(&verifying_key, public_vals).expect("verify zk proof failed!");
+                    }
+                };
                 debug!("zk_verify({}) passed", key);
             }
         }
@@ -182,6 +214,24 @@ pub async fn demo() -> Result<()> {
     let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
     zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
 
+    {
+        let start = Instant::now();
+        let mint_pk = ProvingKey::build(11, &MintContract::default());
+        debug!("Mint PK: [{:?}]", start.elapsed());
+        let start = Instant::now();
+        let burn_pk = ProvingKey::build(11, &BurnContract::default());
+        debug!("Burn PK: [{:?}]", start.elapsed());
+        let start = Instant::now();
+        let mint_vk = VerifyingKey::build(11, &MintContract::default());
+        debug!("Mint VK: [{:?}]", start.elapsed());
+        let start = Instant::now();
+        let burn_vk = VerifyingKey::build(11, &BurnContract::default());
+        debug!("Burn VK: [{:?}]", start.elapsed());
+
+        zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
+        zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
+    }
+
     // State for money contracts
     let cashier_signature_secret = SecretKey::random(&mut OsRng);
     let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
@@ -190,19 +240,10 @@ pub async fn demo() -> Result<()> {
 
     ///////////////////////////////////////////////////
 
-    let start = Instant::now();
-    let mint_vk = VerifyingKey::build(11, &MintContract::default());
-    debug!("Mint VK: [{:?}]", start.elapsed());
-    let start = Instant::now();
-    let burn_vk = VerifyingKey::build(11, &BurnContract::default());
-    debug!("Burn VK: [{:?}]", start.elapsed());
-
     let money_state = Box::new(money_contract::state::State {
         tree: BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100),
         merkle_roots: vec![],
         nullifiers: vec![],
-        mint_vk,
-        burn_vk,
         cashier_signature_public,
         faucet_signature_public,
     });
@@ -275,16 +316,7 @@ pub async fn demo() -> Result<()> {
         }],
     };
 
-    let start = Instant::now();
-    let mint_pk = ProvingKey::build(11, &MintContract::default());
-    debug!("Mint PK: [{:?}]", start.elapsed());
-    let start = Instant::now();
-    let burn_pk = ProvingKey::build(11, &BurnContract::default());
-    debug!("Burn PK: [{:?}]", start.elapsed());
-
-    let func_call = builder.build(&mint_pk, &burn_pk)?;
-
-    //tx.verify(&money_state.mint_vk, &money_state.burn_vk)?;
+    let func_call = builder.build(&zk_bins)?;
 
     let tx = Transaction { func_calls: vec![func_call] };
 
@@ -301,7 +333,7 @@ pub async fn demo() -> Result<()> {
         }
     }
 
-    //tx.zk_verify(&zk_bins);
+    tx.zk_verify(&zk_bins);
 
     ///////////////////////////////////////////////////
 

+ 5 - 17
bin/daod/src/money_contract/state.rs

@@ -17,10 +17,6 @@ pub struct State {
     pub merkle_roots: Vec<MerkleNode>,
     /// Nullifiers prevent double spending
     pub nullifiers: Vec<Nullifier>,
-    /// Verifying key for the mint zk circuit.
-    pub mint_vk: VerifyingKey,
-    /// Verifying key for the burn zk circuit.
-    pub burn_vk: VerifyingKey,
 
     /// Public key of the cashier
     pub cashier_signature_public: PublicKey,
@@ -29,28 +25,20 @@ pub struct State {
     pub faucet_signature_public: PublicKey,
 }
 
-impl ProgramState for State {
-    fn is_valid_cashier_public_key(&self, public: &PublicKey) -> bool {
+impl State {
+    pub fn is_valid_cashier_public_key(&self, public: &PublicKey) -> bool {
         public == &self.cashier_signature_public
     }
 
-    fn is_valid_faucet_public_key(&self, public: &PublicKey) -> bool {
+    pub fn is_valid_faucet_public_key(&self, public: &PublicKey) -> bool {
         public == &self.faucet_signature_public
     }
 
-    fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool {
+    pub 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 {
+    pub fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
         self.nullifiers.iter().any(|n| n == nullifier)
     }
-
-    fn mint_vk(&self) -> &VerifyingKey {
-        &self.mint_vk
-    }
-
-    fn burn_vk(&self) -> &VerifyingKey {
-        &self.burn_vk
-    }
 }

+ 26 - 6
bin/daod/src/money_contract/transfer/builder.rs

@@ -20,6 +20,7 @@ use super::partial::{Partial, PartialClearInput, PartialInput};
 use crate::{
     demo::FuncCall,
     money_contract::transfer::validate::{CallData, ClearInput, Input, Output},
+    ZkBinaryTable, ZkContractInfo,
 };
 
 pub struct Builder {
@@ -70,7 +71,7 @@ impl Builder {
         total
     }
 
-    pub fn build(self, mint_pk: &ProvingKey, burn_pk: &ProvingKey) -> Result<FuncCall> {
+    pub fn build(self, zk_bins: &ZkBinaryTable) -> Result<FuncCall> {
         assert!(self.clear_inputs.len() + self.inputs.len() > 0);
 
         let mut clear_inputs = vec![];
@@ -89,6 +90,8 @@ impl Builder {
             clear_inputs.push(clear_input);
         }
 
+        let mut proofs = vec![];
+
         let mut inputs = vec![];
         let mut input_blinds = vec![];
         let mut signature_secrets = vec![];
@@ -98,7 +101,15 @@ impl Builder {
 
             let signature_secret = SecretKey::random(&mut OsRng);
 
-            let (proof, revealed) = create_burn_proof(
+            let zk_info = zk_bins.lookup(&"money-transfer-burn".to_string()).unwrap();
+            let zk_info = if let ZkContractInfo::Native(info) = zk_info {
+                info
+            } else {
+                panic!("Not native info")
+            };
+            let burn_pk = &zk_info.proving_key;
+
+            let (burn_proof, revealed) = create_burn_proof(
                 burn_pk,
                 input.note.value,
                 input.note.token_id,
@@ -111,11 +122,12 @@ impl Builder {
                 input.merkle_path,
                 signature_secret,
             )?;
+            proofs.push(burn_proof);
 
             // First we make the tx then sign after
             signature_secrets.push(signature_secret);
 
-            let input = PartialInput { burn_proof: proof, revealed };
+            let input = PartialInput { revealed };
             inputs.push(input);
         }
 
@@ -135,6 +147,14 @@ impl Builder {
             let serial = DrkSerial::random(&mut OsRng);
             let coin_blind = DrkCoinBlind::random(&mut OsRng);
 
+            let zk_info = zk_bins.lookup(&"money-transfer-mint".to_string()).unwrap();
+            let zk_info = if let ZkContractInfo::Native(info) = zk_info {
+                info
+            } else {
+                panic!("Not native info")
+            };
+            let mint_pk = &zk_info.proving_key;
+
             let (mint_proof, revealed) = create_mint_proof(
                 mint_pk,
                 output.value,
@@ -145,6 +165,7 @@ impl Builder {
                 coin_blind,
                 output.public,
             )?;
+            proofs.push(mint_proof);
 
             // Encrypted note
             let note = Note {
@@ -159,7 +180,7 @@ impl Builder {
 
             let encrypted_note = note.encrypt(&output.public)?;
 
-            let output = Output { mint_proof, revealed, enc_note: encrypted_note };
+            let output = Output { revealed, enc_note: encrypted_note };
             outputs.push(output);
         }
 
@@ -187,12 +208,11 @@ impl Builder {
 
         let call_data = CallData { clear_inputs, inputs, outputs: partial_tx.outputs };
 
-        // TODO: Proofs is an empty vector right now.
         Ok(FuncCall {
             contract_id: "money".to_string(),
             func_id: "money::transfer()".to_string(),
             call_data: Box::new(call_data),
-            proofs: vec![],
+            proofs,
         })
     }
 }

+ 3 - 1
bin/daod/src/money_contract/transfer/partial.rs

@@ -14,6 +14,7 @@ pub struct Partial {
     pub clear_inputs: Vec<PartialClearInput>,
     pub inputs: Vec<PartialInput>,
     pub outputs: Vec<Output>,
+    //pub proofs: Vec<Proof>,
 }
 
 #[derive(Clone, SerialEncodable, SerialDecodable)]
@@ -27,6 +28,7 @@ pub struct PartialClearInput {
 
 #[derive(Clone, SerialEncodable, SerialDecodable)]
 pub struct PartialInput {
-    pub burn_proof: Proof,
+    // TODO: BUG BUG FIXME!!!
+    //pub burn_proof: Proof,
     pub revealed: BurnRevealedValues,
 }

+ 26 - 38
bin/daod/src/money_contract/transfer/validate.rs

@@ -37,6 +37,8 @@ use crate::{
     },
 };
 
+const TARGET: &str = "money_contract::transfer::validate::state_transition()";
+
 /// A struct representing a state update.
 /// This gets applied on top of an existing state.
 #[derive(Clone)]
@@ -73,7 +75,7 @@ pub fn state_transition(
 ) -> Result<Update> {
     // Check the public keys in the clear inputs to see if they're coming
     // from a valid cashier or faucet.
-    debug!(target: "money_contract::mint::validate::state_transition", "Iterate clear_inputs");
+    debug!(target: TARGET, "Iterate clear_inputs");
     let func_call = &parent_tx.func_calls[func_call_index];
     let call_data = func_call.call_data.as_any();
 
@@ -92,7 +94,7 @@ pub fn state_transition(
         let pk = &input.signature_public;
         // TODO: this depends on the token ID
         if !state.is_valid_cashier_public_key(pk) && !state.is_valid_faucet_public_key(pk) {
-            error!(target: "money_contract::mint::validate::state_transition", "Invalid pubkey for clear input: {:?}", pk);
+            error!(target: TARGET, "Invalid pubkey for clear input: {:?}", pk);
             return Err(Error::VerifyFailed(VerifyFailed::InvalidCashierOrFaucetKey(i)))
         }
     }
@@ -100,15 +102,15 @@ pub fn state_transition(
     // Nullifiers in the transaction
     let mut nullifiers = Vec::with_capacity(call_data.inputs.len());
 
-    debug!(target: "money_contract::mint::validate::state_transition", "Iterate inputs");
+    debug!(target: TARGET, "Iterate inputs");
     for (i, input) in call_data.inputs.iter().enumerate() {
         let merkle = &input.revealed.merkle_root;
 
         // The Merkle root is used to know whether this is a coin that
         // existed in a previous state.
         if !state.is_valid_merkle(merkle) {
-            error!(target: "money_contract::mint::validate::state_transition", "Invalid Merkle root (input {})", i);
-            debug!(target: "money_contract::mint::validate::state_transition", "root: {:?}", merkle);
+            error!(target: TARGET, "Invalid Merkle root (input {})", i);
+            debug!(target: TARGET, "root: {:?}", merkle);
             return Err(Error::VerifyFailed(VerifyFailed::InvalidMerkle(i)))
         }
 
@@ -118,21 +120,21 @@ pub fn state_transition(
         if state.nullifier_exists(nullifier) ||
             (1..nullifiers.len()).any(|i| nullifiers[i..].contains(&nullifiers[i - 1]))
         {
-            error!(target: "money_contract::mint::validate::state_transition", "Duplicate nullifier found (input {})", i);
-            debug!(target: "money_contract::mint::validate::state_transition", "nullifier: {:?}", nullifier);
+            error!(target: TARGET, "Duplicate nullifier found (input {})", i);
+            debug!(target: TARGET, "nullifier: {:?}", nullifier);
             return Err(Error::VerifyFailed(VerifyFailed::NullifierExists(i)))
         }
 
         nullifiers.push(input.revealed.nullifier);
     }
 
-    debug!(target: "money_contract::mint::validate::state_transition", "Verifying zk proofs");
-    match call_data.verify(state.mint_vk(), state.burn_vk()) {
+    debug!(target: TARGET, "Verifying zk proofs");
+    match call_data.verify() {
         Ok(()) => {
-            debug!(target: "money_contract::mint::validate::state_transition", "Verified successfully")
+            debug!(target: TARGET, "Verified successfully")
         }
         Err(e) => {
-            error!(target: "money_contract::mint::validate::state_transition", "Failed verifying zk proofs: {}", e);
+            error!(target: TARGET, "Failed verifying zk proofs: {}", e);
             return Err(Error::VerifyFailed(VerifyFailed::ProofVerifyFailed(e.to_string())))
         }
     }
@@ -190,7 +192,7 @@ impl CallDataBase for CallData {
 }
 impl CallData {
     /// Verify the transaction
-    pub fn verify(&self, mint_vk: &VerifyingKey, burn_vk: &VerifyingKey) -> VerifyResult<()> {
+    pub fn verify(&self) -> VerifyResult<()> {
         //  must have minimum 1 clear or anon input, and 1 output
         if self.clear_inputs.len() + self.inputs.len() == 0 {
             error!("tx::verify(): Missing inputs");
@@ -208,27 +210,13 @@ impl CallData {
         for input in &self.clear_inputs {
             valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
         }
-
         // Add values from the inputs
-        for (i, input) in self.inputs.iter().enumerate() {
-            match verify_burn_proof(burn_vk, &input.burn_proof, &input.revealed) {
-                Ok(()) => valcom_total += &input.revealed.value_commit,
-                Err(e) => {
-                    error!("tx::verify(): Failed to verify burn proof {}: {}", i, e);
-                    return Err(VerifyFailed::BurnProof(i))
-                }
-            }
+        for input in &self.inputs {
+            valcom_total += &input.revealed.value_commit;
         }
-
         // Subtract values from the outputs
-        for (i, output) in self.outputs.iter().enumerate() {
-            match verify_mint_proof(mint_vk, &output.mint_proof, &output.revealed) {
-                Ok(()) => valcom_total -= &output.revealed.value_commit,
-                Err(e) => {
-                    error!("tx::verify(): Failed to verify mint proof {}: {}", i, e);
-                    return Err(VerifyFailed::MintProof(i))
-                }
-            }
+        for output in &self.outputs {
+            valcom_total -= &output.revealed.value_commit;
         }
 
         // If the accumulator is not back in its initial state,
@@ -246,6 +234,7 @@ impl CallData {
 
         // Verify the available signatures
         let mut unsigned_tx_data = vec![];
+        // TODO: BUG!!! MUST INCLUDE PROOFS!!!
         self.encode_without_signature(&mut unsigned_tx_data)?;
 
         for (i, input) in self.clear_inputs.iter().enumerate() {
@@ -267,11 +256,15 @@ impl CallData {
         Ok(())
     }
 
-    pub fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
+    pub fn encode_without_signature<S: io::Write>(
+        &self,
+        mut s: S, /*proofs: &Vec<Proof>*/
+    ) -> Result<usize> {
         let mut len = 0;
         len += self.clear_inputs.encode_without_signature(&mut s)?;
         len += self.inputs.encode_without_signature(&mut s)?;
-        len += self.outputs.encode(s)?;
+        len += self.outputs.encode(&mut s)?;
+        //len += proofs.encode(s)?;
         Ok(len)
     }
 
@@ -313,8 +306,6 @@ pub struct ClearInput {
 /// A transaction's anonymous input
 #[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
 pub struct Input {
-    /// Zero-knowledge proof for the input
-    pub burn_proof: Proof,
     /// Public inputs for the zero-knowledge proof
     pub revealed: BurnRevealedValues,
     /// Input's signature
@@ -324,8 +315,6 @@ pub struct Input {
 /// A transaction's anonymous output
 #[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
 pub struct Output {
-    /// Zero-knowledge proof for the output
-    pub mint_proof: Proof,
     /// Public inputs for the zero-knowledge proof
     pub revealed: MintRevealedValues,
     /// The encrypted note
@@ -357,12 +346,11 @@ impl ClearInput {
 
 impl Input {
     pub fn from_partial(partial: PartialInput, signature: schnorr::Signature) -> Self {
-        Self { burn_proof: partial.burn_proof, revealed: partial.revealed, signature }
+        Self { revealed: partial.revealed, signature }
     }
 
     fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
-        len += self.burn_proof.encode(&mut s)?;
         len += self.revealed.encode(&mut s)?;
         Ok(len)
     }