/* 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 std::collections::HashMap; use darkfi_sdk::{ crypto::{ schnorr::{SchnorrPublic, SchnorrSecret, Signature}, PublicKey, SecretKey, }, pasta::pallas, tx::ContractCall, }; use darkfi_serial::{serialize, Encodable, SerialDecodable, SerialEncodable}; use log::{debug, error}; use rand::{CryptoRng, RngCore}; use crate::{ zk::{proof::VerifyingKey, Proof}, Error, Result, VerifyFailed, }; macro_rules! zip { ($x:expr) => ($x); ($x:expr, $($y:expr), +) => ( $x.iter().zip(zip!($($y), +)) ) } // ANCHOR: transaction /// A Transaction contains an arbitrary number of `ContractCall` objects, /// along with corresponding ZK proofs and Schnorr signatures. #[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)] pub struct Transaction { /// Calls executed in this transaction pub calls: Vec, /// Attached ZK proofs pub proofs: Vec>, /// Attached Schnorr signatures pub signatures: Vec>, } // ANCHOR_END: transaction impl Transaction { /// Verify ZK proofs for the entire transaction. pub async fn verify_zkps( &self, verifying_keys: HashMap<[u8; 32], HashMap>, zkp_table: Vec)>>, ) -> Result<()> { // TODO: Are we sure we should assert here? assert_eq!(self.calls.len(), self.proofs.len()); assert_eq!(self.calls.len(), zkp_table.len()); for (call, (proofs, pubvals)) in zip!(self.calls, self.proofs, zkp_table) { assert_eq!(proofs.len(), pubvals.len()); let Some(contract_map) = verifying_keys.get(&call.contract_id.to_bytes()) else { error!("Verifying keys not found for contract {}", call.contract_id); return Err(VerifyFailed::ProofVerifyFailed("VKs not found for contract".to_string()).into()) }; for (proof, (zk_ns, public_vals)) in proofs.iter().zip(pubvals.iter()) { if let Some(vk) = contract_map.get(zk_ns) { // We have a verifying key for this debug!("public inputs: {:#?}", public_vals); if let Err(e) = proof.verify(vk, public_vals) { error!( target: "", "Failed verifying {}::{} ZK proof: {:#?}", call.contract_id, zk_ns, e ); return Err(VerifyFailed::ProofVerifyFailed(e.to_string()).into()) } debug!("Successfully verified {}::{} ZK proof", call.contract_id, zk_ns); continue } let e = format!("{}:{} circuit VK nonexistent", call.contract_id, zk_ns); error!("{}", e); return Err(VerifyFailed::ProofVerifyFailed(e).into()) } } Ok(()) } /// Verify Schnorr signatures for the entire transaction. pub fn verify_sigs(&self, pub_table: Vec>) -> Result<()> { let tx_data = self.encode_without_sigs()?; let data_hash = blake3::hash(&tx_data); debug!("tx.verify_sigs: data_hash: {:?}", data_hash.as_bytes()); assert!(pub_table.len() == self.signatures.len()); for (i, (sigs, pubkeys)) in self.signatures.iter().zip(pub_table.iter()).enumerate() { for (pubkey, signature) in pubkeys.iter().zip(sigs) { debug!("Verifying signature with public key: {}", pubkey); if !pubkey.verify(&data_hash.as_bytes()[..], signature) { error!("tx::verify_sigs[{}] failed to verify", i); return Err(Error::InvalidSignature) } } debug!("tx::verify_sigs[{}] passed", i); } Ok(()) } /// Create Schnorr signatures for the entire transaction. pub fn create_sigs( &self, rng: &mut (impl CryptoRng + RngCore), secret_keys: &[SecretKey], ) -> Result> { let tx_data = self.encode_without_sigs()?; let data_hash = blake3::hash(&tx_data); debug!("tx.create_sigs: data_hash: {:?}", data_hash.as_bytes()); let mut sigs = vec![]; for secret in secret_keys { debug!("Creating signature with public key: {}", PublicKey::from_secret(*secret)); let signature = secret.sign(rng, &data_hash.as_bytes()[..]); sigs.push(signature); } Ok(sigs) } /// Encode the object into a byte vector for signing pub fn encode_without_sigs(&self) -> Result> { let mut buf = vec![]; self.calls.encode(&mut buf)?; self.proofs.encode(&mut buf)?; Ok(buf) } /// Get the transaction hash pub fn hash(&self) -> blake3::Hash { blake3::hash(&serialize(self)) } }