verification.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. crypto::{PublicKey, CONSENSUS_CONTRACT_ID},
  21. pasta::pallas,
  22. };
  23. use darkfi_serial::{Decodable, Encodable, WriteExt};
  24. use log::{debug, error, warn};
  25. use crate::{
  26. blockchain::{BlockInfo, BlockchainOverlayPtr},
  27. error::TxVerifyFailed,
  28. runtime::vm_runtime::Runtime,
  29. tx::Transaction,
  30. util::time::TimeKeeper,
  31. zk::VerifyingKey,
  32. Error, Result,
  33. };
  34. /// Validate given genesis [`BlockInfo`], and apply it to the provided overlay
  35. pub async fn verify_genesis_block(
  36. overlay: &BlockchainOverlayPtr,
  37. time_keeper: &TimeKeeper,
  38. block: &BlockInfo,
  39. genesis_txs_total: u64,
  40. ) -> Result<()> {
  41. let block_hash = block.blockhash().to_string();
  42. debug!(target: "validator::verification::verify_genesis_block", "Validating genesis block {}", block_hash);
  43. // Check if block already exists
  44. if overlay.lock().unwrap().has_block(block)? {
  45. return Err(Error::BlockAlreadyExists(block_hash))
  46. }
  47. // Block slot must be the same as the time keeper verifying slot
  48. if block.header.slot != time_keeper.verifying_slot {
  49. return Err(Error::VerifyingSlotMissmatch())
  50. }
  51. // Check genesis slot exist
  52. if block.slots.len() != 1 {
  53. return Err(Error::BlockIsInvalid(block_hash))
  54. }
  55. // Retrieve genesis slot
  56. let genesis_slot = block.slots.last().unwrap();
  57. // Genesis block slot total token must correspond to the total
  58. // of all genesis transactions public inputs (genesis distribution).
  59. if genesis_slot.total_tokens != genesis_txs_total {
  60. return Err(Error::SlotIsInvalid(genesis_slot.id))
  61. }
  62. // Verify there is not reward
  63. if genesis_slot.reward != 0 {
  64. return Err(Error::SlotIsInvalid(genesis_slot.id))
  65. }
  66. // Genesis transaction must be the Transaction::default() one (empty)
  67. if block.producer.proposal != Transaction::default() {
  68. error!(target: "validator::verification::verify_genesis_block", "Genesis proposal transaction is not default one");
  69. return Err(TxVerifyFailed::ErroneousTxs(vec![block.producer.proposal.clone()]).into())
  70. }
  71. // Verify transactions
  72. let erroneous_txs = verify_transactions(overlay, time_keeper, &block.txs).await?;
  73. if !erroneous_txs.is_empty() {
  74. warn!(target: "validator::verification::verify_genesis_block", "Erroneous transactions found in set");
  75. overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  76. return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
  77. }
  78. // Insert block
  79. overlay.lock().unwrap().add_block(block)?;
  80. debug!(target: "validator::verification::verify_genesis_block", "Genesis block {} verified successfully", block_hash);
  81. Ok(())
  82. }
  83. /// Validate given [`BlockInfo`], and apply it to the provided overlay
  84. pub async fn verify_block(
  85. overlay: &BlockchainOverlayPtr,
  86. time_keeper: &TimeKeeper,
  87. block: &BlockInfo,
  88. previous: &BlockInfo,
  89. expected_reward: u64,
  90. testing_mode: bool,
  91. ) -> Result<()> {
  92. let block_hash = block.blockhash().to_string();
  93. debug!(target: "validator::verification::verify_block", "Validating block {}", block_hash);
  94. // Check if block already exists
  95. if overlay.lock().unwrap().has_block(block)? {
  96. return Err(Error::BlockAlreadyExists(block_hash))
  97. }
  98. // Block slot must be the same as the time keeper verifying slot
  99. if block.header.slot != time_keeper.verifying_slot {
  100. return Err(Error::VerifyingSlotMissmatch())
  101. }
  102. // Validate block, using its previous
  103. block.validate(previous, expected_reward)?;
  104. // Validate proposal transaction if not in testing mode
  105. if !testing_mode {
  106. verify_proposal_transaction(overlay, time_keeper, &block.producer.proposal).await?;
  107. verify_producer_signature(block)?;
  108. }
  109. // Verify transactions
  110. let erroneous_txs = verify_transactions(overlay, time_keeper, &block.txs).await?;
  111. if !erroneous_txs.is_empty() {
  112. warn!(target: "validator::verification::verify_block", "Erroneous transactions found in set");
  113. overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  114. return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
  115. }
  116. // Insert block
  117. overlay.lock().unwrap().add_block(block)?;
  118. debug!(target: "validator::verification::verify_block", "Block {} verified successfully", block_hash);
  119. Ok(())
  120. }
  121. /// Validate block proposer signature, using the proposal transaction signature as signing key
  122. /// over blocks header, transactions and slots.
  123. pub fn verify_producer_signature(_block: &BlockInfo) -> Result<()> {
  124. // TODO:
  125. // Grab public key from proposal transaction metadata on verify_proposal_transaction
  126. // and pass it here to verify the signature
  127. Ok(())
  128. }
  129. /// Validate WASM execution, signatures, and ZK proofs for a given proposal [`Transaction`],
  130. /// and apply it to the provided overlay.
  131. pub async fn verify_proposal_transaction(
  132. overlay: &BlockchainOverlayPtr,
  133. time_keeper: &TimeKeeper,
  134. tx: &Transaction,
  135. ) -> Result<()> {
  136. let tx_hash = tx.hash()?;
  137. debug!(target: "validator::verification::verify_proposal_transaction", "Validating proposal transaction {}", tx_hash);
  138. // Transaction must contain a single Consensus::Proposal (0x02) call
  139. if tx.calls.len() != 1 ||
  140. (tx.calls[0].contract_id != *CONSENSUS_CONTRACT_ID && tx.calls[0].data[0] != 0x02)
  141. {
  142. error!(target: "validator::verification::verify_proposal_transaction", "Proposal transaction is malformed");
  143. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  144. }
  145. // Map of ZK proof verifying keys for the current transaction batch
  146. let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  147. // Initialize the map
  148. vks.insert(tx.calls[0].contract_id.to_bytes(), HashMap::new());
  149. // TODO: when fee is implemented, differentiate here since this transaction
  150. // won't have fee
  151. verify_transaction(overlay, time_keeper, tx, &mut vks).await?;
  152. debug!(target: "validator::verification::verify_proposal_transaction", "Proposal transaction {} verified successfully", tx_hash);
  153. Ok(())
  154. }
  155. /// Validate WASM execution, signatures, and ZK proofs for a given [`Transaction`],
  156. /// and apply it to the provided overlay.
  157. pub async fn verify_transaction(
  158. overlay: &BlockchainOverlayPtr,
  159. time_keeper: &TimeKeeper,
  160. tx: &Transaction,
  161. verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
  162. ) -> Result<()> {
  163. let tx_hash = tx.hash()?;
  164. debug!(target: "validator::verification::verify_transaction", "Validating transaction {}", tx_hash);
  165. // Table of public inputs used for ZK proof verification
  166. let mut zkp_table = vec![];
  167. // Table of public keys used for signature verification
  168. let mut sig_table = vec![];
  169. // Iterate over all calls to get the metadata
  170. for (idx, call) in tx.calls.iter().enumerate() {
  171. debug!(target: "validator::verification::verify_transaction", "Executing contract call {}", idx);
  172. // Write the actual payload data
  173. let mut payload = vec![];
  174. payload.write_u32(idx as u32)?; // Call index
  175. tx.calls.encode(&mut payload)?; // Actual call data
  176. debug!(target: "validator::verification::verify_transaction", "Instantiating WASM runtime");
  177. let wasm = overlay.lock().unwrap().wasm_bincode.get(call.contract_id)?;
  178. let mut runtime =
  179. Runtime::new(&wasm, overlay.clone(), call.contract_id, time_keeper.clone())?;
  180. debug!(target: "validator::verification::verify_transaction", "Executing \"metadata\" call");
  181. let metadata = runtime.metadata(&payload)?;
  182. // Decode the metadata retrieved from the execution
  183. let mut decoder = Cursor::new(&metadata);
  184. // The tuple is (zkasa_ns, public_inputs)
  185. let zkp_pub: Vec<(String, Vec<pallas::Base>)> = Decodable::decode(&mut decoder)?;
  186. let sig_pub: Vec<PublicKey> = Decodable::decode(&mut decoder)?;
  187. // TODO: Make sure we've read all the bytes above.
  188. debug!(target: "validator::verification::verify_transaction", "Successfully executed \"metadata\" call");
  189. // Here we'll look up verifying keys and insert them into the per-contract map.
  190. debug!(target: "validator::verification::verify_transaction", "Performing VerifyingKey lookups from the sled db");
  191. for (zkas_ns, _) in &zkp_pub {
  192. let inner_vk_map = verifying_keys.get_mut(&call.contract_id.to_bytes()).unwrap();
  193. // TODO: This will be a problem in case of ::deploy, unless we force a different
  194. // namespace and disable updating existing circuit. Might be a smart idea to do
  195. // so in order to have to care less about being able to verify historical txs.
  196. if inner_vk_map.contains_key(zkas_ns.as_str()) {
  197. continue
  198. }
  199. let (_, vk) = overlay.lock().unwrap().contracts.get_zkas(&call.contract_id, zkas_ns)?;
  200. inner_vk_map.insert(zkas_ns.to_string(), vk);
  201. }
  202. zkp_table.push(zkp_pub);
  203. sig_table.push(sig_pub);
  204. // After getting the metadata, we run the "exec" function with the same runtime
  205. // and the same payload.
  206. debug!(target: "validator::verification::verify_transaction", "Executing \"exec\" call");
  207. let state_update = runtime.exec(&payload)?;
  208. debug!(target: "validator::verification::verify_transaction", "Successfully executed \"exec\" call");
  209. // If that was successful, we apply the state update in the ephemeral overlay.
  210. debug!(target: "validator::verification::verify_transaction", "Executing \"apply\" call");
  211. runtime.apply(&state_update)?;
  212. debug!(target: "validator::verification::verify_transaction", "Successfully executed \"apply\" call");
  213. // At this point we're done with the call and move on to the next one.
  214. }
  215. // When we're done looping and executing over the tx's contract calls, we now
  216. // move on with verification. First we verify the signatures as that's cheaper,
  217. // and then finally we verify the ZK proofs.
  218. debug!(target: "validator::verification::verify_transaction", "Verifying signatures for transaction {}", tx_hash);
  219. if sig_table.len() != tx.signatures.len() {
  220. error!(target: "validator::verification::verify_transaction", "Incorrect number of signatures in tx {}", tx_hash);
  221. return Err(TxVerifyFailed::MissingSignatures.into())
  222. }
  223. // TODO: Go through the ZK circuits that have to be verified and account for the opcodes.
  224. if let Err(e) = tx.verify_sigs(sig_table) {
  225. error!(target: "validator::verification::verify_transaction", "Signature verification for tx {} failed: {}", tx_hash, e);
  226. return Err(TxVerifyFailed::InvalidSignature.into())
  227. }
  228. debug!(target: "validator::verification::verify_transaction", "Signature verification successful");
  229. debug!(target: "validator::verification::verify_transaction", "Verifying ZK proofs for transaction {}", tx_hash);
  230. if let Err(e) = tx.verify_zkps(verifying_keys, zkp_table).await {
  231. error!(target: "validator::verification::verify_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
  232. return Err(TxVerifyFailed::InvalidZkProof.into())
  233. }
  234. debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
  235. debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);
  236. Ok(())
  237. }
  238. /// Validate a set of [`Transaction`] in sequence and apply them if all are valid.
  239. /// In case any of the transactions fail, they will be returned to the caller.
  240. /// The function takes a boolean called `write` which tells it to actually write
  241. /// the state transitions to the database.
  242. pub async fn verify_transactions(
  243. overlay: &BlockchainOverlayPtr,
  244. time_keeper: &TimeKeeper,
  245. txs: &[Transaction],
  246. ) -> Result<Vec<Transaction>> {
  247. debug!(target: "validator::verification::verify_transactions", "Verifying {} transactions", txs.len());
  248. // Tracker for failed txs
  249. let mut erroneous_txs = vec![];
  250. // Map of ZK proof verifying keys for the current transaction batch
  251. let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  252. // Initialize the map
  253. for tx in txs {
  254. for call in &tx.calls {
  255. vks.insert(call.contract_id.to_bytes(), HashMap::new());
  256. }
  257. }
  258. // Iterate over transactions and attempt to verify them
  259. for tx in txs {
  260. overlay.lock().unwrap().checkpoint();
  261. if let Err(e) = verify_transaction(overlay, time_keeper, tx, &mut vks).await {
  262. warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
  263. erroneous_txs.push(tx.clone());
  264. // TODO: verify this works as expected
  265. overlay.lock().unwrap().revert_to_checkpoint()?;
  266. }
  267. }
  268. Ok(erroneous_txs)
  269. }