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