darkfi/validator/
verification.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::collections::HashMap;
20
21use darkfi_sdk::{
22    blockchain::block_version,
23    crypto::{
24        schnorr::{SchnorrPublic, Signature},
25        ContractId, MerkleTree, PublicKey,
26    },
27    dark_tree::dark_forest_leaf_vec_integrity_check,
28    deploy::DeployParamsV1,
29    pasta::pallas,
30};
31use darkfi_serial::{deserialize_async, serialize_async, AsyncDecodable, AsyncEncodable};
32use num_bigint::BigUint;
33use sled_overlay::SledDbOverlayStateDiff;
34use smol::io::Cursor;
35use tracing::{debug, error, warn};
36
37use crate::{
38    blockchain::{
39        block_store::append_tx_to_merkle_tree, header_store::PowData::DarkFi, BlockInfo,
40        Blockchain, BlockchainOverlayPtr, HeaderHash,
41    },
42    error::TxVerifyFailed,
43    runtime::vm_runtime::Runtime,
44    tx::{Transaction, MAX_TX_CALLS, MIN_TX_CALLS},
45    validator::{
46        consensus::{Consensus, Fork, Proposal, BLOCK_GAS_LIMIT},
47        fees::{circuit_gas_use, compute_fee, GasData, PALLAS_SCHNORR_SIGNATURE_FEE},
48        pow::PoWModule,
49    },
50    zk::VerifyingKey,
51    Error, Result,
52};
53
54/// Verify given genesis [`BlockInfo`], and apply it to the provided
55/// overlay.
56///
57/// Note: Always remember to purge new trees from the database if not
58/// needed.
59pub async fn verify_genesis_block(
60    overlay: &BlockchainOverlayPtr,
61    diffs: &[SledDbOverlayStateDiff],
62    block: &BlockInfo,
63    block_target: u32,
64) -> Result<()> {
65    let block_hash = block.hash().as_string();
66    debug!(target: "validator::verification::verify_genesis_block", "Validating genesis block {block_hash}");
67
68    // Check if block already exists
69    if overlay.lock().unwrap().has_block(block)? {
70        return Err(Error::BlockAlreadyExists(block_hash))
71    }
72
73    // Block height must be 0
74    if block.header.height != 0 {
75        return Err(Error::BlockIsInvalid(block_hash))
76    }
77
78    // Block version must be correct
79    if block.header.version != block_version(block.header.height) {
80        return Err(Error::BlockIsInvalid(block_hash))
81    }
82
83    // Block must use Darkfi native Proof of Work data
84    match block.header.pow_data {
85        DarkFi => { /* do nothing */ }
86        _ => return Err(Error::BlockIsInvalid(block_hash)),
87    }
88
89    // Verify transactions vector contains at least one(producers) transaction
90    if block.txs.is_empty() {
91        return Err(Error::BlockContainsNoTransactions(block_hash))
92    }
93
94    // Genesis producer transaction must be the Transaction::default() one(empty)
95    let producer_tx = block.txs.last().unwrap();
96    if producer_tx != &Transaction::default() {
97        error!(target: "validator::verification::verify_genesis_block", "Genesis producer transaction is not default one");
98        return Err(TxVerifyFailed::ErroneousTxs(vec![producer_tx.clone()]).into())
99    }
100
101    // Verify transactions, exluding producer(last) one/
102    // Genesis block doesn't check for fees
103    let mut tree = MerkleTree::new(1);
104    let txs = &block.txs[..block.txs.len() - 1];
105    if let Err(e) =
106        verify_transactions(overlay, block.header.height, block_target, txs, &mut tree, false).await
107    {
108        warn!(
109            target: "validator::verification::verify_genesis_block",
110            "[VALIDATOR] Erroneous transactions found in set",
111        );
112        return Err(e)
113    }
114
115    // Append producer transaction to the tree and check tree matches header one
116    append_tx_to_merkle_tree(&mut tree, producer_tx);
117    if tree.root(0).unwrap() != block.header.transactions_root {
118        error!(target: "validator::verification::verify_genesis_block", "Genesis Merkle tree is invalid");
119        return Err(Error::BlockIsInvalid(block_hash))
120    }
121
122    // Update the contracts states monotree and verify header contracts states root
123    let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(diffs)?;
124    let state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
125    if state_root != block.header.state_root {
126        return Err(Error::ContractsStatesRootError(
127            blake3::Hash::from_bytes(state_root).to_string(),
128            blake3::Hash::from_bytes(block.header.state_root).to_string(),
129        ));
130    }
131
132    // Genesis producer signature must be the Signature::dummy() one(empty)
133    if block.signature != Signature::dummy() {
134        error!(target: "validator::verification::verify_genesis_block", "Genesis producer signature is not dummy one");
135        return Err(Error::InvalidSignature)
136    }
137
138    // Insert block
139    overlay.lock().unwrap().add_block(block)?;
140
141    debug!(target: "validator::verification::verify_genesis_block", "Genesis block {block_hash} verified successfully");
142    Ok(())
143}
144
145/// Validate provided block according to set rules.
146///
147/// A block is considered valid when the following rules apply:
148///     1. Block version is correct for its height
149///     2. Previous hash is equal to the hash of the provided previous block
150///     3. Block height increments previous block height by 1
151///     4. Timestamp is valid based on PoWModule validation
152///     5. Block header Proof of Work data are valid
153///     6. Block hash is valid based on PoWModule validation
154/// Additional validity rules can be applied.
155pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, module: &PoWModule) -> Result<()> {
156    // Check block version (1)
157    if block.header.version != block_version(block.header.height) {
158        return Err(Error::BlockIsInvalid(block.hash().as_string()))
159    }
160
161    // Check previous hash (2)
162    if block.header.previous != previous.hash() {
163        return Err(Error::BlockIsInvalid(block.hash().as_string()))
164    }
165
166    // Check heights are incremental (3)
167    if block.header.height != previous.header.height + 1 {
168        return Err(Error::BlockIsInvalid(block.hash().as_string()))
169    }
170
171    // Check timestamp validity (4)
172    if !module.verify_timestamp_by_median(block.header.timestamp) {
173        return Err(Error::BlockIsInvalid(block.hash().as_string()))
174    }
175
176    // Check PoW data validty (5)
177    if !block.header.validate_powdata() {
178        return Err(Error::BlockIsInvalid(block.hash().as_string()))
179    }
180
181    // Check block hash corresponds to next  mine target (6)
182    module.verify_block_hash(&block.header)?;
183
184    Ok(())
185}
186
187/// A blockchain is considered valid, when every block is valid,
188/// based on validate_block checks.
189/// Be careful as this will try to load everything in memory.
190pub fn validate_blockchain(
191    blockchain: &Blockchain,
192    pow_target: u32,
193    pow_fixed_difficulty: Option<BigUint>,
194) -> Result<()> {
195    // Generate a PoW module
196    let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
197
198    // We use block order store here so we have all blocks in order
199    let blocks = blockchain.blocks.get_all_order()?;
200    for (index, block) in blocks[1..].iter().enumerate() {
201        let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
202        let full_block = &full_blocks[1];
203        validate_block(full_block, &full_blocks[0], &module)?;
204        // Update PoW module
205        module.append(&full_block.header, &module.next_difficulty()?)?;
206    }
207
208    Ok(())
209}
210
211/// Verify given [`BlockInfo`], and apply it to the provided overlay.
212///
213/// Note: Always remember to purge new trees from the database if not
214/// needed.
215pub async fn verify_block(
216    overlay: &BlockchainOverlayPtr,
217    diffs: &[SledDbOverlayStateDiff],
218    module: &PoWModule,
219    block: &BlockInfo,
220    previous: &BlockInfo,
221    verify_fees: bool,
222) -> Result<()> {
223    let block_hash = block.hash();
224    debug!(target: "validator::verification::verify_block", "Validating block {block_hash}");
225
226    // Check if block already exists
227    if overlay.lock().unwrap().has_block(block)? {
228        return Err(Error::BlockAlreadyExists(block_hash.as_string()))
229    }
230
231    // Validate block, using its previous
232    validate_block(block, previous, module)?;
233
234    // Verify transactions vector contains at least one(producers) transaction
235    if block.txs.is_empty() {
236        return Err(Error::BlockContainsNoTransactions(block_hash.as_string()))
237    }
238
239    // Verify transactions, exluding producer(last) one
240    let mut tree = MerkleTree::new(1);
241    let txs = &block.txs[..block.txs.len() - 1];
242    if let Err(e) = verify_transactions(
243        overlay,
244        block.header.height,
245        module.target,
246        txs,
247        &mut tree,
248        verify_fees,
249    )
250    .await
251    {
252        warn!(
253            target: "validator::verification::verify_block",
254            "[VALIDATOR] Erroneous transactions found in set",
255        );
256        return Err(e)
257    }
258
259    // Verify producer transaction
260    let public_key = verify_producer_transaction(
261        overlay,
262        block.header.height,
263        module.target,
264        block.txs.last().unwrap(),
265        &mut tree,
266    )
267    .await?;
268
269    // Verify transactions merkle tree root matches header one
270    if tree.root(0).unwrap() != block.header.transactions_root {
271        error!(target: "validator::verification::verify_block", "Block Merkle tree root is invalid");
272        return Err(Error::BlockIsInvalid(block_hash.as_string()))
273    }
274
275    // Update the contracts states monotree and verify header contracts states root
276    let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(diffs)?;
277    let state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
278    if state_root != block.header.state_root {
279        return Err(Error::ContractsStatesRootError(
280            blake3::Hash::from_bytes(state_root).to_string(),
281            blake3::Hash::from_bytes(block.header.state_root).to_string(),
282        ));
283    }
284
285    // Verify producer signature
286    verify_producer_signature(block, &public_key)?;
287
288    // Insert block
289    overlay.lock().unwrap().add_block(block)?;
290
291    debug!(target: "validator::verification::verify_block", "Block {block_hash} verified successfully");
292    Ok(())
293}
294
295/// Verify given checkpoint [`BlockInfo`], and apply it to the provided
296/// overlay.
297///
298/// Note: Always remember to purge new trees from the database if not
299/// needed.
300pub async fn verify_checkpoint_block(
301    overlay: &BlockchainOverlayPtr,
302    diffs: &[SledDbOverlayStateDiff],
303    block: &BlockInfo,
304    header: &HeaderHash,
305    block_target: u32,
306) -> Result<()> {
307    let block_hash = block.hash();
308    debug!(target: "validator::verification::verify_checkpoint_block", "Validating block {block_hash}");
309
310    // Check if block already exists
311    if overlay.lock().unwrap().has_block(block)? {
312        return Err(Error::BlockAlreadyExists(block_hash.as_string()))
313    }
314
315    // Check if block hash matches the expected(provided) one
316    if block_hash != *header {
317        error!(target: "validator::verification::verify_checkpoint_block", "Block hash doesn't match the expected one");
318        return Err(Error::BlockIsInvalid(block_hash.as_string()))
319    }
320
321    // Verify transactions vector contains at least one(producers) transaction
322    if block.txs.is_empty() {
323        return Err(Error::BlockContainsNoTransactions(block_hash.as_string()))
324    }
325
326    // Apply transactions, excluding producer(last) one
327    let mut tree = MerkleTree::new(1);
328    let txs = &block.txs[..block.txs.len() - 1];
329    if let Err(e) =
330        apply_transactions(overlay, block.header.height, block_target, txs, &mut tree).await
331    {
332        warn!(
333            target: "validator::verification::verify_checkpoint_block",
334            "[VALIDATOR] Erroneous transactions found in set",
335        );
336        return Err(e)
337    }
338
339    // Apply producer transaction
340    let public_key = apply_producer_transaction(
341        overlay,
342        block.header.height,
343        block_target,
344        block.txs.last().unwrap(),
345        &mut tree,
346    )
347    .await?;
348
349    // Verify transactions merkle tree root matches header one
350    if tree.root(0).unwrap() != block.header.transactions_root {
351        error!(target: "validator::verification::verify_checkpoint_block", "Block Merkle tree root is invalid");
352        return Err(Error::BlockIsInvalid(block_hash.as_string()))
353    }
354
355    // Update the contracts states monotree and verify header contracts states root
356    let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(diffs)?;
357    let state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
358    if state_root != block.header.state_root {
359        return Err(Error::ContractsStatesRootError(
360            blake3::Hash::from_bytes(state_root).to_string(),
361            blake3::Hash::from_bytes(block.header.state_root).to_string(),
362        ));
363    }
364
365    // Verify producer signature
366    verify_producer_signature(block, &public_key)?;
367
368    // Insert block
369    overlay.lock().unwrap().add_block(block)?;
370
371    debug!(target: "validator::verification::verify_checkpoint_block", "Block {block_hash} verified successfully");
372    Ok(())
373}
374
375/// Verify block proposer signature, using the producer transaction signature as signing key
376/// over blocks header hash.
377pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> Result<()> {
378    if !public_key.verify(block.header.hash().inner(), &block.signature) {
379        warn!(target: "validator::verification::verify_producer_signature", "Proposer {public_key} signature could not be verified");
380        return Err(Error::InvalidSignature)
381    }
382
383    Ok(())
384}
385
386/// Verify provided producer [`Transaction`].
387///
388/// Verify WASM execution, signatures, and ZK proofs and apply it to the provided,
389/// provided overlay. Returns transaction signature public key. Additionally,
390/// append its hash to the provided Merkle tree.
391pub async fn verify_producer_transaction(
392    overlay: &BlockchainOverlayPtr,
393    verifying_block_height: u32,
394    block_target: u32,
395    tx: &Transaction,
396    tree: &mut MerkleTree,
397) -> Result<PublicKey> {
398    let tx_hash = tx.hash();
399    debug!(target: "validator::verification::verify_producer_transaction", "Validating producer transaction {tx_hash}");
400
401    // Transaction must be a PoW reward one
402    if !tx.is_pow_reward() {
403        return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
404    }
405
406    // Retrieve first call from the transaction for further processing
407    let call = &tx.calls[0];
408
409    // Map of ZK proof verifying keys for the current transaction
410    let mut verifying_keys: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
411
412    // Initialize the map
413    verifying_keys.insert(call.data.contract_id.to_bytes(), HashMap::new());
414
415    // Table of public inputs used for ZK proof verification
416    let mut zkp_table = vec![];
417    // Table of public keys used for signature verification
418    let mut sig_table = vec![];
419
420    debug!(target: "validator::verification::verify_producer_transaction", "Executing contract call");
421
422    // Write the actual payload data
423    let mut payload = vec![];
424    tx.calls.encode_async(&mut payload).await?; // Actual call data
425
426    debug!(target: "validator::verification::verify_producer_transaction", "Instantiating WASM runtime");
427    let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
428
429    let mut runtime = Runtime::new(
430        &wasm,
431        overlay.clone(),
432        call.data.contract_id,
433        verifying_block_height,
434        block_target,
435        tx_hash,
436        // Call index in producer tx is 0
437        0,
438    )?;
439
440    debug!(target: "validator::verification::verify_producer_transaction", "Executing \"metadata\" call");
441    let metadata = runtime.metadata(&payload)?;
442
443    // Decode the metadata retrieved from the execution
444    let mut decoder = Cursor::new(&metadata);
445
446    // The tuple is (zkas_ns, public_inputs)
447    let zkp_pub: Vec<(String, Vec<pallas::Base>)> =
448        AsyncDecodable::decode_async(&mut decoder).await?;
449    let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
450
451    // Check that only one ZK proof and signature public key exist
452    if zkp_pub.len() != 1 || sig_pub.len() != 1 {
453        error!(target: "validator::verification::verify_producer_transaction", "Producer transaction contains multiple ZK proofs or signature public keys");
454        return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
455    }
456
457    // TODO: Make sure we've read all the bytes above.
458    debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"metadata\" call");
459
460    // Here we'll look up verifying keys and insert them into the map.
461    debug!(target: "validator::verification::verify_producer_transaction", "Performing VerifyingKey lookups from the sled db");
462    for (zkas_ns, _) in &zkp_pub {
463        // TODO: verify this is correct behavior
464        let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();
465        if inner_vk_map.contains_key(zkas_ns.as_str()) {
466            continue
467        }
468
469        let (_zkbin, vk) =
470            overlay.lock().unwrap().contracts.get_zkas(&call.data.contract_id, zkas_ns)?;
471
472        inner_vk_map.insert(zkas_ns.to_string(), vk);
473    }
474
475    zkp_table.push(zkp_pub);
476    let signature_public_key = *sig_pub.last().unwrap();
477    sig_table.push(sig_pub);
478
479    // After getting the metadata, we run the "exec" function with the same runtime
480    // and the same payload. We keep the returned state update in a buffer, prefixed
481    // by the call function ID, enforcing the state update function in the contract.
482    debug!(target: "validator::verification::verify_producer_transaction", "Executing \"exec\" call");
483    let mut state_update = vec![call.data.data[0]];
484    state_update.append(&mut runtime.exec(&payload)?);
485    debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"exec\" call");
486
487    // If that was successful, we apply the state update in the ephemeral overlay.
488    debug!(target: "validator::verification::verify_producer_transaction", "Executing \"apply\" call");
489    runtime.apply(&state_update)?;
490    debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"apply\" call");
491
492    // When we're done executing over the tx's contract call, we now move on with verification.
493    // First we verify the signatures as that's cheaper, and then finally we verify the ZK proofs.
494    debug!(target: "validator::verification::verify_producer_transaction", "Verifying signatures for transaction {tx_hash}");
495    if sig_table.len() != tx.signatures.len() {
496        error!(target: "validator::verification::verify_producer_transaction", "Incorrect number of signatures in tx {tx_hash}");
497        return Err(TxVerifyFailed::MissingSignatures.into())
498    }
499
500    // TODO: Go through the ZK circuits that have to be verified and account for the opcodes.
501
502    if let Err(e) = tx.verify_sigs(sig_table) {
503        error!(target: "validator::verification::verify_producer_transaction", "Signature verification for tx {tx_hash} failed: {e}");
504        return Err(TxVerifyFailed::InvalidSignature.into())
505    }
506
507    debug!(target: "validator::verification::verify_producer_transaction", "Signature verification successful");
508
509    debug!(target: "validator::verification::verify_producer_transaction", "Verifying ZK proofs for transaction {tx_hash}");
510    if let Err(e) = tx.verify_zkps(&verifying_keys, zkp_table).await {
511        error!(target: "validator::verification::verify_producer_transaction", "ZK proof verification for tx {tx_hash} failed: {e}");
512        return Err(TxVerifyFailed::InvalidZkProof.into())
513    }
514    debug!(target: "validator::verification::verify_producer_transaction", "ZK proof verification successful");
515
516    // Append hash to merkle tree
517    append_tx_to_merkle_tree(tree, tx);
518
519    debug!(target: "validator::verification::verify_producer_transaction", "Producer transaction {tx_hash} verified successfully");
520
521    Ok(signature_public_key)
522}
523
524/// Apply given producer [`Transaction`] to the provided overlay, without formal verification.
525/// Returns transaction signature public key. Additionally, append its hash to the provided Merkle tree.
526pub async fn apply_producer_transaction(
527    overlay: &BlockchainOverlayPtr,
528    verifying_block_height: u32,
529    block_target: u32,
530    tx: &Transaction,
531    tree: &mut MerkleTree,
532) -> Result<PublicKey> {
533    let tx_hash = tx.hash();
534    debug!(target: "validator::verification::apply_producer_transaction", "Applying producer transaction {tx_hash}");
535
536    // Producer transactions must contain a single, non-empty call
537    if !tx.is_single_call() {
538        return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
539    }
540
541    debug!(target: "validator::verification::apply_producer_transaction", "Executing contract call");
542
543    // Write the actual payload data
544    let mut payload = vec![];
545    tx.calls.encode_async(&mut payload).await?; // Actual call data
546
547    debug!(target: "validator::verification::apply_producer_transaction", "Instantiating WASM runtime");
548    let call = &tx.calls[0];
549    let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
550
551    let mut runtime = Runtime::new(
552        &wasm,
553        overlay.clone(),
554        call.data.contract_id,
555        verifying_block_height,
556        block_target,
557        tx_hash,
558        // Call index in producer tx is 0
559        0,
560    )?;
561
562    debug!(target: "validator::verification::apply_producer_transaction", "Executing \"metadata\" call");
563    let metadata = runtime.metadata(&payload)?;
564
565    // Decode the metadata retrieved from the execution
566    let mut decoder = Cursor::new(&metadata);
567
568    // The tuple is (zkas_ns, public_inputs)
569    let _: Vec<(String, Vec<pallas::Base>)> = AsyncDecodable::decode_async(&mut decoder).await?;
570    let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
571
572    // Check that only one ZK proof and signature public key exist
573    if sig_pub.len() != 1 {
574        error!(target: "validator::verification::apply_producer_transaction", "Producer transaction contains multiple ZK proofs or signature public keys");
575        return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
576    }
577
578    let signature_public_key = *sig_pub.last().unwrap();
579
580    // After getting the metadata, we run the "exec" function with the same runtime
581    // and the same payload. We keep the returned state update in a buffer, prefixed
582    // by the call function ID, enforcing the state update function in the contract.
583    debug!(target: "validator::verification::apply_producer_transaction", "Executing \"exec\" call");
584    let mut state_update = vec![call.data.data[0]];
585    state_update.append(&mut runtime.exec(&payload)?);
586    debug!(target: "validator::verification::apply_producer_transaction", "Successfully executed \"exec\" call");
587
588    // If that was successful, we apply the state update in the ephemeral overlay.
589    debug!(target: "validator::verification::apply_producer_transaction", "Executing \"apply\" call");
590    runtime.apply(&state_update)?;
591    debug!(target: "validator::verification::apply_producer_transaction", "Successfully executed \"apply\" call");
592
593    // Append hash to merkle tree
594    append_tx_to_merkle_tree(tree, tx);
595
596    debug!(target: "validator::verification::apply_producer_transaction", "Producer transaction {tx_hash} executed successfully");
597
598    Ok(signature_public_key)
599}
600
601/// Verify WASM execution, signatures, and ZK proofs for a given [`Transaction`],
602/// and apply it to the provided overlay. Additionally, append its hash to the
603/// provided Merkle tree.
604pub async fn verify_transaction(
605    overlay: &BlockchainOverlayPtr,
606    verifying_block_height: u32,
607    block_target: u32,
608    tx: &Transaction,
609    tree: &mut MerkleTree,
610    verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
611    verify_fee: bool,
612) -> Result<GasData> {
613    let tx_hash = tx.hash();
614    debug!(target: "validator::verification::verify_transaction", "Validating transaction {tx_hash}");
615
616    // Create a FeeData instance to hold the calculated fee data
617    let mut gas_data = GasData::default();
618
619    // Verify calls indexes integrity
620    if verify_fee {
621        dark_forest_leaf_vec_integrity_check(
622            &tx.calls,
623            Some(MIN_TX_CALLS + 1),
624            Some(MAX_TX_CALLS),
625        )?;
626    } else {
627        dark_forest_leaf_vec_integrity_check(&tx.calls, Some(MIN_TX_CALLS), Some(MAX_TX_CALLS))?;
628    }
629
630    // Table of public inputs used for ZK proof verification
631    let mut zkp_table = vec![];
632    // Table of public keys used for signature verification
633    let mut sig_table = vec![];
634
635    // Index of the Fee-paying call
636    let mut fee_call_idx = 0;
637
638    if verify_fee {
639        // Verify that there is a single money fee call in the transaction
640        let mut found_fee = false;
641        for (call_idx, call) in tx.calls.iter().enumerate() {
642            if !call.data.is_money_fee() {
643                continue
644            }
645
646            if found_fee {
647                error!(
648                    target: "validator::verification::verify_transcation",
649                    "[VALIDATOR] Transaction {tx_hash} contains multiple fee payment calls"
650                );
651                return Err(TxVerifyFailed::InvalidFee.into())
652            }
653
654            found_fee = true;
655            fee_call_idx = call_idx;
656        }
657
658        if !found_fee {
659            error!(
660                target: "validator::verification::verify_transcation",
661                "[VALIDATOR] Transaction {tx_hash} does not contain fee payment call"
662            );
663            return Err(TxVerifyFailed::InvalidFee.into())
664        }
665    }
666
667    // Write the transaction calls payload data
668    let mut payload = vec![];
669    tx.calls.encode_async(&mut payload).await?;
670
671    // Define a buffer in case we want to use a different payload in a specific call
672    let mut _call_payload = vec![];
673
674    // We'll also take note of all the circuits in a Vec so we can calculate their verification cost.
675    let mut circuits_to_verify = vec![];
676
677    // Iterate over all calls to get the metadata
678    for (idx, call) in tx.calls.iter().enumerate() {
679        debug!(target: "validator::verification::verify_transaction", "Executing contract call {idx}");
680
681        // Transaction must not contain a Pow reward call
682        if call.data.is_money_pow_reward() {
683            error!(target: "validator::verification::verify_transaction", "Reward transaction detected");
684            return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
685        }
686
687        // Check if its the fee call so we only pass its payload
688        let (call_idx, call_payload) = if call.data.is_money_fee() {
689            _call_payload = vec![];
690            vec![call.clone()].encode_async(&mut _call_payload).await?;
691            (0_u8, &_call_payload)
692        } else {
693            (idx as u8, &payload)
694        };
695
696        debug!(target: "validator::verification::verify_transaction", "Instantiating WASM runtime");
697        let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
698        let mut runtime = Runtime::new(
699            &wasm,
700            overlay.clone(),
701            call.data.contract_id,
702            verifying_block_height,
703            block_target,
704            tx_hash,
705            call_idx,
706        )?;
707
708        debug!(target: "validator::verification::verify_transaction", "Executing \"metadata\" call");
709        let metadata = runtime.metadata(call_payload)?;
710
711        // Decode the metadata retrieved from the execution
712        let mut decoder = Cursor::new(&metadata);
713
714        // The tuple is (zkas_ns, public_inputs)
715        let zkp_pub: Vec<(String, Vec<pallas::Base>)> =
716            AsyncDecodable::decode_async(&mut decoder).await?;
717        let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
718
719        if decoder.position() != metadata.len() as u64 {
720            error!(
721                target: "validator::verification::verify_transaction",
722                "[VALIDATOR] Failed decoding entire metadata buffer for {tx_hash}:{idx}"
723            );
724            return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
725        }
726
727        debug!(target: "validator::verification::verify_transaction", "Successfully executed \"metadata\" call");
728
729        // Here we'll look up verifying keys and insert them into the per-contract map.
730        // TODO: This vk map can potentially use a lot of RAM. Perhaps load keys on-demand at verification time?
731        debug!(target: "validator::verification::verify_transaction", "Performing VerifyingKey lookups from the sled db");
732        for (zkas_ns, _) in &zkp_pub {
733            let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();
734
735            // TODO: This will be a problem in case of ::deploy, unless we force a different
736            // namespace and disable updating existing circuit. Might be a smart idea to do
737            // so in order to have to care less about being able to verify historical txs.
738            if inner_vk_map.contains_key(zkas_ns.as_str()) {
739                continue
740            }
741
742            let (zkbin, vk) =
743                overlay.lock().unwrap().contracts.get_zkas(&call.data.contract_id, zkas_ns)?;
744
745            inner_vk_map.insert(zkas_ns.to_string(), vk);
746            circuits_to_verify.push(zkbin);
747        }
748
749        zkp_table.push(zkp_pub);
750        sig_table.push(sig_pub);
751
752        // After getting the metadata, we run the "exec" function with the same runtime
753        // and the same payload. We keep the returned state update in a buffer, prefixed
754        // by the call function ID, enforcing the state update function in the contract.
755        debug!(target: "validator::verification::verify_transaction", "Executing \"exec\" call");
756        let mut state_update = vec![call.data.data[0]];
757        state_update.append(&mut runtime.exec(call_payload)?);
758        debug!(target: "validator::verification::verify_transaction", "Successfully executed \"exec\" call");
759
760        // If that was successful, we apply the state update in the ephemeral overlay.
761        debug!(target: "validator::verification::verify_transaction", "Executing \"apply\" call");
762        runtime.apply(&state_update)?;
763        debug!(target: "validator::verification::verify_transaction", "Successfully executed \"apply\" call");
764
765        // If this call is supposed to deploy a new contract, we have to instantiate
766        // a new `Runtime` and run its deploy function.
767        if call.data.is_deployment()
768        /* DeployV1 */
769        {
770            debug!(target: "validator::verification::verify_transaction", "Deploying new contract");
771            // Deserialize the deployment parameters
772            let deploy_params: DeployParamsV1 = deserialize_async(&call.data.data[1..]).await?;
773            let deploy_cid = ContractId::derive_public(deploy_params.public_key);
774
775            // Instantiate the new deployment runtime
776            let mut deploy_runtime = Runtime::new(
777                &deploy_params.wasm_bincode,
778                overlay.clone(),
779                deploy_cid,
780                verifying_block_height,
781                block_target,
782                tx_hash,
783                call_idx,
784            )?;
785
786            deploy_runtime.deploy(&deploy_params.ix)?;
787
788            let deploy_gas_used = deploy_runtime.gas_used();
789            debug!(target: "validator::verification::verify_transaction", "The gas used for deployment call {call:?} of transaction {tx_hash}: {deploy_gas_used}");
790            gas_data.deployments += deploy_gas_used;
791        }
792
793        // At this point we're done with the call and move on to the next one.
794        // Accumulate the WASM gas used.
795        let wasm_gas_used = runtime.gas_used();
796        debug!(target: "validator::verification::verify_transaction", "The gas used for WASM call {call:?} of transaction {tx_hash}: {wasm_gas_used}");
797
798        // Append the used wasm gas
799        gas_data.wasm += wasm_gas_used;
800    }
801
802    // The signature fee is tx_size + fixed_sig_fee * n_signatures
803    gas_data.signatures = (PALLAS_SCHNORR_SIGNATURE_FEE * tx.signatures.len() as u64) +
804        serialize_async(tx).await.len() as u64;
805    debug!(target: "validator::verification::verify_transaction", "The gas used for signature of transaction {tx_hash}: {}", gas_data.signatures);
806
807    // The ZK circuit fee is calculated using a function in validator/fees.rs
808    for zkbin in circuits_to_verify.iter() {
809        let zk_circuit_gas_used = circuit_gas_use(zkbin);
810        debug!(target: "validator::verification::verify_transaction", "The gas used for ZK circuit in namespace {} of transaction {tx_hash}: {zk_circuit_gas_used}", zkbin.namespace);
811
812        // Append the used zk circuit gas
813        gas_data.zk_circuits += zk_circuit_gas_used;
814    }
815
816    // Store the calculated total gas used to avoid recalculating it for subsequent uses
817    let total_gas_used = gas_data.total_gas_used();
818
819    if verify_fee {
820        // Deserialize the fee call to find the paid fee
821        let fee: u64 = match deserialize_async(&tx.calls[fee_call_idx].data.data[1..9]).await {
822            Ok(v) => v,
823            Err(e) => {
824                error!(
825                    target: "validator::verification::verify_transaction",
826                    "[VALIDATOR] Failed deserializing tx {tx_hash} fee call: {e}"
827                );
828                return Err(TxVerifyFailed::InvalidFee.into())
829            }
830        };
831
832        // Compute the required fee for this transaction
833        let required_fee = compute_fee(&total_gas_used);
834
835        // Check that enough fee has been paid for the used gas in this transaction
836        if required_fee > fee {
837            error!(
838                target: "validator::verification::verify_transaction",
839                "[VALIDATOR] Transaction {tx_hash} has insufficient fee. Required: {required_fee}, Paid: {fee}"
840            );
841            return Err(TxVerifyFailed::InsufficientFee.into())
842        }
843        debug!(target: "validator::verification::verify_transaction", "The gas paid for transaction {tx_hash}: {}", gas_data.paid);
844
845        // Store paid fee
846        gas_data.paid = fee;
847    }
848
849    // When we're done looping and executing over the tx's contract calls and
850    // (optionally) made sure that enough fee was paid, we now move on with
851    // verification. First we verify the transaction signatures and then we
852    // verify any accompanying ZK proofs.
853    debug!(target: "validator::verification::verify_transaction", "Verifying signatures for transaction {tx_hash}");
854    if sig_table.len() != tx.signatures.len() {
855        error!(
856            target: "validator::verification::verify_transaction",
857            "[VALIDATOR] Incorrect number of signatures in tx {tx_hash}"
858        );
859        return Err(TxVerifyFailed::MissingSignatures.into())
860    }
861
862    if let Err(e) = tx.verify_sigs(sig_table) {
863        error!(
864            target: "validator::verification::verify_transaction",
865            "[VALIDATOR] Signature verification for tx {tx_hash} failed: {e}"
866        );
867        return Err(TxVerifyFailed::InvalidSignature.into())
868    }
869    debug!(target: "validator::verification::verify_transaction", "Signature verification successful");
870
871    debug!(target: "validator::verification::verify_transaction", "Verifying ZK proofs for transaction {tx_hash}");
872    if let Err(e) = tx.verify_zkps(verifying_keys, zkp_table).await {
873        error!(
874            target: "validator::verification::verify_transaction",
875            "[VALIDATOR] ZK proof verification for tx {tx_hash} failed: {e}"
876        );
877        return Err(TxVerifyFailed::InvalidZkProof.into())
878    }
879    debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
880
881    // Append hash to merkle tree
882    append_tx_to_merkle_tree(tree, tx);
883
884    debug!(target: "validator::verification::verify_transaction", "The total gas used for transaction {tx_hash}: {total_gas_used}");
885    debug!(target: "validator::verification::verify_transaction", "Transaction {tx_hash} verified successfully");
886    Ok(gas_data)
887}
888
889/// Apply given [`Transaction`] to the provided overlay.
890/// Additionally, append its hash to the provided Merkle tree.
891pub async fn apply_transaction(
892    overlay: &BlockchainOverlayPtr,
893    verifying_block_height: u32,
894    block_target: u32,
895    tx: &Transaction,
896    tree: &mut MerkleTree,
897) -> Result<()> {
898    let tx_hash = tx.hash();
899    debug!(target: "validator::verification::apply_transaction", "Applying transaction {tx_hash}");
900
901    // Write the transaction calls payload data
902    let mut payload = vec![];
903    tx.calls.encode_async(&mut payload).await?;
904
905    // Iterate over all calls to get the metadata
906    for (idx, call) in tx.calls.iter().enumerate() {
907        debug!(target: "validator::verification::apply_transaction", "Executing contract call {idx}");
908
909        debug!(target: "validator::verification::apply_transaction", "Instantiating WASM runtime");
910        let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
911        let mut runtime = Runtime::new(
912            &wasm,
913            overlay.clone(),
914            call.data.contract_id,
915            verifying_block_height,
916            block_target,
917            tx_hash,
918            idx as u8,
919        )?;
920
921        // Run the "exec" function. We keep the returned state update in a buffer, prefixed
922        // by the call function ID, enforcing the state update function in the contract.
923        debug!(target: "validator::verification::apply_transaction", "Executing \"exec\" call");
924        let mut state_update = vec![call.data.data[0]];
925        state_update.append(&mut runtime.exec(&payload)?);
926        debug!(target: "validator::verification::apply_transaction", "Successfully executed \"exec\" call");
927
928        // If that was successful, we apply the state update in the ephemeral overlay
929        debug!(target: "validator::verification::apply_transaction", "Executing \"apply\" call");
930        runtime.apply(&state_update)?;
931        debug!(target: "validator::verification::apply_transaction", "Successfully executed \"apply\" call");
932
933        // If this call is supposed to deploy a new contract, we have to instantiate
934        // a new `Runtime` and run its deploy function.
935        if call.data.is_deployment()
936        /* DeployV1 */
937        {
938            debug!(target: "validator::verification::apply_transaction", "Deploying new contract");
939            // Deserialize the deployment parameters
940            let deploy_params: DeployParamsV1 = deserialize_async(&call.data.data[1..]).await?;
941            let deploy_cid = ContractId::derive_public(deploy_params.public_key);
942
943            // Instantiate the new deployment runtime
944            let mut deploy_runtime = Runtime::new(
945                &deploy_params.wasm_bincode,
946                overlay.clone(),
947                deploy_cid,
948                verifying_block_height,
949                block_target,
950                tx_hash,
951                idx as u8,
952            )?;
953
954            deploy_runtime.deploy(&deploy_params.ix)?;
955        }
956    }
957
958    // Append hash to merkle tree
959    append_tx_to_merkle_tree(tree, tx);
960
961    debug!(target: "validator::verification::apply_transaction", "Transaction {tx_hash} applied successfully");
962    Ok(())
963}
964
965/// Verify a set of [`Transaction`] in sequence and apply them if all
966/// are valid. In case any of the transactions fail, they will be
967/// returned to the caller as an error. If all transactions are valid,
968/// the function will return the total gas used and total paid fees
969/// from all the transactions. Additionally, their hash is appended to
970/// the provided Merkle tree.
971///
972/// Note: Always remember to purge new trees from the database if not
973/// needed.
974pub async fn verify_transactions(
975    overlay: &BlockchainOverlayPtr,
976    verifying_block_height: u32,
977    block_target: u32,
978    txs: &[Transaction],
979    tree: &mut MerkleTree,
980    verify_fees: bool,
981) -> Result<(u64, u64)> {
982    debug!(target: "validator::verification::verify_transactions", "Verifying {} transactions", txs.len());
983    if txs.is_empty() {
984        return Ok((0, 0))
985    }
986
987    // Tracker for failed txs
988    let mut erroneous_txs = vec![];
989
990    // Total gas accumulators
991    let mut total_gas_used = 0;
992    let mut total_gas_paid = 0;
993
994    // Map of ZK proof verifying keys for the current transaction batch
995    let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
996
997    // Initialize the map
998    for tx in txs {
999        for call in &tx.calls {
1000            vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
1001        }
1002    }
1003
1004    // Iterate over transactions and attempt to verify them
1005    for tx in txs {
1006        overlay.lock().unwrap().checkpoint();
1007        let gas_data = match verify_transaction(
1008            overlay,
1009            verifying_block_height,
1010            block_target,
1011            tx,
1012            tree,
1013            &mut vks,
1014            verify_fees,
1015        )
1016        .await
1017        {
1018            Ok(gas_values) => gas_values,
1019            Err(e) => {
1020                warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {e}");
1021                erroneous_txs.push(tx.clone());
1022                overlay.lock().unwrap().revert_to_checkpoint();
1023                continue
1024            }
1025        };
1026
1027        // Store the gas used by the verified transaction
1028        let tx_gas_used = gas_data.total_gas_used();
1029
1030        // Calculate current accumulated gas usage
1031        let accumulated_gas_usage = total_gas_used + tx_gas_used;
1032
1033        // Check gas limit - if accumulated gas used exceeds it, break out of loop
1034        if accumulated_gas_usage > BLOCK_GAS_LIMIT {
1035            warn!(
1036                target: "validator::verification::verify_transactions",
1037                "Transaction {} exceeds configured transaction gas limit: {accumulated_gas_usage} - {BLOCK_GAS_LIMIT}",
1038                tx.hash()
1039            );
1040            erroneous_txs.push(tx.clone());
1041            overlay.lock().unwrap().revert_to_checkpoint();
1042            break
1043        }
1044
1045        // Update accumulated total gas
1046        total_gas_used += tx_gas_used;
1047        total_gas_paid += gas_data.paid;
1048    }
1049
1050    if !erroneous_txs.is_empty() {
1051        return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
1052    }
1053
1054    Ok((total_gas_used, total_gas_paid))
1055}
1056
1057/// Apply given set of [`Transaction`] in sequence, without formal verification.
1058/// In case any of the transactions fail, they will be returned to the caller as an error.
1059/// Additionally, their hash is appended to the provided Merkle tree.
1060async fn apply_transactions(
1061    overlay: &BlockchainOverlayPtr,
1062    verifying_block_height: u32,
1063    block_target: u32,
1064    txs: &[Transaction],
1065    tree: &mut MerkleTree,
1066) -> Result<()> {
1067    debug!(target: "validator::verification::apply_transactions", "Applying {} transactions", txs.len());
1068    if txs.is_empty() {
1069        return Ok(())
1070    }
1071
1072    // Tracker for failed txs
1073    let mut erroneous_txs = vec![];
1074
1075    // Iterate over transactions and attempt to apply them
1076    for tx in txs {
1077        overlay.lock().unwrap().checkpoint();
1078        if let Err(e) =
1079            apply_transaction(overlay, verifying_block_height, block_target, tx, tree).await
1080        {
1081            warn!(target: "validator::verification::apply_transactions", "Transaction apply failed: {e}");
1082            erroneous_txs.push(tx.clone());
1083            overlay.lock().unwrap().revert_to_checkpoint();
1084        };
1085    }
1086
1087    if !erroneous_txs.is_empty() {
1088        return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
1089    }
1090
1091    Ok(())
1092}
1093
1094/// Verify given [`Proposal`] against provided consensus state.
1095///
1096/// A proposal is considered valid when the following rules apply:
1097///     1. Proposal hash matches the actual block one
1098///     2. Block is valid
1099/// Additional validity rules can be applied.
1100///
1101/// Note: Always remember to purge new trees from the database if not
1102/// needed.
1103pub async fn verify_proposal(
1104    consensus: &Consensus,
1105    proposal: &Proposal,
1106    verify_fees: bool,
1107) -> Result<(Fork, Option<usize>)> {
1108    // Check if proposal hash matches actual one (1)
1109    let proposal_hash = proposal.block.hash();
1110    if proposal.hash != proposal_hash {
1111        warn!(
1112            target: "validator::verification::verify_proposal", "Received proposal contains mismatched hashes: {} - {proposal_hash}",
1113            proposal.hash
1114        );
1115        return Err(Error::ProposalHashesMissmatchError)
1116    }
1117
1118    // Check if proposal extends any existing forks
1119    let (fork, index) = consensus.find_extended_fork(proposal).await?;
1120
1121    // Grab overlay last block
1122    let previous = fork.overlay.lock().unwrap().last_block()?;
1123
1124    // Verify proposal block (2)
1125    if let Err(e) = verify_block(
1126        &fork.overlay,
1127        &fork.diffs,
1128        &fork.module,
1129        &proposal.block,
1130        &previous,
1131        verify_fees,
1132    )
1133    .await
1134    {
1135        error!(target: "validator::verification::verify_proposal", "Erroneous proposal block found: {e}");
1136        return Err(Error::BlockIsInvalid(proposal.hash.as_string()))
1137    };
1138
1139    Ok((fork, index))
1140}
1141
1142/// Verify given [`Proposal`] against provided fork state.
1143///
1144/// A proposal is considered valid when the following rules apply:
1145///     1. Proposal hash matches the actual block one
1146///     2. Block is valid
1147/// Additional validity rules can be applied.
1148///
1149/// Note: Always remember to purge new trees from the database if not
1150/// needed.
1151pub async fn verify_fork_proposal(
1152    fork: &mut Fork,
1153    proposal: &Proposal,
1154    verify_fees: bool,
1155) -> Result<()> {
1156    // Check if proposal hash matches actual one (1)
1157    let proposal_hash = proposal.block.hash();
1158    if proposal.hash != proposal_hash {
1159        warn!(
1160            target: "validator::verification::verify_fork_proposal", "Received proposal contains mismatched hashes: {} - {proposal_hash}",
1161            proposal.hash
1162        );
1163        return Err(Error::ProposalHashesMissmatchError)
1164    }
1165
1166    // Grab overlay last block
1167    let previous = fork.overlay.lock().unwrap().last_block()?;
1168
1169    // Verify proposal block (2)
1170    if let Err(e) = verify_block(
1171        &fork.overlay,
1172        &fork.diffs,
1173        &fork.module,
1174        &proposal.block,
1175        &previous,
1176        verify_fees,
1177    )
1178    .await
1179    {
1180        error!(target: "validator::verification::verify_fork_proposal", "Erroneous proposal block found: {e}");
1181        return Err(Error::BlockIsInvalid(proposal.hash.as_string()))
1182    };
1183
1184    Ok(())
1185}