verification.rs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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. use std::collections::HashMap;
  19. use darkfi_sdk::{
  20. blockchain::{block_version, expected_reward},
  21. crypto::{
  22. schnorr::SchnorrPublic, ContractId, PublicKey, CONSENSUS_CONTRACT_ID,
  23. DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
  24. },
  25. dark_tree::dark_forest_leaf_vec_integrity_check,
  26. deploy::DeployParamsV1,
  27. pasta::pallas,
  28. };
  29. use darkfi_serial::{deserialize_async, AsyncDecodable, AsyncEncodable, AsyncWriteExt, WriteExt};
  30. use log::{debug, error, warn};
  31. use smol::io::Cursor;
  32. use crate::{
  33. blockchain::{BlockInfo, BlockchainOverlayPtr},
  34. error::TxVerifyFailed,
  35. runtime::vm_runtime::Runtime,
  36. tx::{Transaction, MAX_TX_CALLS, MIN_TX_CALLS},
  37. util::time::TimeKeeper,
  38. validator::{
  39. consensus::{Consensus, Fork, Proposal, TXS_CAP},
  40. pow::PoWModule,
  41. validation::validate_block,
  42. },
  43. zk::VerifyingKey,
  44. Error, Result,
  45. };
  46. /// Verify given genesis [`BlockInfo`], and apply it to the provided overlay
  47. pub async fn verify_genesis_block(
  48. overlay: &BlockchainOverlayPtr,
  49. time_keeper: &TimeKeeper,
  50. block: &BlockInfo,
  51. genesis_txs_total: u64,
  52. ) -> Result<()> {
  53. let block_hash = block.hash()?.to_string();
  54. debug!(target: "validator::verification::verify_genesis_block", "Validating genesis block {}", block_hash);
  55. // Check if block already exists
  56. if overlay.lock().unwrap().has_block(block)? {
  57. return Err(Error::BlockAlreadyExists(block_hash))
  58. }
  59. // Block height must be 0
  60. if block.header.height != 0 {
  61. return Err(Error::BlockIsInvalid(block_hash))
  62. }
  63. // Block height must be the same as the time keeper verifying slot
  64. if block.header.height != time_keeper.verifying_slot {
  65. return Err(Error::VerifyingSlotMissmatch())
  66. }
  67. // Check genesis slot exist
  68. if block.slots.len() != 1 {
  69. return Err(Error::BlockIsInvalid(block_hash))
  70. }
  71. // Retrieve genesis slot
  72. let genesis_slot = block.slots.last().unwrap();
  73. // Genesis block slot total token must correspond to the total
  74. // of all genesis transactions public inputs (genesis distribution).
  75. if genesis_slot.total_tokens != genesis_txs_total {
  76. return Err(Error::SlotIsInvalid(genesis_slot.id))
  77. }
  78. // Verify there is no reward
  79. if genesis_slot.reward != 0 {
  80. return Err(Error::SlotIsInvalid(genesis_slot.id))
  81. }
  82. // Verify transactions vector contains at least one(producers) transaction
  83. if block.txs.is_empty() {
  84. return Err(Error::BlockContainsNoTransactions(block_hash))
  85. }
  86. // Insert genesis slot so transactions can be validated against.
  87. // Since an overlay is used, original database is not affected.
  88. overlay.lock().unwrap().slots.insert(&[genesis_slot.clone()])?;
  89. // Genesis transaction must be the Transaction::default() one(empty)
  90. if block.txs[0] != Transaction::default() {
  91. error!(target: "validator::verification::verify_genesis_block", "Genesis proposal transaction is not default one");
  92. return Err(TxVerifyFailed::ErroneousTxs(vec![block.txs[0].clone()]).into())
  93. }
  94. // Verify transactions, exluding producer(first) one
  95. let txs = &block.txs[1..];
  96. let erroneous_txs = verify_transactions(overlay, time_keeper, txs, false).await?;
  97. if !erroneous_txs.is_empty() {
  98. warn!(target: "validator::verification::verify_genesis_block", "Erroneous transactions found in set");
  99. overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  100. return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
  101. }
  102. // Insert block
  103. overlay.lock().unwrap().add_block(block)?;
  104. debug!(target: "validator::verification::verify_genesis_block", "Genesis block {} verified successfully", block_hash);
  105. Ok(())
  106. }
  107. /// Verify given [`BlockInfo`], and apply it to the provided overlay
  108. pub async fn verify_block(
  109. overlay: &BlockchainOverlayPtr,
  110. time_keeper: &TimeKeeper,
  111. module: &PoWModule,
  112. block: &BlockInfo,
  113. previous: &BlockInfo,
  114. expected_reward: u64,
  115. pos_testing_mode: bool,
  116. ) -> Result<()> {
  117. let block_hash = block.hash()?.to_string();
  118. debug!(target: "validator::verification::verify_block", "Validating block {}", block_hash);
  119. // Check if block already exists
  120. if overlay.lock().unwrap().has_block(block)? {
  121. return Err(Error::BlockAlreadyExists(block_hash))
  122. }
  123. // Block height must be the same as the time keeper verifying slot
  124. if block.header.height != time_keeper.verifying_slot {
  125. return Err(Error::VerifyingSlotMissmatch())
  126. }
  127. // Block epoch must be the correct one, calculated by the time keeper configuration
  128. if block.header.epoch != time_keeper.slot_epoch(block.header.height) {
  129. return Err(Error::VerifyingSlotMissmatch())
  130. }
  131. // Validate block, using its previous
  132. validate_block(block, previous, expected_reward, module)?;
  133. // Verify transactions vector contains at least one(producers) transaction
  134. if block.txs.is_empty() {
  135. return Err(Error::BlockContainsNoTransactions(block_hash))
  136. }
  137. // Insert last block slot so transactions can be validated against.
  138. // Rest (empty) slots will be inserted along with the block.
  139. // Since an overlay is used, original database is not affected.
  140. overlay.lock().unwrap().slots.insert(&[block.slots.last().unwrap().clone()])?;
  141. // Verify proposal transaction.
  142. // For PoS blocks(version 2) verify if not in PoS testing mode.
  143. if block.header.version != 2 || !pos_testing_mode {
  144. let public_key =
  145. verify_producer_transaction(overlay, time_keeper, &block.txs[0], block.header.version)
  146. .await?;
  147. verify_producer_signature(block, &public_key)?;
  148. }
  149. // Verify transactions, exluding producer(first) one
  150. let txs = &block.txs[1..];
  151. let erroneous_txs = verify_transactions(overlay, time_keeper, txs, false).await?;
  152. if !erroneous_txs.is_empty() {
  153. warn!(target: "validator::verification::verify_block", "Erroneous transactions found in set");
  154. overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  155. return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
  156. }
  157. // Insert block
  158. overlay.lock().unwrap().add_block(block)?;
  159. debug!(target: "validator::verification::verify_block", "Block {} verified successfully", block_hash);
  160. Ok(())
  161. }
  162. /// Verify block proposer signature, using the proposal transaction signature as signing key
  163. /// over blocks header hash.
  164. pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> Result<()> {
  165. if !public_key.verify(&block.header.hash()?.as_bytes()[..], &block.signature) {
  166. warn!(target: "validator::verification::verify_producer_signature", "Proposer {} signature could not be verified", public_key);
  167. return Err(Error::InvalidSignature)
  168. }
  169. Ok(())
  170. }
  171. /// Verify WASM execution, signatures, and ZK proofs for a given producer [`Transaction`],
  172. /// and apply it to the provided overlay. Returns transaction signature public key.
  173. pub async fn verify_producer_transaction(
  174. overlay: &BlockchainOverlayPtr,
  175. time_keeper: &TimeKeeper,
  176. tx: &Transaction,
  177. block_version: u8,
  178. ) -> Result<PublicKey> {
  179. let tx_hash = tx.hash()?;
  180. debug!(target: "validator::verification::verify_producer_transaction", "Validating proposal transaction {}", tx_hash);
  181. // Producer transactions must contain a single, non-empty call
  182. if tx.calls.len() != 1 || tx.calls[0].data.data.is_empty() {
  183. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  184. }
  185. // Verify call based on version
  186. let call = &tx.calls[0];
  187. match block_version {
  188. 1 => {
  189. // Version 1 blocks must contain a Money::PoWReward(0x08) call
  190. if call.data.contract_id != *MONEY_CONTRACT_ID || call.data.data[0] != 0x08 {
  191. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  192. }
  193. }
  194. 2 => {
  195. // Version 2 blocks must contain a Consensus::Proposal(0x02) call
  196. if call.data.contract_id != *CONSENSUS_CONTRACT_ID || call.data.data[0] != 0x02 {
  197. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  198. }
  199. }
  200. _ => return Err(Error::BlockVersionIsInvalid(block_version)),
  201. }
  202. // Map of ZK proof verifying keys for the current transaction
  203. let mut verifying_keys: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  204. // Initialize the map
  205. verifying_keys.insert(call.data.contract_id.to_bytes(), HashMap::new());
  206. // Table of public inputs used for ZK proof verification
  207. let mut zkp_table = vec![];
  208. // Table of public keys used for signature verification
  209. let mut sig_table = vec![];
  210. debug!(target: "validator::verification::verify_producer_transaction", "Executing contract call");
  211. // Write the actual payload data
  212. let mut payload = vec![];
  213. payload.write_u32_async(0).await?; // Call index
  214. tx.calls.encode_async(&mut payload).await?; // Actual call data
  215. debug!(target: "validator::verification::verify_producer_transaction", "Instantiating WASM runtime");
  216. let wasm = overlay.lock().unwrap().wasm_bincode.get(call.data.contract_id)?;
  217. let mut runtime =
  218. Runtime::new(&wasm, overlay.clone(), call.data.contract_id, time_keeper.clone())?;
  219. debug!(target: "validator::verification::verify_producer_transaction", "Executing \"metadata\" call");
  220. let metadata = runtime.metadata(&payload)?;
  221. // Decode the metadata retrieved from the execution
  222. let mut decoder = Cursor::new(&metadata);
  223. // The tuple is (zkas_ns, public_inputs)
  224. let zkp_pub: Vec<(String, Vec<pallas::Base>)> =
  225. AsyncDecodable::decode_async(&mut decoder).await?;
  226. let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
  227. // Check that only one ZK proof and signature public key exist
  228. if zkp_pub.len() != 1 || sig_pub.len() != 1 {
  229. error!(target: "validator::verification::verify_producer_transaction", "Proposal contains multiple ZK proofs or signature public keys");
  230. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  231. }
  232. // TODO: Make sure we've read all the bytes above.
  233. debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"metadata\" call");
  234. // Here we'll look up verifying keys and insert them into the map.
  235. debug!(target: "validator::verification::verify_producer_transaction", "Performing VerifyingKey lookups from the sled db");
  236. for (zkas_ns, _) in &zkp_pub {
  237. // TODO: verify this is correct behavior
  238. let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();
  239. if inner_vk_map.contains_key(zkas_ns.as_str()) {
  240. continue
  241. }
  242. let (_, vk) =
  243. overlay.lock().unwrap().contracts.get_zkas(&call.data.contract_id, zkas_ns)?;
  244. inner_vk_map.insert(zkas_ns.to_string(), vk);
  245. }
  246. zkp_table.push(zkp_pub);
  247. let signature_public_key = *sig_pub.last().unwrap();
  248. sig_table.push(sig_pub);
  249. // After getting the metadata, we run the "exec" function with the same runtime
  250. // and the same payload.
  251. debug!(target: "validator::verification::verify_producer_transaction", "Executing \"exec\" call");
  252. let state_update = runtime.exec(&payload)?;
  253. debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"exec\" call");
  254. // If that was successful, we apply the state update in the ephemeral overlay.
  255. debug!(target: "validator::verification::verify_producer_transaction", "Executing \"apply\" call");
  256. runtime.apply(&state_update)?;
  257. debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"apply\" call");
  258. // When we're done executing over the tx's contract call, we now move on with verification.
  259. // First we verify the signatures as that's cheaper, and then finally we verify the ZK proofs.
  260. debug!(target: "validator::verification::verify_producer_transaction", "Verifying signatures for transaction {}", tx_hash);
  261. if sig_table.len() != tx.signatures.len() {
  262. error!(target: "validator::verification::verify_producer_transaction", "Incorrect number of signatures in tx {}", tx_hash);
  263. return Err(TxVerifyFailed::MissingSignatures.into())
  264. }
  265. // TODO: Go through the ZK circuits that have to be verified and account for the opcodes.
  266. if let Err(e) = tx.verify_sigs(sig_table) {
  267. error!(target: "validator::verification::verify_producer_transaction", "Signature verification for tx {} failed: {}", tx_hash, e);
  268. return Err(TxVerifyFailed::InvalidSignature.into())
  269. }
  270. debug!(target: "validator::verification::verify_producer_transaction", "Signature verification successful");
  271. debug!(target: "validator::verification::verify_producer_transaction", "Verifying ZK proofs for transaction {}", tx_hash);
  272. if let Err(e) = tx.verify_zkps(&verifying_keys, zkp_table).await {
  273. error!(target: "validator::verification::verify_proposal_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
  274. return Err(TxVerifyFailed::InvalidZkProof.into())
  275. }
  276. debug!(target: "validator::verification::verify_producer_transaction", "ZK proof verification successful");
  277. debug!(target: "validator::verification::verify_producer_transaction", "Proposal transaction {} verified successfully", tx_hash);
  278. Ok(signature_public_key)
  279. }
  280. /// Verify WASM execution, signatures, and ZK proofs for a given [`Transaction`],
  281. /// and apply it to the provided overlay.
  282. pub async fn verify_transaction(
  283. overlay: &BlockchainOverlayPtr,
  284. time_keeper: &TimeKeeper,
  285. tx: &Transaction,
  286. verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
  287. verify_fee: bool,
  288. ) -> Result<u64> {
  289. let tx_hash = tx.hash()?;
  290. debug!(target: "validator::verification::verify_transaction", "Validating transaction {}", tx_hash);
  291. // Gas accumulator
  292. let mut gas_used = 0;
  293. // Verify calls indexes integrity
  294. if verify_fee {
  295. dark_forest_leaf_vec_integrity_check(
  296. &tx.calls,
  297. Some(MIN_TX_CALLS + 1),
  298. Some(MAX_TX_CALLS),
  299. )?;
  300. } else {
  301. dark_forest_leaf_vec_integrity_check(&tx.calls, Some(MIN_TX_CALLS), Some(MAX_TX_CALLS))?;
  302. }
  303. // Table of public inputs used for ZK proof verification
  304. let mut zkp_table = vec![];
  305. // Table of public keys used for signature verification
  306. let mut sig_table = vec![];
  307. if verify_fee {
  308. let mut found_fee = false;
  309. // Verify that there is a Money::FeeV1 (0x00) call in the transaction
  310. for call in tx.calls.iter() {
  311. if call.data.contract_id == *MONEY_CONTRACT_ID && call.data.data[0] == 0x00 {
  312. found_fee = true;
  313. break
  314. }
  315. }
  316. if !found_fee {
  317. error!(
  318. target: "validator::verification::verify_transcation",
  319. "[VALIDATOR] Transaction {} does not contain fee payment call", tx_hash,
  320. );
  321. return Err(TxVerifyFailed::InvalidFee.into())
  322. }
  323. }
  324. // Iterate over all calls to get the metadata
  325. for (idx, call) in tx.calls.iter().enumerate() {
  326. // Transaction must not contain a reward call, Money::PoWReward(0x08) or Consensus::Proposal(0x02)
  327. if (call.data.contract_id == *MONEY_CONTRACT_ID && call.data.data[0] == 0x08) ||
  328. (call.data.contract_id == *CONSENSUS_CONTRACT_ID && call.data.data[0] == 0x02)
  329. {
  330. error!(target: "validator::verification::verify_transaction", "Reward transaction detected");
  331. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  332. }
  333. debug!(target: "validator::verification::verify_transaction", "Executing contract call {}", idx);
  334. // Write the actual payload data
  335. let mut payload = vec![];
  336. payload.write_u32(idx as u32)?; // Call index
  337. tx.calls.encode_async(&mut payload).await?; // Actual call data
  338. debug!(target: "validator::verification::verify_transaction", "Instantiating WASM runtime");
  339. let wasm = overlay.lock().unwrap().wasm_bincode.get(call.data.contract_id)?;
  340. let mut runtime =
  341. Runtime::new(&wasm, overlay.clone(), call.data.contract_id, time_keeper.clone())?;
  342. debug!(target: "validator::verification::verify_transaction", "Executing \"metadata\" call");
  343. let metadata = runtime.metadata(&payload)?;
  344. // Decode the metadata retrieved from the execution
  345. let mut decoder = Cursor::new(&metadata);
  346. // The tuple is (zkas_ns, public_inputs)
  347. let zkp_pub: Vec<(String, Vec<pallas::Base>)> =
  348. AsyncDecodable::decode_async(&mut decoder).await?;
  349. let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
  350. if decoder.position() != metadata.len() as u64 {
  351. error!(
  352. target: "validator::verification::verify_transaction",
  353. "[VALIDATOR] Failed decoding entire metadata buffer for {}:{}", tx_hash, idx,
  354. );
  355. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  356. }
  357. debug!(target: "validator::verification::verify_transaction", "Successfully executed \"metadata\" call");
  358. // Here we'll look up verifying keys and insert them into the per-contract map.
  359. debug!(target: "validator::verification::verify_transaction", "Performing VerifyingKey lookups from the sled db");
  360. for (zkas_ns, _) in &zkp_pub {
  361. let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();
  362. // TODO: This will be a problem in case of ::deploy, unless we force a different
  363. // namespace and disable updating existing circuit. Might be a smart idea to do
  364. // so in order to have to care less about being able to verify historical txs.
  365. if inner_vk_map.contains_key(zkas_ns.as_str()) {
  366. continue
  367. }
  368. let (_, vk) =
  369. overlay.lock().unwrap().contracts.get_zkas(&call.data.contract_id, zkas_ns)?;
  370. inner_vk_map.insert(zkas_ns.to_string(), vk);
  371. }
  372. zkp_table.push(zkp_pub);
  373. sig_table.push(sig_pub);
  374. // After getting the metadata, we run the "exec" function with the same runtime
  375. // and the same payload.
  376. debug!(target: "validator::verification::verify_transaction", "Executing \"exec\" call");
  377. let state_update = runtime.exec(&payload)?;
  378. debug!(target: "validator::verification::verify_transaction", "Successfully executed \"exec\" call");
  379. // If that was successful, we apply the state update in the ephemeral overlay.
  380. debug!(target: "validator::verification::verify_transaction", "Executing \"apply\" call");
  381. runtime.apply(&state_update)?;
  382. debug!(target: "validator::verification::verify_transaction", "Successfully executed \"apply\" call");
  383. // If this call is supposed to deploy a new contract, we have to instantiate
  384. // a new `Runtime` and run its deploy function.
  385. if call.data.contract_id == *DEPLOYOOOR_CONTRACT_ID && call.data.data[0] == 0x00
  386. /* DeployV1 */
  387. {
  388. debug!(target: "validator::verification::verify_transaction", "Deploying new contract");
  389. // Deserialize the deployment parameters
  390. let deploy_params: DeployParamsV1 = deserialize_async(&call.data.data[1..]).await?;
  391. let deploy_cid = ContractId::derive_public(deploy_params.public_key);
  392. // Instantiate the new deployment runtime
  393. let mut deploy_runtime = Runtime::new(
  394. &deploy_params.wasm_bincode,
  395. overlay.clone(),
  396. deploy_cid,
  397. time_keeper.clone(),
  398. )?;
  399. deploy_runtime.deploy(&deploy_params.ix)?;
  400. // Append the used gas
  401. gas_used += deploy_runtime.gas_used();
  402. }
  403. // At this point we're done with the call and move on to the next one.
  404. // Accumulate the WASM gas used.
  405. gas_used += runtime.gas_used();
  406. }
  407. // When we're done looping and executing over the tx's contract calls, we now
  408. // move on with verification. First we verify the signatures as that's cheaper,
  409. // and then finally we verify the ZK proofs.
  410. debug!(target: "validator::verification::verify_transaction", "Verifying signatures for transaction {}", tx_hash);
  411. if sig_table.len() != tx.signatures.len() {
  412. error!(target: "validator::verification::verify_transaction", "Incorrect number of signatures in tx {}", tx_hash);
  413. return Err(TxVerifyFailed::MissingSignatures.into())
  414. }
  415. // TODO: Go through the ZK circuits that have to be verified and account for the opcodes.
  416. if let Err(e) = tx.verify_sigs(sig_table) {
  417. error!(target: "validator::verification::verify_transaction", "Signature verification for tx {} failed: {}", tx_hash, e);
  418. return Err(TxVerifyFailed::InvalidSignature.into())
  419. }
  420. debug!(target: "validator::verification::verify_transaction", "Signature verification successful");
  421. debug!(target: "validator::verification::verify_transaction", "Verifying ZK proofs for transaction {}", tx_hash);
  422. if let Err(e) = tx.verify_zkps(verifying_keys, zkp_table).await {
  423. error!(target: "validator::verification::verify_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
  424. return Err(TxVerifyFailed::InvalidZkProof.into())
  425. }
  426. if verify_fee {
  427. // TODO: This counts 1 gas as 1 token unit. Pricing should be better specified.
  428. // TODO: Currently this doesn't account for signatures or ZK proofs
  429. // TODO: Currently this doesn't account for WASM host functions
  430. // Deserialize the first call to find the paid fee
  431. let fee: u64 = match deserialize_async(&tx.calls[0].data.data[1..9]).await {
  432. Ok(v) => v,
  433. Err(e) => {
  434. error!(
  435. target: "validator::verification::verify_transaction",
  436. "[VALIDATOR] Failed deserializing tx {} fee call: {}", tx_hash, e,
  437. );
  438. return Err(TxVerifyFailed::InvalidFee.into())
  439. }
  440. };
  441. // Check that enough fee has been paid for the used gas in this transaction.
  442. if gas_used > fee {
  443. error!(
  444. target: "validator::verification::verify_transaction",
  445. "[VALIDATOR] Transaction {} has insufficient fee. Required: {}, Paid: {}",
  446. tx_hash, gas_used, fee,
  447. );
  448. return Err(TxVerifyFailed::InsufficientFee.into())
  449. }
  450. }
  451. debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
  452. debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);
  453. Ok(gas_used)
  454. }
  455. /// Verify a set of [`Transaction`] in sequence and apply them if all are valid.
  456. /// In case any of the transactions fail, they will be returned to the caller.
  457. pub async fn verify_transactions(
  458. overlay: &BlockchainOverlayPtr,
  459. time_keeper: &TimeKeeper,
  460. txs: &[Transaction],
  461. verify_fees: bool,
  462. ) -> Result<Vec<Transaction>> {
  463. debug!(target: "validator::verification::verify_transactions", "Verifying {} transactions", txs.len());
  464. // Tracker for failed txs
  465. let mut erroneous_txs = vec![];
  466. // Gas accumulator
  467. let mut _gas_used = 0;
  468. // Map of ZK proof verifying keys for the current transaction batch
  469. let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  470. // Initialize the map
  471. for tx in txs {
  472. for call in &tx.calls {
  473. vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
  474. }
  475. }
  476. // Iterate over transactions and attempt to verify them
  477. for tx in txs {
  478. overlay.lock().unwrap().checkpoint();
  479. match verify_transaction(overlay, time_keeper, tx, &mut vks, verify_fees).await {
  480. Ok(gas) => _gas_used += gas,
  481. Err(e) => {
  482. warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
  483. erroneous_txs.push(tx.clone());
  484. // TODO: verify this works as expected
  485. overlay.lock().unwrap().revert_to_checkpoint()?;
  486. }
  487. }
  488. if verify_fees {
  489. // Enforce that enough fee is paid.
  490. todo!()
  491. }
  492. }
  493. Ok(erroneous_txs)
  494. }
  495. /// Verify given [`Proposal`] against provided consensus state
  496. pub async fn verify_proposal(
  497. consensus: &Consensus,
  498. proposal: &Proposal,
  499. ) -> Result<(Fork, Option<usize>)> {
  500. // TODO: verify proposal validations work as expected on versions change(cutoff)
  501. match block_version(proposal.block.header.height) {
  502. 1 => verify_pow_proposal(consensus, proposal).await,
  503. 2 => verify_pos_proposal(consensus, proposal).await,
  504. _ => Err(Error::BlockVersionIsInvalid(proposal.block.header.version)),
  505. }
  506. }
  507. /// Verify given PoW [`Proposal`] against provided consensus state,
  508. /// A proposal is considered valid when the following rules apply:
  509. /// 1. Proposal hash matches the actual block one
  510. /// 2. Block transactions don't exceed set limit
  511. /// 3. If proposal extends a known fork, verify block's slot
  512. /// correspond to the fork hot/live/next one
  513. /// 4. Block is valid
  514. /// Additional validity rules can be applied.
  515. pub async fn verify_pow_proposal(
  516. consensus: &Consensus,
  517. proposal: &Proposal,
  518. ) -> Result<(Fork, Option<usize>)> {
  519. // Check if proposal hash matches actual one (1)
  520. let proposal_hash = proposal.block.hash()?;
  521. if proposal.hash != proposal_hash {
  522. warn!(
  523. target: "validator::verification::verify_pow_proposal", "Received proposal contains mismatched hashes: {} - {}",
  524. proposal.hash, proposal_hash
  525. );
  526. return Err(Error::ProposalHashesMissmatchError)
  527. }
  528. // Check that proposal transactions don't exceed limit (2)
  529. if proposal.block.txs.len() > TXS_CAP {
  530. warn!(
  531. target: "validator::verification::verify_pow_proposal", "Received proposal transactions exceed configured cap: {} - {}",
  532. proposal.block.txs.len(),
  533. TXS_CAP
  534. );
  535. return Err(Error::ProposalTxsExceedCapError)
  536. }
  537. // Check if proposal extends any existing forks
  538. let (fork, index) = consensus.find_extended_fork(proposal).await?;
  539. // Verify block's slot correspond to the forks' hot/live/next one (3)
  540. if fork.slots.len() != 1 || fork.slots != proposal.block.slots {
  541. return Err(Error::ProposalContainsUnknownSlots)
  542. }
  543. // Insert block slot so transactions can be validated against.
  544. // Since this fork uses an overlay clone, original overlay is not affected.
  545. fork.overlay.lock().unwrap().slots.insert(&[proposal.block.slots.last().unwrap().clone()])?;
  546. // Grab overlay last block
  547. let previous = fork.overlay.lock().unwrap().last_block()?;
  548. // Retrieve expected reward
  549. let expected_reward = expected_reward(proposal.block.header.height);
  550. // Generate a time keeper for proposal block leight
  551. let mut time_keeper = consensus.time_keeper.current();
  552. time_keeper.verifying_slot = proposal.block.header.height;
  553. // Verify proposal block (4)
  554. if verify_block(
  555. &fork.overlay,
  556. &time_keeper,
  557. &fork.module,
  558. &proposal.block,
  559. &previous,
  560. expected_reward,
  561. consensus.pos_testing_mode,
  562. )
  563. .await
  564. .is_err()
  565. {
  566. error!(target: "validator::verification::verify_pow_proposal", "Erroneous proposal block found");
  567. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  568. return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
  569. };
  570. Ok((fork, index))
  571. }
  572. /// Verify given PoS [`Proposal`] against provided consensus state,
  573. /// A proposal is considered valid when the following rules apply:
  574. /// 1. Consensus(node) has not started current slot finalization
  575. /// 2. Proposal refers to current slot
  576. /// 3. Proposal hash matches the actual block one
  577. /// 4. Block transactions don't exceed set limit
  578. /// 5. If proposal extends a known fork, verify block slots
  579. /// correspond to the fork hot/live ones
  580. /// 6. Block is valid
  581. /// Additional validity rules can be applied.
  582. pub async fn verify_pos_proposal(
  583. consensus: &Consensus,
  584. proposal: &Proposal,
  585. ) -> Result<(Fork, Option<usize>)> {
  586. // Generate a time keeper for current slot
  587. let time_keeper = consensus.time_keeper.current();
  588. // Node have already checked for finalization in this slot (1)
  589. if time_keeper.verifying_slot <= *consensus.checked_finalization.read().await {
  590. warn!(target: "validator::verification::verify_pos_proposal", "Proposal received after finalization sync period.");
  591. return Err(Error::ProposalAfterFinalizationError)
  592. }
  593. // Proposal validations
  594. let hdr = &proposal.block.header;
  595. // Ignore proposal if not for current slot (2)
  596. if hdr.height != time_keeper.verifying_slot {
  597. return Err(Error::ProposalNotForCurrentSlotError)
  598. }
  599. // Check if proposal hash matches actual one (3)
  600. let proposal_hash = proposal.block.hash()?;
  601. if proposal.hash != proposal_hash {
  602. warn!(
  603. target: "validator::verification::verify_pos_proposal", "Received proposal contains mismatched hashes: {} - {}",
  604. proposal.hash, proposal_hash
  605. );
  606. return Err(Error::ProposalHashesMissmatchError)
  607. }
  608. // Check that proposal transactions don't exceed limit (4)
  609. if proposal.block.txs.len() > TXS_CAP {
  610. warn!(
  611. target: "validator::verification::verify_pos_proposal", "Received proposal transactions exceed configured cap: {} - {}",
  612. proposal.block.txs.len(),
  613. TXS_CAP
  614. );
  615. return Err(Error::ProposalTxsExceedCapError)
  616. }
  617. // Check if proposal extends any existing forks
  618. let (fork, index) = consensus.find_extended_fork(proposal).await?;
  619. // Verify block slots correspond to the forks' hot/live ones (5)
  620. if !fork.slots.is_empty() && fork.slots != proposal.block.slots {
  621. return Err(Error::ProposalContainsUnknownSlots)
  622. }
  623. // Insert last block slot so transactions can be validated against.
  624. // Rest (empty) slots will be inserted along with the block.
  625. // Since this fork uses an overlay clone, original overlay is not affected.
  626. fork.overlay.lock().unwrap().slots.insert(&[proposal.block.slots.last().unwrap().clone()])?;
  627. // Grab overlay last block
  628. let previous = fork.overlay.lock().unwrap().last_block()?;
  629. // Retrieve expected reward
  630. let expected_reward = expected_reward(time_keeper.verifying_slot);
  631. // Verify proposal block (6)
  632. if verify_block(
  633. &fork.overlay,
  634. &time_keeper,
  635. &fork.module,
  636. &proposal.block,
  637. &previous,
  638. expected_reward,
  639. consensus.pos_testing_mode,
  640. )
  641. .await
  642. .is_err()
  643. {
  644. error!(target: "validator::verification::verify_pos_proposal", "Erroneous proposal block found");
  645. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  646. return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
  647. };
  648. Ok((fork, index))
  649. }