/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use darkfi::{crypto::Proof, Result, VerifyFailed::ProofVerifyFailed}; use darkfi_sdk::{ crypto::{ schnorr::{SchnorrPublic, Signature}, PublicKey, }, pasta::pallas, tx::ContractCall, }; use darkfi_serial::Encodable; use log::debug; use crate::{ contract::{dao, example, money}, note::EncryptedNote2, schema::WalletCache, util::{sign, StateRegistry, ZkContractInfo, ZkContractTable}, }; macro_rules! zip { ($x: expr) => ($x); ($x: expr, $($y: expr), +) => ( $x.iter().zip( zip!($($y), +)) ) } pub struct Transaction { pub calls: Vec, pub proofs: Vec>, pub signatures: Vec>, } impl Transaction { /// 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 pub fn zk_verify( &self, zk_bins: &ZkContractTable, zkpub_table: &Vec)>>, ) -> Result<()> { assert_eq!( self.calls.len(), self.proofs.len(), "calls.len()={} and proofs.len()={} do not match", self.calls.len(), self.proofs.len() ); assert_eq!( self.calls.len(), zkpub_table.len(), "calls.len()={} and zkpub_table.len()={} do not match", self.calls.len(), zkpub_table.len() ); for (call, (proofs, pubvals)) in zip!(self.calls, self.proofs, zkpub_table) { assert_eq!( proofs.len(), pubvals.len(), "proofs.len()={} and pubvals.len()={} do not match", proofs.len(), pubvals.len() ); for (i, (proof, (key, public_vals))) in proofs.iter().zip(pubvals.iter()).enumerate() { match zk_bins.lookup(key).unwrap() { ZkContractInfo::Binary(info) => { let verifying_key = &info.verifying_key; let verify_result = proof.verify(verifying_key, public_vals); if verify_result.is_err() { return Err(ProofVerifyFailed(key.to_string()).into()) } //assert!(verify_result.is_ok(), "verify proof[{}]='{}' failed", i, key); } ZkContractInfo::Native(info) => { let verifying_key = &info.verifying_key; let verify_result = proof.verify(verifying_key, public_vals); if verify_result.is_err() { return Err(ProofVerifyFailed(key.to_string()).into()) } //assert!(verify_result.is_ok(), "verify proof[{}]='{}' failed", i, key); } }; debug!(target: "demo", "zk_verify({}) passed [i={}]", key, i); } } Ok(()) } pub fn verify_sigs(&self, sigpub_table: &Vec>) -> Result<()> { let mut tx_data = Vec::new(); self.calls.encode(&mut tx_data)?; self.proofs.encode(&mut tx_data)?; // TODO: Hash it and use the hash as the signing data // let sighash = ... for (i, (signatures, signature_public_keys)) in self.signatures.iter().zip(sigpub_table.iter()).enumerate() { for (signature_pub_key, signature) in signature_public_keys.iter().zip(signatures) { let signature_pub_key = PublicKey::from(*signature_pub_key); let verify_result = signature_pub_key.verify(&tx_data[..], &signature); assert!(verify_result, "verify sigs[{}] failed", i); } debug!(target: "demo", "verify_sigs({}) passed", i); } Ok(()) } }