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