verification.rs 28 KB

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