Forráskód Böngészése

validator: Use saturating arithmetic

x 6 hónapja
szülő
commit
06cf1be98d

+ 24 - 52
src/validator/consensus.rs

@@ -233,6 +233,19 @@ impl Consensus {
         Ok(Some(index))
     }
 
+    /// Auxiliary function to find the index of a fork containing the provided
+    /// header hash in its proposals.
+    fn find_fork_by_header(&self, fork_header: &HeaderHash) -> Option<usize> {
+        for (index, fork) in self.forks.iter().enumerate() {
+            for p in fork.proposals.iter().rev() {
+                if p == fork_header {
+                    return Some(index)
+                }
+            }
+        }
+        None
+    }
+
     /// Auxiliary function to retrieve the fork header hash of provided height.
     /// The fork is identified by the provided header hash.
     pub async fn get_fork_header_hash(
@@ -241,19 +254,7 @@ impl Consensus {
         fork_header: &HeaderHash,
     ) -> Result<Option<HeaderHash>> {
         // Find the fork containing the provided header
-        let mut found = None;
-        'outer: for (index, fork) in self.forks.iter().enumerate() {
-            for p in fork.proposals.iter().rev() {
-                if p == fork_header {
-                    found = Some(index);
-                    break 'outer
-                }
-            }
-        }
-        if found.is_none() {
-            return Ok(None)
-        }
-        let index = found.unwrap();
+        let Some(index) = self.find_fork_by_header(fork_header) else { return Ok(None) };
 
         // Grab header if it exists
         let header =
@@ -271,16 +272,7 @@ impl Consensus {
         fork_header: &HeaderHash,
     ) -> Result<Vec<Header>> {
         // Find the fork containing the provided header
-        let mut found = None;
-        'outer: for (index, fork) in self.forks.iter().enumerate() {
-            for p in fork.proposals.iter().rev() {
-                if p == fork_header {
-                    found = Some(index);
-                    break 'outer
-                }
-            }
-        }
-        let Some(index) = found else { return Ok(vec![]) };
+        let Some(index) = self.find_fork_by_header(fork_header) else { return Ok(vec![]) };
 
         // Grab headers
         let headers = self.forks[index].overlay.lock().unwrap().get_headers_by_hash(headers)?;
@@ -297,16 +289,7 @@ impl Consensus {
         fork_header: &HeaderHash,
     ) -> Result<Vec<Proposal>> {
         // Find the fork containing the provided header
-        let mut found = None;
-        'outer: for (index, fork) in self.forks.iter().enumerate() {
-            for p in fork.proposals.iter().rev() {
-                if p == fork_header {
-                    found = Some(index);
-                    break 'outer
-                }
-            }
-        }
-        let Some(index) = found else { return Ok(vec![]) };
+        let Some(index) = self.find_fork_by_header(fork_header) else { return Ok(vec![]) };
 
         // Grab proposals
         let blocks = self.forks[index].overlay.lock().unwrap().get_blocks_by_hash(headers)?;
@@ -334,19 +317,8 @@ impl Consensus {
         // Grab fork index to use
         let index = match fork_tip {
             Some(fork_tip) => {
-                let mut found = None;
-                'outer: for (index, fork) in self.forks.iter().enumerate() {
-                    for p in fork.proposals.iter().rev() {
-                        if p == &fork_tip {
-                            found = Some(index);
-                            break 'outer
-                        }
-                    }
-                }
-                if found.is_none() {
-                    return Ok(proposals)
-                }
-                found.unwrap()
+                let Some(found) = self.find_fork_by_header(&fork_tip) else { return Ok(proposals) };
+                found
             }
             None => best_fork_index(&self.forks)?,
         };
@@ -360,7 +332,7 @@ impl Consensus {
 
         // Check tip is not far behind
         let last_block_height = self.forks[index].overlay.lock().unwrap().last()?.0;
-        if last_block_height - existing_tips[0].header.height >= limit {
+        if last_block_height.saturating_sub(existing_tips[0].header.height) >= limit {
             return Ok(proposals)
         }
 
@@ -758,8 +730,8 @@ impl Fork {
         let mut tree = MerkleTree::new(1);
 
         // Total gas accumulators
-        let mut total_gas_used = 0;
-        let mut total_gas_paid = 0;
+        let mut total_gas_used = 0_u64;
+        let mut total_gas_paid = 0_u64;
 
         // Map of ZK proof verifying keys for the current transaction batch
         let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
@@ -817,7 +789,7 @@ impl Fork {
             let tx_gas_used = gas_data.total_gas_used();
 
             // Calculate current accumulated gas usage
-            let accumulated_gas_usage = total_gas_used + tx_gas_used;
+            let accumulated_gas_usage = total_gas_used.saturating_add(tx_gas_used);
 
             // Check gas limit - if accumulated gas used exceeds it, break out of loop
             if accumulated_gas_usage > BLOCK_GAS_LIMIT {
@@ -830,8 +802,8 @@ impl Fork {
             }
 
             // Update accumulated total gas
-            total_gas_used += tx_gas_used;
-            total_gas_paid += gas_data.paid;
+            total_gas_used = total_gas_used.saturating_add(tx_gas_used);
+            total_gas_paid = total_gas_paid.saturating_add(gas_data.paid);
 
             // Push the tx hash into the unproposed transactions vector
             unproposed_txs.push(unproposed_tx);

+ 11 - 6
src/validator/fees.rs

@@ -30,10 +30,10 @@ pub fn circuit_gas_use(zkbin: &ZkBinary) -> u64 {
     let mut accumulator: u64 = 0;
 
     // Constants each with a cost of 10
-    accumulator += 10 * zkbin.constants.len() as u64;
+    accumulator = accumulator.saturating_add(10u64.saturating_mul(zkbin.constants.len() as u64));
 
     // Literals each with a cost of 10 (for now there's only 1 type of literal)
-    accumulator += 10 * zkbin.literals.len() as u64;
+    accumulator = accumulator.saturating_add(10u64.saturating_mul(zkbin.literals.len() as u64));
 
     // Witnesses have cost by type
     for witness in &zkbin.witnesses {
@@ -55,7 +55,7 @@ pub fn circuit_gas_use(zkbin: &ZkBinary) -> u64 {
             VarType::Any => 10,
         };
 
-        accumulator += cost;
+        accumulator = accumulator.saturating_add(cost);
     }
 
     // Opcodes depending on how heavy they are
@@ -69,7 +69,9 @@ pub fn circuit_gas_use(zkbin: &ZkBinary) -> u64 {
             Opcode::EcMulVarBase => 30,
             Opcode::EcGetX => 5,
             Opcode::EcGetY => 5,
-            Opcode::PoseidonHash => 20 + 10 * opcode.1.len() as u64,
+            Opcode::PoseidonHash => {
+                20u64.saturating_add(10u64.saturating_mul(opcode.1.len() as u64))
+            }
             Opcode::MerkleRoot => 10 * MERKLE_DEPTH_ORCHARD as u64,
             Opcode::SparseMerkleRoot => 10 * SPARSE_MERKLE_DEPTH as u64,
             Opcode::BaseAdd => 15,
@@ -88,7 +90,7 @@ pub fn circuit_gas_use(zkbin: &ZkBinary) -> u64 {
             Opcode::DebugPrint => 100,
         };
 
-        accumulator += cost;
+        accumulator = accumulator.saturating_add(cost);
     }
 
     accumulator
@@ -115,7 +117,10 @@ pub struct GasData {
 impl GasData {
     /// Calculates the total gas used by summing all individual gas usage fields.
     pub fn total_gas_used(&self) -> u64 {
-        self.wasm + self.zk_circuits + self.signatures + self.deployments
+        self.wasm
+            .saturating_add(self.zk_circuits)
+            .saturating_add(self.signatures)
+            .saturating_add(self.deployments)
     }
 }
 

+ 2 - 8
src/validator/mod.rs

@@ -329,10 +329,7 @@ impl Validator {
         info!(target: "validator::confirmation", "Performing confirmation check");
 
         // Grab best fork index that can be confirmed
-        let confirmed_fork = match self.consensus.confirmation().await {
-            Ok(f) => f,
-            Err(e) => return Err(e),
-        };
+        let confirmed_fork = self.consensus.confirmation().await?;
         if confirmed_fork.is_none() {
             info!(target: "validator::confirmation", "No proposals can be confirmed");
             return Ok(vec![])
@@ -642,10 +639,7 @@ impl Validator {
         let lock = overlay.lock().unwrap();
         let mut overlay = lock.overlay.lock().unwrap();
 
-        let gas_values = match verify_result {
-            Ok(v) => v,
-            Err(e) => return Err(e),
-        };
+        let gas_values = verify_result?;
 
         if !write {
             debug!(target: "validator::add_transactions", "Skipping apply of state updates because write=false");

+ 6 - 3
src/validator/pow.rs

@@ -19,7 +19,7 @@
 use std::{
     sync::{
         atomic::{AtomicBool, AtomicU32, Ordering},
-        Arc,
+        Arc, LazyLock,
     },
     thread,
     time::Instant,
@@ -79,6 +79,9 @@ pub const RANDOMX_KEY_CHANGING_HEIGHT: u32 = 2048;
 /// RandomX VM key change delay
 pub const RANDOMX_KEY_CHANGE_DELAY: u32 = 64;
 
+/// Max 32 bytes integer, cached to avoid repeated allocation
+static MAX_32_BYTES: LazyLock<BigUint> = LazyLock::new(|| BigUint::from_bytes_le(&[0xFF; 32]));
+
 /// This struct represents the information required by the PoW algorithm
 #[derive(Clone)]
 pub struct PoWModule {
@@ -232,13 +235,13 @@ impl PoWModule {
 
     /// Compute the next mine target.
     pub fn next_mine_target(&self) -> Result<BigUint> {
-        Ok(BigUint::from_bytes_le(&[0xFF; 32]) / &self.next_difficulty()?)
+        Ok(&*MAX_32_BYTES / &self.next_difficulty()?)
     }
 
     /// Compute the next mine target and difficulty.
     pub fn next_mine_target_and_difficulty(&self) -> Result<(BigUint, BigUint)> {
         let difficulty = self.next_difficulty()?;
-        let mine_target = BigUint::from_bytes_le(&[0xFF; 32]) / &difficulty;
+        let mine_target = &*MAX_32_BYTES / &difficulty;
         Ok((mine_target, difficulty))
     }
 

+ 12 - 9
src/validator/utils.rs

@@ -16,6 +16,8 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+use std::sync::LazyLock;
+
 use darkfi_sdk::{
     crypto::{DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID},
     tx::TransactionHash,
@@ -34,6 +36,10 @@ use crate::{
     Error, Result,
 };
 
+/// Max 32 bytes integer, used in rank calculations.
+/// Cached to avoid repeated allocation.
+static MAX_32_BYTES: LazyLock<BigUint> = LazyLock::new(|| BigUint::from_bytes_le(&[0xFF; 32]));
+
 /// Deploy DarkFi native wasm contracts to provided blockchain overlay.
 ///
 /// If overlay already contains the contracts, it will just open the
@@ -131,15 +137,12 @@ pub fn header_rank(module: &mut PoWModule, header: &Header) -> Result<(BigUint,
     // Verify hash is less than the expected mine target
     let out_hash = module.verify_block_target(header, &target)?;
 
-    // Grab the max 32 bytes int
-    let max = BigUint::from_bytes_le(&[0xFF; 32]);
-
     // Compute the squared mining target distance
-    let target_distance = &max - target;
+    let target_distance = &*MAX_32_BYTES - target;
     let target_distance_sq = &target_distance * &target_distance;
 
     // Compute the output hash distance
-    let hash_distance = max - out_hash;
+    let hash_distance = &*MAX_32_BYTES - out_hash;
     let hash_distance_sq = &hash_distance * &hash_distance;
 
     Ok((difficulty, target_distance_sq, hash_distance_sq))
@@ -157,10 +160,10 @@ pub fn block_rank(block: &BlockInfo, target: &BigUint) -> Result<(BigUint, BigUi
     }
 
     // Grab the max 32 bytes int
-    let max = BigUint::from_bytes_le(&[0xFF; 32]);
+    let max = &*MAX_32_BYTES;
 
     // Compute the squared mining target distance
-    let target_distance = &max - target;
+    let target_distance = max - target;
     let target_distance_sq = &target_distance * &target_distance;
 
     // Setup RandomX verifier
@@ -214,12 +217,12 @@ pub fn find_extended_fork_index(forks: &[Fork], proposal: &Proposal) -> Result<(
         // Traverse fork proposals sequence in reverse
         for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
             // Check we haven't already seen that proposal
-            if &proposal_hash == p_hash {
+            if proposal_hash == *p_hash {
                 return Err(Error::ProposalAlreadyExists)
             }
 
             // Check if proposal extends this fork
-            if &proposal.block.header.previous == p_hash {
+            if proposal.block.header.previous == *p_hash {
                 (fork_index, proposal_index) = (Some(f_index), Some(p_index));
             }
         }

+ 11 - 10
src/validator/verification.rs

@@ -791,7 +791,7 @@ pub async fn verify_transaction(
 
             let deploy_gas_used = deploy_runtime.gas_used();
             debug!(target: "validator::verification::verify_transaction", "The gas used for deployment call {call:?} of transaction {tx_hash}: {deploy_gas_used}");
-            gas_data.deployments += deploy_gas_used;
+            gas_data.deployments = gas_data.deployments.saturating_add(deploy_gas_used);
         }
 
         // At this point we're done with the call and move on to the next one.
@@ -800,12 +800,13 @@ pub async fn verify_transaction(
         debug!(target: "validator::verification::verify_transaction", "The gas used for WASM call {call:?} of transaction {tx_hash}: {wasm_gas_used}");
 
         // Append the used wasm gas
-        gas_data.wasm += wasm_gas_used;
+        gas_data.wasm = gas_data.wasm.saturating_add(wasm_gas_used);
     }
 
     // The signature fee is tx_size + fixed_sig_fee * n_signatures
-    gas_data.signatures = (PALLAS_SCHNORR_SIGNATURE_FEE * tx.signatures.len() as u64) +
-        serialize_async(tx).await.len() as u64;
+    gas_data.signatures = PALLAS_SCHNORR_SIGNATURE_FEE
+        .saturating_mul(tx.signatures.len() as u64)
+        .saturating_add(serialize_async(tx).await.len() as u64);
     debug!(target: "validator::verification::verify_transaction", "The gas used for signature of transaction {tx_hash}: {}", gas_data.signatures);
 
     // The ZK circuit fee is calculated using a function in validator/fees.rs
@@ -814,7 +815,7 @@ pub async fn verify_transaction(
         debug!(target: "validator::verification::verify_transaction", "The gas used for ZK circuit in namespace {} of transaction {tx_hash}: {zk_circuit_gas_used}", zkbin.namespace);
 
         // Append the used zk circuit gas
-        gas_data.zk_circuits += zk_circuit_gas_used;
+        gas_data.zk_circuits = gas_data.zk_circuits.saturating_add(zk_circuit_gas_used);
     }
 
     // Store the calculated total gas used to avoid recalculating it for subsequent uses
@@ -992,8 +993,8 @@ pub async fn verify_transactions(
     let mut erroneous_txs = vec![];
 
     // Total gas accumulators
-    let mut total_gas_used = 0;
-    let mut total_gas_paid = 0;
+    let mut total_gas_used = 0_u64;
+    let mut total_gas_paid = 0_u64;
 
     // Map of ZK proof verifying keys for the current transaction batch
     let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
@@ -1032,7 +1033,7 @@ pub async fn verify_transactions(
         let tx_gas_used = gas_data.total_gas_used();
 
         // Calculate current accumulated gas usage
-        let accumulated_gas_usage = total_gas_used + tx_gas_used;
+        let accumulated_gas_usage = total_gas_used.saturating_add(tx_gas_used);
 
         // Check gas limit - if accumulated gas used exceeds it, break out of loop
         if accumulated_gas_usage > BLOCK_GAS_LIMIT {
@@ -1047,8 +1048,8 @@ pub async fn verify_transactions(
         }
 
         // Update accumulated total gas
-        total_gas_used += tx_gas_used;
-        total_gas_paid += gas_data.paid;
+        total_gas_used = total_gas_used.saturating_add(tx_gas_used);
+        total_gas_paid = total_gas_paid.saturating_add(gas_data.paid);
     }
 
     if !erroneous_txs.is_empty() {