main.rs 21 KB

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