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 }