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

daod: fix errors in contracts + add some validate.rs boilerplate

lunar-mining 4 лет назад
Родитель
Сommit
6ca5b6f5f0

+ 8 - 15
bin/daod/proof/dao-vote-burn.zk

@@ -5,31 +5,24 @@ constant "DaoVoteInput" {
 }
 
 contract "DaoVoteInput" {
-	Base value,
-	Scalar value_blind,
-
-	Base gov_token_id,
-	Base gov_token_blind,
-
+	Base secret,
 	Base serial,
-
 	Base spend_hook,
 	Base user_data,
-
+	Base value,
+	Base gov_token_id,
+	Base coin_blind,
+	Scalar value_blind,
+	Base gov_token_blind,
 	Uint32 leaf_pos,
 	MerklePath path,
-
-    Base all_coins,
-	Base coin_blind,
-
-	Base secret,
 	Base signature_secret,
 }
 
 circuit "DaoVoteInput" {
 	# Poseidon hash of the nullifier
-	nullifier = poseidon_hash(secret, serial);
-	constrain_instance(nullifier);
+	# nullifier = poseidon_hash(secret, serial);
+	# constrain_instance(nullifier);
 
 	# Pedersen commitment for coin's value
 	vcv = ec_mul_short(value, VALUE_COMMIT_VALUE);

+ 2 - 5
bin/daod/proof/dao-vote-main.zk

@@ -1,9 +1,6 @@
 constant "DaoVoteMain" {
 	EcFixedPointShort VALUE_COMMIT_VALUE,
 	EcFixedPoint VALUE_COMMIT_RANDOM,
-
-	EcFixedPointShort VOTE_COMMIT_OPTION,
-	EcFixedPoint VOTE_COMMIT_RANDOM,
 }
 
 contract "DaoVoteMain" {
@@ -27,8 +24,8 @@ circuit "DaoVoteMain" {
 	# Pedersen commitment for vote option
     # Make the weighted vote
     wv = base_mul(vote_option, total_value);
-	vco = ec_mul_short(wv, VOTE_COMMIT_OPTION);
-	vcr = ec_mul(vote_option_blind, VOTE_COMMIT_RANDOM);
+	vco = ec_mul_short(wv, VALUE_COMMIT_VALUE);
+	vcr = ec_mul(vote_option_blind, VALUE_COMMIT_RANDOM);
 	total_vote_commit = ec_add(vco, vcr);
 	# Since total_vote_commit is a curve point, we fetch its coordinates
 	# and constrain them:

+ 2 - 0
bin/daod/src/dao_contract/state.rs

@@ -85,6 +85,8 @@ impl State {
         });
     }
 
+    //pub fn add_proposal_vote(&mut self,
+
     pub fn is_valid_dao_merkle(&self, root: &MerkleNode) -> bool {
         self.dao_roots.iter().any(|m| m == root)
     }

+ 123 - 2
bin/daod/src/dao_contract/vote/validate.rs

@@ -21,6 +21,28 @@ use crate::{
     note::EncryptedNote2,
 };
 
+#[derive(Debug, Clone, thiserror::Error)]
+pub enum Error {
+    #[error("Invalid input merkle root")]
+    InvalidInputMerkleRoot,
+
+    #[error("Invalid DAO merkle root")]
+    InvalidDaoMerkleRoot,
+
+    #[error("Signature verification failed")]
+    SignatureVerifyFailed,
+
+    #[error("DarkFi error: {0}")]
+    DarkFiError(String),
+}
+type Result<T> = std::result::Result<T, Error>;
+
+impl From<DarkFiError> for Error {
+    fn from(err: DarkFiError) -> Self {
+        Self::DarkFiError(err.to_string())
+    }
+}
+
 pub struct CallData {
     pub header: Header,
     pub inputs: Vec<Input>,
@@ -29,11 +51,55 @@ pub struct CallData {
 
 impl CallDataBase for CallData {
     fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
-        vec![]
+        let mut zk_publics = Vec::new();
+        let mut total_funds_commit = pallas::Point::identity();
+
+        assert!(self.inputs.len() > 0, "inputs length cannot be zero");
+        for input in &self.inputs {
+            total_funds_commit += input.value_commit;
+            let value_coords = input.value_commit.to_affine().coordinates().unwrap();
+            let value_commit_x = *value_coords.x();
+            let value_commit_y = *value_coords.y();
+
+            let sigpub_coords = input.signature_public.0.to_affine().coordinates().unwrap();
+            let sigpub_x = *sigpub_coords.x();
+            let sigpub_y = *sigpub_coords.y();
+
+            zk_publics.push(vec![
+                value_commit_x,
+                value_commit_y,
+                //TODO: self.header.token_commit,
+                input.merkle_root.0,
+                sigpub_x,
+                sigpub_y,
+            ]);
+        }
+
+        let total_funds_coords = total_funds_commit.to_affine().coordinates().unwrap();
+        let total_funds_x = *total_funds_coords.x();
+        let total_funds_y = *total_funds_coords.y();
+        zk_publics.push(
+            // dao-propose-main proof
+            vec![
+                //self.header.token_commit,
+                //self.header.dao_merkle_root.0,
+                //self.header.proposal_bulla,
+                total_funds_x,
+                total_funds_y,
+            ],
+        );
+
+        zk_publics
+        //vec![]
     }
 
     fn zk_proof_addrs(&self) -> Vec<String> {
-        vec![]
+        let mut zk_addrs = Vec::new();
+        for input in &self.inputs {
+            zk_addrs.push("dao-vote-burn".to_string());
+        }
+        zk_addrs.push("dao-voe-main".to_string());
+        zk_addrs
     }
 
     fn as_any(&self) -> &dyn Any {
@@ -52,3 +118,58 @@ pub struct Input {
     pub merkle_root: MerkleNode,
     pub signature_public: PublicKey,
 }
+
+pub fn state_transition(
+    states: &StateRegistry,
+    func_call_index: usize,
+    parent_tx: &Transaction,
+) -> Result<()> {
+    let func_call = &parent_tx.func_calls[func_call_index];
+    let call_data = func_call.call_data.as_any();
+
+    assert_eq!((&*call_data).type_id(), TypeId::of::<CallData>());
+    let call_data = call_data.downcast_ref::<CallData>();
+
+    // This will be inside wasm so unwrap is fine.
+    let call_data = call_data.unwrap();
+
+    // Check the merkle roots for the input coins are valid
+    for input in &call_data.inputs {
+        let money_state = states.lookup::<MoneyState>(&"Money".to_string()).unwrap();
+        if !money_state.is_valid_merkle(&input.merkle_root) {
+            return Err(Error::InvalidInputMerkleRoot)
+        }
+    }
+
+    let state = states.lookup::<DaoState>(&"DAO".to_string()).unwrap();
+
+    //// TODO: check if the proposal bulla generated in the ZK proof is valid?
+
+    // Verify the available signatures
+    let mut unsigned_tx_data = vec![];
+    call_data.header.encode(&mut unsigned_tx_data).expect("failed to encode data");
+    call_data.inputs.encode(&mut unsigned_tx_data).expect("failed to encode inputs");
+    func_call.proofs.encode(&mut unsigned_tx_data).expect("failed to encode proofs");
+
+    for (i, (input, signature)) in
+        call_data.inputs.iter().zip(call_data.signatures.iter()).enumerate()
+    {
+        let public = &input.signature_public;
+        if !public.verify(&unsigned_tx_data[..], signature) {
+            return Err(Error::SignatureVerifyFailed)
+        }
+    }
+
+    //// TODO: look at gov tokens avoid using already spent ones
+    //// Need to spend original coin and generate 2 nullifiers?
+
+    //Ok(Update { proposal_bulla: call_data.header.proposal_bulla })
+    Ok(())
+}
+
+pub struct Update {
+    // TODO
+    //value_commit:
+    //vote_commit:
+    //token_commit:
+}

+ 70 - 43
bin/daod/src/dao_contract/vote/wallet.rs

@@ -6,6 +6,7 @@ use pasta_curves::{
     pallas,
 };
 use rand::rngs::OsRng;
+use std::any::{Any, TypeId};
 
 use darkfi::{
     crypto::{
@@ -27,12 +28,31 @@ use darkfi::{
 };
 
 use crate::{
-    dao_contract::vote::validate::{CallData, Header, Input},
+    dao_contract::{
+        propose::wallet::Proposal,
+        vote::validate::{Header, Input},
+    },
     demo::{CallDataBase, FuncCall, StateRegistry, ZkContractInfo, ZkContractTable},
     money_contract, note,
     util::poseidon_hash,
 };
 
+use log::debug;
+
+struct CallData {}
+
+impl CallDataBase for CallData {
+    fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
+        vec![]
+    }
+    fn zk_proof_addrs(&self) -> Vec<String> {
+        vec![]
+    }
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
 #[derive(SerialEncodable, SerialDecodable)]
 pub struct Note {
     vote: Vote,
@@ -61,10 +81,12 @@ pub struct Builder {
     pub inputs: Vec<BuilderInput>,
     pub vote: Vote,
     pub vote_keypair: Keypair,
+    pub proposal: Proposal,
 }
 
 impl Builder {
     pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
+        debug!(target: "dao_contract::vote::wallet::Builder", "build()");
         let mut proofs = vec![];
 
         let gov_token_blind = pallas::Base::random(&mut OsRng);
@@ -163,6 +185,7 @@ impl Builder {
 
             let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
             let proving_key = &zk_info.proving_key;
+            debug!(target: "dao_contract::vote::wallet::Builder", "input_proof Proof::create()");
             let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
                 .expect("DAO::vote() proving error!");
             proofs.push(input_proof);
@@ -201,48 +224,52 @@ impl Builder {
         };
         let zk_bin = zk_info.bincode.clone();
 
-        let prover_witnesses = vec![
-            // Total number of gov tokens allocated
-            Witness::Base(Value::known(value_base)),
-            Witness::Scalar(Value::known(total_value_blind)),
-            // Vote
-            Witness::Base(Value::known(vote)),
-            Witness::Scalar(Value::known(vote_blind)),
-            // TODO: gov token
-        ];
-
-        let public_inputs = vec![
-            //TODO: token_commit
-            total_value_x,
-            total_value_y,
-            vote_commit_x,
-            vote_commit_y,
-        ];
-
-        let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
-
-        let proving_key = &zk_info.proving_key;
-        let main_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
-            .expect("DAO::vote() proving error!");
-        proofs.push(main_proof);
-
-        let note = Note { vote: self.vote, value: total_value };
-        let enc_note = note::encrypt(&note, &self.vote_keypair.public).unwrap();
-
-        let header = Header { enc_note };
-
-        let mut unsigned_tx_data = vec![];
-        header.encode(&mut unsigned_tx_data).expect("failed to encode data");
-        inputs.encode(&mut unsigned_tx_data).expect("failed to encode inputs");
-        proofs.encode(&mut unsigned_tx_data).expect("failed to encode proofs");
-
-        let mut signatures = vec![];
-        for signature_secret in &signature_secrets {
-            let signature = signature_secret.sign(&unsigned_tx_data[..]);
-            signatures.push(signature);
-        }
-
-        let call_data = CallData { header, inputs, signatures };
+        //let prover_witnesses = vec![
+        //    // Total number of gov tokens allocated
+        //    Witness::Base(Value::known(value_base)),
+        //    Witness::Scalar(Value::known(total_value_blind)),
+        //    // Vote
+        //    Witness::Base(Value::known(vote)),
+        //    Witness::Scalar(Value::known(vote_blind)),
+        //    // TODO: gov token
+        //    Witness::Base(Value::known(vote.token_id)),
+        //    Witness::Base(Value::known(vote.token_blind)),
+        //];
+
+        //let public_inputs = vec![
+        //    //TODO: token_commit
+        //    total_value_x,
+        //    total_value_y,
+        //    vote_commit_x,
+        //    vote_commit_y,
+        //];
+
+        //let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
+
+        //let proving_key = &zk_info.proving_key;
+        //debug!(target: "dao_contract::vote::wallet::Builder", "main_proof Proof::create()");
+        //let main_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
+        //    .expect("DAO::vote() proving error!");
+        //proofs.push(main_proof);
+
+        //let note = Note { vote: self.vote, value: total_value };
+        //let enc_note = note::encrypt(&note, &self.vote_keypair.public).unwrap();
+
+        //let header = Header { enc_note };
+
+        //let mut unsigned_tx_data = vec![];
+        //header.encode(&mut unsigned_tx_data).expect("failed to encode data");
+        //inputs.encode(&mut unsigned_tx_data).expect("failed to encode inputs");
+        //proofs.encode(&mut unsigned_tx_data).expect("failed to encode proofs");
+
+        //let mut signatures = vec![];
+        //for signature_secret in &signature_secrets {
+        //    let signature = signature_secret.sign(&unsigned_tx_data[..]);
+        //    signatures.push(signature);
+        //}
+
+        //let call_data = CallData { header, inputs, signatures };
+        let call_data = CallData {};
 
         FuncCall {
             contract_id: "DAO".to_string(),

+ 20 - 0
bin/daod/src/demo.rs

@@ -258,9 +258,11 @@ pub async fn demo() -> Result<()> {
     debug!(target: "demo", "Loading dao-vote-main.zk");
     let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
     let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
+    zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
     debug!(target: "demo", "Loading dao-vote-burn.zk");
     let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
     let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
+    zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
 
     // State for money contracts
     let cashier_signature_secret = SecretKey::random(&mut OsRng);
@@ -719,6 +721,7 @@ pub async fn demo() -> Result<()> {
         (leaf_position, merkle_path)
     };
 
+    debug!(target: "demo", "Stage 5. Creating inputs...");
     let input = dao_contract::vote::wallet::BuilderInput {
         secret: gov_keypair_2.secret,
         note: gov_recv[1].note.clone(),
@@ -733,6 +736,7 @@ pub async fn demo() -> Result<()> {
     // We create a new keypair to encrypt the vote.
     let vote_keypair = Keypair::random(&mut OsRng);
 
+    debug!(target: "demo", "Stage 5. Creating builder...");
     let builder = dao_contract::vote::wallet::Builder {
         inputs: vec![input],
         vote: dao_contract::vote::wallet::Vote {
@@ -741,7 +745,23 @@ pub async fn demo() -> Result<()> {
             vote_option_blind: pallas::Scalar::random(&mut OsRng),
         },
         vote_keypair,
+        proposal,
     };
+    debug!(target: "demo", "Stage 5. build()...");
+    let func_call = builder.build(&zk_bins);
+
+    let tx = Transaction { func_calls: vec![func_call] };
+
+    //// Validator
 
+    for (idx, func_call) in tx.func_calls.iter().enumerate() {
+        if func_call.func_id == "DAO::vote()" {
+            debug!(target: "demo", "dao_contract::vote::state_transition()");
+
+            //let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
+            //    .expect("dao_contract::vote::validate::state_transition() failed!");
+            //dao_contract::vote::validate::apply(&mut states, update);
+        }
+    }
     Ok(())
 }