main.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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 clap::Parser;
  20. use darkfi::{
  21. blockchain::{
  22. Blockchain, BlockchainOverlay, BlockchainOverlayPtr, block_store::append_tx_to_merkle_tree,
  23. },
  24. cli_desc,
  25. error::TxVerifyFailed,
  26. runtime::vm_runtime::Runtime,
  27. tx::{MAX_TX_CALLS, MIN_TX_CALLS, Transaction},
  28. util::path::expand_path,
  29. validator::{
  30. fees::{GasData, PALLAS_SCHNORR_SIGNATURE_FEE, circuit_gas_use},
  31. verification::verify_transaction,
  32. },
  33. zk::VerifyingKey,
  34. };
  35. use darkfi_sdk::{
  36. blockchain::compute_fee,
  37. crypto::{ContractId, MerkleTree, PublicKey},
  38. dark_tree::dark_forest_leaf_vec_integrity_check,
  39. deploy::DeployParamsV1,
  40. pasta::pallas,
  41. tx::TransactionHash,
  42. };
  43. use darkfi_serial::{AsyncDecodable, AsyncEncodable, deserialize_async, serialize_async};
  44. use smol::io::Cursor;
  45. #[derive(Parser)]
  46. #[command(about = cli_desc!())]
  47. struct Args {
  48. #[arg(short, long)]
  49. database_path: String,
  50. #[arg(short, long)]
  51. tx_hash: String,
  52. #[arg(long, conflicts_with_all = ["zkp", "sig"])]
  53. wasm: bool,
  54. #[arg(long, conflicts_with_all = ["wasm", "sig"])]
  55. zkp: bool,
  56. #[arg(long, conflicts_with_all = ["wasm", "zkp"])]
  57. sig: bool,
  58. }
  59. fn main() {
  60. smol::block_on(async {
  61. let args = Args::parse();
  62. replay_tx(args).await;
  63. });
  64. }
  65. async fn replay_tx(args: Args) {
  66. let db_path = expand_path(&args.database_path).unwrap();
  67. let sled_db = sled_overlay::sled::open(&db_path).unwrap();
  68. let blockchain = Blockchain::new(&sled_db).unwrap();
  69. let txh: TransactionHash = args.tx_hash.parse().unwrap();
  70. let (tx_height, _) =
  71. blockchain.transactions.get_location(&[txh], true).unwrap().first().unwrap().unwrap();
  72. let block_header_hash =
  73. blockchain.blocks.get_order(&[tx_height], true).unwrap().first().unwrap().unwrap();
  74. // Get all the transactions in the block of our target tx
  75. let block = blockchain
  76. .blocks
  77. .get(&[block_header_hash], true)
  78. .unwrap()
  79. .first()
  80. .unwrap()
  81. .clone()
  82. .unwrap();
  83. let txs: Vec<Transaction> = blockchain
  84. .transactions
  85. .get(&block.txs, true)
  86. .unwrap()
  87. .into_iter()
  88. .map(|t| t.unwrap())
  89. .collect();
  90. let (overlay, new_height) = rollback_database(&blockchain, txh).await;
  91. // Apply all transactions upto and including our target tx
  92. let mut tree = MerkleTree::new(1);
  93. for tx in txs {
  94. perform_tx_verification(&tx, new_height, &overlay, &mut tree, &args).await;
  95. // We have applied our target tx so let's bail out
  96. if tx.hash() == txh {
  97. break;
  98. }
  99. }
  100. }
  101. async fn perform_tx_verification(
  102. tx: &Transaction,
  103. new_height: u32,
  104. overlay: &BlockchainOverlayPtr,
  105. tree: &mut MerkleTree,
  106. args: &Args,
  107. ) {
  108. let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  109. for call in &tx.calls {
  110. vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
  111. }
  112. let result = if args.wasm {
  113. verify_transaction_wasm(overlay, new_height, 2, tx, tree, &mut vks, true).await.unwrap()
  114. } else if args.zkp {
  115. verify_transaction_zkps(overlay, new_height, 2, tx, tree, &mut vks, true).await.unwrap()
  116. } else if args.sig {
  117. verify_transaction_signatures(overlay, new_height, 2, tx, tree, &mut vks, true)
  118. .await
  119. .unwrap()
  120. } else {
  121. verify_transaction(overlay, new_height, 2, tx, tree, &mut vks, true).await.unwrap()
  122. };
  123. println!("Verify Transaction Result: {:?}", result);
  124. }
  125. /// Resets the blockchain in memory to a height before the transaction.
  126. async fn rollback_database(
  127. blockchain: &Blockchain,
  128. txh: TransactionHash,
  129. ) -> (BlockchainOverlayPtr, u32) {
  130. let (tx_height, _) =
  131. blockchain.transactions.get_location(&[txh], true).unwrap().first().unwrap().unwrap();
  132. let new_height = tx_height - 1;
  133. println!("Rolling back database to Height: {new_height}");
  134. let (last, _) = blockchain.last().unwrap();
  135. let heights: Vec<u32> = (new_height + 1..=last).rev().collect();
  136. let inverse_diffs = blockchain.blocks.get_state_inverse_diff(&heights, true).unwrap();
  137. let overlay = BlockchainOverlay::new(blockchain).unwrap();
  138. let overlay_lock = overlay.lock().unwrap();
  139. let mut lock = overlay_lock.overlay.lock().unwrap();
  140. for inverse_diff in inverse_diffs {
  141. let inverse_diff = inverse_diff.unwrap();
  142. lock.add_diff(&inverse_diff).unwrap();
  143. }
  144. drop(lock);
  145. drop(overlay_lock);
  146. (overlay, new_height)
  147. }
  148. async fn verify_transaction_wasm(
  149. overlay: &BlockchainOverlayPtr,
  150. verifying_block_height: u32,
  151. block_target: u32,
  152. tx: &Transaction,
  153. tree: &mut MerkleTree,
  154. _verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
  155. verify_fee: bool,
  156. ) -> darkfi::Result<GasData> {
  157. let tx_hash = tx.hash();
  158. // Create a FeeData instance to hold the calculated fee data
  159. let mut gas_data = GasData::default();
  160. // Verify calls indexes integrity
  161. if verify_fee {
  162. dark_forest_leaf_vec_integrity_check(
  163. &tx.calls,
  164. Some(MIN_TX_CALLS + 1),
  165. Some(MAX_TX_CALLS),
  166. )?;
  167. } else {
  168. dark_forest_leaf_vec_integrity_check(&tx.calls, Some(MIN_TX_CALLS), Some(MAX_TX_CALLS))?;
  169. }
  170. // Index of the Fee-paying call
  171. let mut fee_call_idx = 0;
  172. if verify_fee {
  173. // Verify that there is a single money fee call in the transaction
  174. let mut found_fee = false;
  175. for (call_idx, call) in tx.calls.iter().enumerate() {
  176. if !call.data.is_money_fee() {
  177. continue
  178. }
  179. if found_fee {
  180. return Err(TxVerifyFailed::InvalidFee.into())
  181. }
  182. found_fee = true;
  183. fee_call_idx = call_idx;
  184. }
  185. if !found_fee {
  186. return Err(TxVerifyFailed::InvalidFee.into())
  187. }
  188. }
  189. // Write the transaction calls payload data
  190. let mut payload = vec![];
  191. tx.calls.encode_async(&mut payload).await?;
  192. // Define a buffer in case we want to use a different payload in a specific call
  193. let mut _call_payload = vec![];
  194. // Iterate over all calls to get the metadata
  195. for (idx, call) in tx.calls.iter().enumerate() {
  196. // Transaction must not contain a Pow reward call
  197. if call.data.is_money_pow_reward() {
  198. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  199. }
  200. // Check if its the fee call so we only pass its payload
  201. let (call_idx, call_payload) = if call.data.is_money_fee() {
  202. _call_payload = vec![];
  203. vec![call.clone()].encode_async(&mut _call_payload).await?;
  204. (0_u8, &_call_payload)
  205. } else {
  206. (idx as u8, &payload)
  207. };
  208. let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
  209. let mut runtime = Runtime::new(
  210. &wasm,
  211. overlay.clone(),
  212. call.data.contract_id,
  213. verifying_block_height,
  214. block_target,
  215. tx_hash,
  216. call_idx,
  217. )?;
  218. // After getting the metadata, we run the "exec" function with the same runtime
  219. // and the same payload. We keep the returned state update in a buffer, prefixed
  220. // by the call function ID, enforcing the state update function in the contract.
  221. let mut state_update = vec![call.data.data[0]];
  222. state_update.append(&mut runtime.exec(call_payload)?);
  223. // If that was successful, we apply the state update in the ephemeral overlay.
  224. runtime.apply(&state_update)?;
  225. // If this call is supposed to deploy a new contract, we have to instantiate
  226. // a new `Runtime` and run its deploy function.
  227. if call.data.is_deployment()
  228. /* DeployV1 */
  229. {
  230. // Deserialize the deployment parameters
  231. let deploy_params: DeployParamsV1 = deserialize_async(&call.data.data[1..]).await?;
  232. let deploy_cid = ContractId::derive_public(deploy_params.public_key);
  233. // Instantiate the new deployment runtime
  234. let mut deploy_runtime = Runtime::new(
  235. &deploy_params.wasm_bincode,
  236. overlay.clone(),
  237. deploy_cid,
  238. verifying_block_height,
  239. block_target,
  240. tx_hash,
  241. call_idx,
  242. )?;
  243. deploy_runtime.deploy(&deploy_params.ix)?;
  244. let deploy_gas_used = deploy_runtime.gas_used();
  245. gas_data.deployments += deploy_gas_used;
  246. }
  247. // At this point we're done with the call and move on to the next one.
  248. // Accumulate the WASM gas used.
  249. let wasm_gas_used = runtime.gas_used();
  250. // Append the used wasm gas
  251. gas_data.wasm += wasm_gas_used;
  252. }
  253. // Store the calculated total gas used to avoid recalculating it for subsequent uses
  254. let total_gas_used = gas_data.total_gas_used();
  255. if verify_fee {
  256. // Deserialize the fee call to find the paid fee
  257. let fee: u64 = match deserialize_async(&tx.calls[fee_call_idx].data.data[1..9]).await {
  258. Ok(v) => v,
  259. Err(_) => return Err(TxVerifyFailed::InvalidFee.into()),
  260. };
  261. // Compute the required fee for this transaction
  262. let required_fee = compute_fee(&total_gas_used);
  263. // Check that enough fee has been paid for the used gas in this transaction
  264. if required_fee > fee {
  265. return Err(TxVerifyFailed::InsufficientFee.into())
  266. }
  267. // Store paid fee
  268. gas_data.paid = fee;
  269. }
  270. // Append hash to merkle tree
  271. append_tx_to_merkle_tree(tree, tx);
  272. Ok(gas_data)
  273. }
  274. async fn verify_transaction_zkps(
  275. overlay: &BlockchainOverlayPtr,
  276. verifying_block_height: u32,
  277. block_target: u32,
  278. tx: &Transaction,
  279. tree: &mut MerkleTree,
  280. verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
  281. verify_fee: bool,
  282. ) -> darkfi::Result<GasData> {
  283. let tx_hash = tx.hash();
  284. // Create a FeeData instance to hold the calculated fee data
  285. let mut gas_data = GasData::default();
  286. // Verify calls indexes integrity
  287. if verify_fee {
  288. dark_forest_leaf_vec_integrity_check(
  289. &tx.calls,
  290. Some(MIN_TX_CALLS + 1),
  291. Some(MAX_TX_CALLS),
  292. )?;
  293. } else {
  294. dark_forest_leaf_vec_integrity_check(&tx.calls, Some(MIN_TX_CALLS), Some(MAX_TX_CALLS))?;
  295. }
  296. // Table of public inputs used for ZK proof verification
  297. let mut zkp_table = vec![];
  298. // Table of public keys used for signature verification
  299. let mut sig_table = vec![];
  300. // Index of the Fee-paying call
  301. let mut fee_call_idx = 0;
  302. if verify_fee {
  303. // Verify that there is a single money fee call in the transaction
  304. let mut found_fee = false;
  305. for (call_idx, call) in tx.calls.iter().enumerate() {
  306. if !call.data.is_money_fee() {
  307. continue
  308. }
  309. if found_fee {
  310. return Err(TxVerifyFailed::InvalidFee.into())
  311. }
  312. found_fee = true;
  313. fee_call_idx = call_idx;
  314. }
  315. if !found_fee {
  316. return Err(TxVerifyFailed::InvalidFee.into())
  317. }
  318. }
  319. // Write the transaction calls payload data
  320. let mut payload = vec![];
  321. tx.calls.encode_async(&mut payload).await?;
  322. // Define a buffer in case we want to use a different payload in a specific call
  323. let mut _call_payload = vec![];
  324. // We'll also take note of all the circuits in a Vec so we can calculate their verification cost.
  325. let mut circuits_to_verify = vec![];
  326. // Iterate over all calls to get the metadata
  327. for (idx, call) in tx.calls.iter().enumerate() {
  328. // Transaction must not contain a Pow reward call
  329. if call.data.is_money_pow_reward() {
  330. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  331. }
  332. // Check if its the fee call so we only pass its payload
  333. let (call_idx, call_payload) = if call.data.is_money_fee() {
  334. _call_payload = vec![];
  335. vec![call.clone()].encode_async(&mut _call_payload).await?;
  336. (0_u8, &_call_payload)
  337. } else {
  338. (idx as u8, &payload)
  339. };
  340. let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
  341. let mut runtime = Runtime::new(
  342. &wasm,
  343. overlay.clone(),
  344. call.data.contract_id,
  345. verifying_block_height,
  346. block_target,
  347. tx_hash,
  348. call_idx,
  349. )?;
  350. let metadata = runtime.metadata(call_payload)?;
  351. // Decode the metadata retrieved from the execution
  352. let mut decoder = Cursor::new(&metadata);
  353. // The tuple is (zkas_ns, public_inputs)
  354. let zkp_pub: Vec<(String, Vec<pallas::Base>)> =
  355. AsyncDecodable::decode_async(&mut decoder).await?;
  356. let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
  357. if decoder.position() != metadata.len() as u64 {
  358. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  359. }
  360. // Here we'll look up verifying keys and insert them into the per-contract map.
  361. // TODO: This vk map can potentially use a lot of RAM. Perhaps load keys on-demand at verification time?
  362. for (zkas_ns, _) in &zkp_pub {
  363. let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();
  364. // TODO: This will be a problem in case of ::deploy, unless we force a different
  365. // namespace and disable updating existing circuit. Might be a smart idea to do
  366. // so in order to have to care less about being able to verify historical txs.
  367. if inner_vk_map.contains_key(zkas_ns.as_str()) {
  368. continue
  369. }
  370. let (zkbin, vk) =
  371. overlay.lock().unwrap().contracts.get_zkas(&call.data.contract_id, zkas_ns)?;
  372. inner_vk_map.insert(zkas_ns.to_string(), vk);
  373. circuits_to_verify.push(zkbin);
  374. }
  375. zkp_table.push(zkp_pub);
  376. sig_table.push(sig_pub);
  377. // At this point we're done with the call and move on to the next one.
  378. // Accumulate the WASM gas used.
  379. let wasm_gas_used = runtime.gas_used();
  380. // Append the used wasm gas
  381. gas_data.wasm += wasm_gas_used;
  382. }
  383. // The ZK circuit fee is calculated using a function in validator/fees.rs
  384. for zkbin in circuits_to_verify.iter() {
  385. let zk_circuit_gas_used = circuit_gas_use(zkbin);
  386. // Append the used zk circuit gas
  387. gas_data.zk_circuits += zk_circuit_gas_used;
  388. }
  389. // Store the calculated total gas used to avoid recalculating it for subsequent uses
  390. let total_gas_used = gas_data.total_gas_used();
  391. if verify_fee {
  392. // Deserialize the fee call to find the paid fee
  393. let fee: u64 = match deserialize_async(&tx.calls[fee_call_idx].data.data[1..9]).await {
  394. Ok(v) => v,
  395. Err(_) => return Err(TxVerifyFailed::InvalidFee.into()),
  396. };
  397. // Compute the required fee for this transaction
  398. let required_fee = compute_fee(&total_gas_used);
  399. // Check that enough fee has been paid for the used gas in this transaction
  400. if required_fee > fee {
  401. return Err(TxVerifyFailed::InsufficientFee.into())
  402. }
  403. // Store paid fee
  404. gas_data.paid = fee;
  405. }
  406. if tx.verify_zkps(verifying_keys, zkp_table).await.is_err() {
  407. return Err(TxVerifyFailed::InvalidZkProof.into())
  408. }
  409. // Append hash to merkle tree
  410. append_tx_to_merkle_tree(tree, tx);
  411. Ok(gas_data)
  412. }
  413. async fn verify_transaction_signatures(
  414. overlay: &BlockchainOverlayPtr,
  415. verifying_block_height: u32,
  416. block_target: u32,
  417. tx: &Transaction,
  418. tree: &mut MerkleTree,
  419. _verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
  420. verify_fee: bool,
  421. ) -> darkfi::Result<GasData> {
  422. let tx_hash = tx.hash();
  423. // Create a FeeData instance to hold the calculated fee data
  424. let mut gas_data = GasData::default();
  425. // Verify calls indexes integrity
  426. if verify_fee {
  427. dark_forest_leaf_vec_integrity_check(
  428. &tx.calls,
  429. Some(MIN_TX_CALLS + 1),
  430. Some(MAX_TX_CALLS),
  431. )?;
  432. } else {
  433. dark_forest_leaf_vec_integrity_check(&tx.calls, Some(MIN_TX_CALLS), Some(MAX_TX_CALLS))?;
  434. }
  435. // Table of public inputs used for ZK proof verification
  436. let mut zkp_table = vec![];
  437. // Table of public keys used for signature verification
  438. let mut sig_table = vec![];
  439. // Index of the Fee-paying call
  440. let mut fee_call_idx = 0;
  441. if verify_fee {
  442. // Verify that there is a single money fee call in the transaction
  443. let mut found_fee = false;
  444. for (call_idx, call) in tx.calls.iter().enumerate() {
  445. if !call.data.is_money_fee() {
  446. continue
  447. }
  448. if found_fee {
  449. return Err(TxVerifyFailed::InvalidFee.into())
  450. }
  451. found_fee = true;
  452. fee_call_idx = call_idx;
  453. }
  454. if !found_fee {
  455. return Err(TxVerifyFailed::InvalidFee.into())
  456. }
  457. }
  458. // Write the transaction calls payload data
  459. let mut payload = vec![];
  460. tx.calls.encode_async(&mut payload).await?;
  461. // Define a buffer in case we want to use a different payload in a specific call
  462. let mut _call_payload = vec![];
  463. // Iterate over all calls to get the metadata
  464. for (idx, call) in tx.calls.iter().enumerate() {
  465. // Transaction must not contain a Pow reward call
  466. if call.data.is_money_pow_reward() {
  467. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  468. }
  469. // Check if its the fee call so we only pass its payload
  470. let (call_idx, call_payload) = if call.data.is_money_fee() {
  471. _call_payload = vec![];
  472. vec![call.clone()].encode_async(&mut _call_payload).await?;
  473. (0_u8, &_call_payload)
  474. } else {
  475. (idx as u8, &payload)
  476. };
  477. let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
  478. let mut runtime = Runtime::new(
  479. &wasm,
  480. overlay.clone(),
  481. call.data.contract_id,
  482. verifying_block_height,
  483. block_target,
  484. tx_hash,
  485. call_idx,
  486. )?;
  487. let metadata = runtime.metadata(call_payload)?;
  488. // Decode the metadata retrieved from the execution
  489. let mut decoder = Cursor::new(&metadata);
  490. // The tuple is (zkas_ns, public_inputs)
  491. let zkp_pub: Vec<(String, Vec<pallas::Base>)> =
  492. AsyncDecodable::decode_async(&mut decoder).await?;
  493. let sig_pub: Vec<PublicKey> = AsyncDecodable::decode_async(&mut decoder).await?;
  494. if decoder.position() != metadata.len() as u64 {
  495. return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
  496. }
  497. zkp_table.push(zkp_pub);
  498. sig_table.push(sig_pub);
  499. // At this point we're done with the call and move on to the next one.
  500. // Accumulate the WASM gas used.
  501. let wasm_gas_used = runtime.gas_used();
  502. // Append the used wasm gas
  503. gas_data.wasm += wasm_gas_used;
  504. }
  505. // The signature fee is tx_size + fixed_sig_fee * n_signatures
  506. gas_data.signatures = (PALLAS_SCHNORR_SIGNATURE_FEE * tx.signatures.len() as u64) +
  507. serialize_async(tx).await.len() as u64;
  508. // Store the calculated total gas used to avoid recalculating it for subsequent uses
  509. let total_gas_used = gas_data.total_gas_used();
  510. if verify_fee {
  511. // Deserialize the fee call to find the paid fee
  512. let fee: u64 = match deserialize_async(&tx.calls[fee_call_idx].data.data[1..9]).await {
  513. Ok(v) => v,
  514. Err(_) => return Err(TxVerifyFailed::InvalidFee.into()),
  515. };
  516. // Compute the required fee for this transaction
  517. let required_fee = compute_fee(&total_gas_used);
  518. // Check that enough fee has been paid for the used gas in this transaction
  519. if required_fee > fee {
  520. return Err(TxVerifyFailed::InsufficientFee.into())
  521. }
  522. // Store paid fee
  523. gas_data.paid = fee;
  524. }
  525. // When we're done looping and executing over the tx's contract calls and
  526. // (optionally) made sure that enough fee was paid, we now move on with
  527. // verification. First we verify the transaction signatures and then we
  528. // verify any accompanying ZK proofs.
  529. if sig_table.len() != tx.signatures.len() {
  530. return Err(TxVerifyFailed::MissingSignatures.into())
  531. }
  532. if tx.verify_sigs(sig_table).is_err() {
  533. return Err(TxVerifyFailed::InvalidSignature.into())
  534. }
  535. // Append hash to merkle tree
  536. append_tx_to_merkle_tree(tree, tx);
  537. Ok(gas_data)
  538. }