1use std::collections::HashMap;
20
21use darkfi_sdk::{
22 crypto::{
23 schnorr::{SchnorrPublic, SchnorrSecret, Signature},
24 PublicKey, SecretKey,
25 },
26 dark_tree::{dark_forest_leaf_vec_integrity_check, DarkForest, DarkLeaf, DarkTree},
27 error::DarkTreeResult,
28 pasta::pallas,
29 tx::{ContractCall, TransactionHash},
30};
31
32#[cfg(feature = "async-serial")]
33use darkfi_serial::async_trait;
34
35use darkfi_serial::{Encodable, SerialDecodable, SerialEncodable};
36use tracing::{debug, error};
37
38use crate::{
39 error::TxVerifyFailed,
40 zk::{proof::VerifyingKey, Proof},
41 Error, Result,
42};
43
44macro_rules! zip {
45 ($x:expr) => ($x);
46 ($x:expr, $($y:expr), +) => (
47 $x.iter().zip(zip!($($y), +))
48 )
49}
50
51#[derive(Clone, Default, Eq, PartialEq, SerialEncodable, SerialDecodable)]
57pub struct Transaction {
59 pub calls: Vec<DarkLeaf<ContractCall>>,
61 pub proofs: Vec<Vec<Proof>>,
63 pub signatures: Vec<Vec<Signature>>,
65}
66impl Transaction {
70 pub async fn verify_zkps(
72 &self,
73 verifying_keys: &HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
74 zkp_table: Vec<Vec<(String, Vec<pallas::Base>)>>,
75 ) -> Result<()> {
76 assert_eq!(self.calls.len(), self.proofs.len());
78 assert_eq!(self.calls.len(), zkp_table.len());
79
80 for (call, (proofs, pubvals)) in zip!(self.calls, self.proofs, zkp_table) {
81 assert_eq!(proofs.len(), pubvals.len());
82
83 let Some(contract_map) = verifying_keys.get(&call.data.contract_id.to_bytes()) else {
84 error!(
85 target: "tx::verify_zkps",
86 "[TX] Verifying keys not found for contract {}",
87 call.data.contract_id,
88 );
89 return Err(TxVerifyFailed::InvalidZkProof.into())
90 };
91
92 for (proof, (zk_ns, public_vals)) in proofs.iter().zip(pubvals.iter()) {
93 if let Some(vk) = contract_map.get(zk_ns) {
94 debug!(target: "tx::verify_zkps", "[TX] public inputs: {public_vals:#?}");
96 if let Err(e) = proof.verify(vk, public_vals) {
97 error!(
98 target: "tx::verify_zkps",
99 "[TX] Failed verifying {}::{zk_ns} ZK proof: {e:#?}",
100 call.data.contract_id
101 );
102 return Err(TxVerifyFailed::InvalidZkProof.into())
103 }
104 debug!(
105 target: "tx::verify_zkps",
106 "[TX] Successfully verified {}::{zk_ns} ZK proof",
107 call.data.contract_id
108 );
109 continue
110 }
111
112 error!(
113 target: "tx::verify_zkps",
114 "[TX] {}::{zk_ns} circuit VK nonexistent",
115 call.data.contract_id
116 );
117 return Err(TxVerifyFailed::InvalidZkProof.into())
118 }
119 }
120
121 Ok(())
122 }
123
124 pub fn verify_sigs(&self, pub_table: Vec<Vec<PublicKey>>) -> Result<()> {
126 let mut hasher = blake3::Hasher::new();
128 self.calls.encode(&mut hasher)?;
129 self.proofs.encode(&mut hasher)?;
130 let data_hash = hasher.finalize();
131
132 debug!(target: "tx::verify_sigs", "tx.verify_sigs: data_hash: {data_hash}");
133
134 assert_eq!(self.signatures.len(), pub_table.len());
135
136 for (i, (sigs, pubkeys)) in self.signatures.iter().zip(pub_table.iter()).enumerate() {
137 assert_eq!(sigs.len(), pubkeys.len());
138
139 for (pubkey, signature) in pubkeys.iter().zip(sigs) {
140 debug!(target: "tx::verify_sigs", "[TX] Verifying signature with public key: {pubkey}");
141 if !pubkey.verify(&data_hash.as_bytes()[..], signature) {
142 error!(target: "tx::verify_sigs", "[TX] tx::verify_sigs[{i}] failed to verify signature");
143 return Err(Error::InvalidSignature)
144 }
145 }
146
147 debug!(target: "tx::verify_sigs", "[TX] tx::verify_sigs[{i}] passed");
148 }
149
150 Ok(())
151 }
152
153 pub fn create_sigs(&self, secret_keys: &[SecretKey]) -> Result<Vec<Signature>> {
155 let mut hasher = blake3::Hasher::new();
157 self.calls.encode(&mut hasher)?;
158 self.proofs.encode(&mut hasher)?;
159 let data_hash = hasher.finalize();
160
161 debug!(target: "tx::create_sigs", "[TX] tx.create_sigs: data_hash: {data_hash}");
162
163 let mut sigs = vec![];
164 for secret in secret_keys {
165 debug!(
166 target: "tx::create_sigs",
167 "[TX] Creating signature with public key: {}", PublicKey::from_secret(*secret),
168 );
169 let signature = secret.sign(&data_hash.as_bytes()[..]);
170 sigs.push(signature);
171 }
172
173 Ok(sigs)
174 }
175
176 pub fn hash(&self) -> TransactionHash {
178 let mut hasher = blake3::Hasher::new();
179 self.encode(&mut hasher).expect("blake3 hasher");
183 TransactionHash(hasher.finalize().into())
184 }
185
186 pub fn is_pow_reward(&self) -> bool {
188 if !self.is_single_call() {
190 return false;
191 }
192
193 self.calls[0].data.is_money_pow_reward()
194 }
195
196 pub fn is_single_call(&self) -> bool {
198 self.calls.len() == 1 && !self.calls[0].data.data.is_empty()
199 }
200}
201
202impl std::fmt::Debug for Transaction {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 writeln!(f, "Transaction {{")?;
206 for (i, call) in self.calls.iter().enumerate() {
207 writeln!(f, " Call {i} {{")?;
208 writeln!(f, " contract_id: {:?}", call.data.contract_id.inner())?;
209 let calldata = &call.data.data;
210 if !calldata.is_empty() {
211 writeln!(f, " function_code: {}", calldata[0])?;
212 }
213 writeln!(f, " parent: {:?}", call.parent_index)?;
214 writeln!(f, " children: {:?}", call.children_indexes)?;
215 writeln!(f, " }},")?;
216 }
217 writeln!(f, "}}")
218 }
219}
220
221#[cfg(feature = "net")]
222use crate::{
223 net::{metering::MeteringConfiguration, Message},
224 util::time::NanoTimestamp,
225};
226
227#[cfg(feature = "net")]
228crate::impl_p2p_message!(
233 Transaction,
234 "tx",
235 0,
236 1,
237 MeteringConfiguration {
238 threshold: 100,
239 sleep_step: 500,
240 expiry_time: NanoTimestamp::from_secs(5),
241 }
242);
243
244pub const MIN_TX_CALLS: usize = 1;
247pub const MAX_TX_CALLS: usize = 20;
249
250#[derive(Clone)]
253pub struct ContractCallLeaf {
254 pub call: ContractCall,
256 pub proofs: Vec<Proof>,
258}
259
260pub struct TransactionBuilder {
263 pub calls: DarkForest<ContractCallLeaf>,
265}
266
267impl TransactionBuilder {
270 pub fn new(
273 data: ContractCallLeaf,
274 children: Vec<DarkTree<ContractCallLeaf>>,
275 ) -> DarkTreeResult<Self> {
276 let calls = DarkForest::new(Some(MIN_TX_CALLS), Some(MAX_TX_CALLS));
277 let mut self_ = Self { calls };
278 self_.append(data, children)?;
279 Ok(self_)
280 }
281
282 pub fn append(
284 &mut self,
285 data: ContractCallLeaf,
286 children: Vec<DarkTree<ContractCallLeaf>>,
287 ) -> DarkTreeResult<()> {
288 let tree = DarkTree::new(data, children, None, None);
289 self.calls.append(tree)
290 }
291
292 pub fn build(&mut self) -> DarkTreeResult<Transaction> {
295 let leafs = self.calls.build_vec()?;
297
298 dark_forest_leaf_vec_integrity_check(&leafs, Some(MIN_TX_CALLS), Some(MAX_TX_CALLS))?;
300
301 let mut calls = Vec::with_capacity(leafs.len());
303 let mut proofs = Vec::with_capacity(leafs.len());
304 for leaf in leafs {
305 let call = DarkLeaf {
306 data: leaf.data.call,
307 parent_index: leaf.parent_index,
308 children_indexes: leaf.children_indexes,
309 };
310 calls.push(call);
311 proofs.push(leaf.data.proofs);
312 }
313
314 Ok(Transaction { calls, proofs, signatures: vec![] })
315 }
316}