Просмотр исходного кода

explorerd/service: enhance put_block and calculate_tx_gas_data to apply contract state changes, cleanup

This commit updates the `calculate_tx_gas_data` function to apply state changes during contract execution, enabling contract data availability for subsequent contract calls during block sync. Additionally, `put_block` was modified to apply PoW reward transactions to the WASM runtime, ensuring their effects are visible to transaction runtimes used for fee calculations where applicable.

Minor cleanup was performed.
kalm 1 год назад
Родитель
Сommit
f03f5977b6

+ 51 - 11
bin/explorer/explorerd/src/service/blocks.rs

@@ -22,13 +22,18 @@ use tinyjson::JsonValue;
 
 use darkfi::{
     blockchain::{
-        BlockInfo, BlockchainOverlay, HeaderHash, SLED_BLOCK_DIFFICULTY_TREE,
-        SLED_BLOCK_ORDER_TREE, SLED_BLOCK_TREE,
+        block_store::append_tx_to_merkle_tree, BlockInfo, BlockchainOverlay, HeaderHash,
+        SLED_BLOCK_DIFFICULTY_TREE, SLED_BLOCK_ORDER_TREE, SLED_BLOCK_TREE,
     },
+    runtime::vm_runtime::Runtime,
     util::time::Timestamp,
     Error, Result,
 };
-use darkfi_sdk::{crypto::schnorr::Signature, tx::TransactionHash};
+use darkfi_sdk::{
+    crypto::{schnorr::Signature, MerkleTree},
+    tx::TransactionHash,
+};
+use darkfi_serial::AsyncEncodable;
 
 use crate::{error::ExplorerdError, ExplorerService};
 
@@ -114,24 +119,59 @@ impl ExplorerService {
     ///
     /// This function processes each transaction in the block, calculating and updating the
     /// latest [`GasMetrics`] for non-genesis blocks and for transactions that are not
-    /// PoW rewards. After processing all transactions, the block is permanently persisted to
+    /// PoW rewards. PoW reward transactions update the contract runtime state as required.
+    /// After processing all transactions, the block is permanently persisted to
     /// the explorer database.
     pub async fn put_block(&self, block: &BlockInfo) -> Result<()> {
         let blockchain_overlay = BlockchainOverlay::new(&self.db.blockchain)?;
+        let mut tree = MerkleTree::new(1);
 
-        // Initialize collections to hold gas data and transactions that have gas data
+        // Initialize collections that store gas related data
         let mut tx_gas_data = Vec::with_capacity(block.txs.len());
         let mut txs_hashes_with_gas_data = Vec::with_capacity(block.txs.len());
 
-        // Calculate gas data for non-PoW reward transactions and non-genesis blocks
-        for (i, tx) in block.txs.iter().enumerate() {
-            if !tx.is_pow_reward() && block.header.height != 0 {
-                tx_gas_data.insert(i, self.calculate_tx_gas_data(tx, false).await?);
-                txs_hashes_with_gas_data.insert(i, tx.hash());
+        // Apply transactions to WASM runtime for PoW rewards or calculate fees
+        for (idx, tx) in block.txs.iter().enumerate() {
+            // Apply PoW reward transactions to update contract runtime state
+            if tx.is_pow_reward() {
+                let mut payload = vec![];
+                tx.calls.encode_async(&mut payload).await?;
+
+                let call = &tx.calls[0];
+                let wasm =
+                    blockchain_overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
+                let block_target = self.db.blockchain.blocks.get_last()?.0 + 1;
+
+                let mut runtime = Runtime::new(
+                    &wasm,
+                    blockchain_overlay.clone(),
+                    call.data.contract_id,
+                    block.header.height,
+                    block_target,
+                    tx.hash(),
+                    0,
+                )?;
+
+                // Execute and apply state changes
+                let mut state_update = vec![call.data.data[0]];
+                state_update.append(&mut runtime.exec(&payload)?);
+                runtime.apply(&state_update)?;
+
+                // Append transaction hash to the Merkle tree
+                append_tx_to_merkle_tree(&mut tree, tx);
+
+                // No gas calculations needed for PoW rewards, proceed to next transaction
+                continue;
+            }
+
+            // Calculate gas data for non-PoW reward transactions and non-genesis blocks
+            if block.header.height != 0 {
+                tx_gas_data.insert(idx, self.calculate_tx_gas_data(tx, true).await?);
+                txs_hashes_with_gas_data.insert(idx, tx.hash());
             }
         }
 
-        // If the block contains transaction gas data, insert the gas metrics into the metrics store
+        // Insert gas metrics into the store
         if !tx_gas_data.is_empty() {
             self.db.metrics_store.insert_gas_metrics(
                 block.header.height,

+ 14 - 8
bin/explorer/explorerd/src/service/transactions.rs

@@ -265,6 +265,10 @@ impl ExplorerService {
         // Index of the Fee-paying call
         let fee_call_idx = 0;
 
+        // Write the transaction calls payload data
+        let mut payload = vec![];
+        tx.calls.encode_async(&mut payload).await?;
+
         // Map of ZK proof verifying keys for the transaction
         let mut verifying_keys: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
         for call in &tx.calls {
@@ -278,18 +282,13 @@ impl ExplorerService {
 
         // Iterate over all calls to get the metadata
         for (idx, call) in tx.calls.iter().enumerate() {
-            // Transaction must not contain a Money::PoWReward(0x02) call
+            // Transaction must not contain a Pow reward call
             if call.data.is_money_pow_reward() {
-                error!(target: "block_explorer::calculate_tx_gas_data", "Reward transaction detected");
+                error!(target: "explorerd::calculate_tx_gas_data", "Reward transaction detected");
                 return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
             }
 
-            // Write the actual payload data
-            let mut payload = vec![];
-            tx.calls.encode_async(&mut payload).await?;
-
             let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
-
             let mut runtime = Runtime::new(
                 &wasm,
                 overlay.clone(),
@@ -352,7 +351,14 @@ impl ExplorerService {
             zkp_table.push(zkp_pub);
             sig_table.push(sig_pub);
 
-            // Contracts are not included within blocks. They need to be deployed off-chain so that they can be accessed and utilized for fee data computation
+            // Execute the contract
+            let mut state_update = vec![call.data.data[0]];
+            state_update.append(&mut runtime.exec(&payload)?);
+
+            // Apply contract state update
+            runtime.apply(&state_update)?;
+
+            // Contracts are not included within blocks. They need to be deployed so that they can be accessed and utilized for fee data computation
             if call.data.is_deployment()
             /* DeployV1 */
             {