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