main.rs 22 KB

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