Browse Source

validator/randomx_factory: simplified factory api to not use locks

skoupidi 6 months ago
parent
commit
e36a6d635b

+ 2 - 2
bin/darkfid/src/task/unknown_proposal.rs

@@ -533,7 +533,7 @@ async fn retrieve_peer_headers_sequence_ranking(
 
             // Verify header hash and calculate its rank
             let (next_difficulty, target_distance_sq, hash_distance_sq) =
-                match header_rank(&module, peer_header) {
+                match header_rank(&mut module, peer_header) {
                     Ok(tuple) => tuple,
                     Err(PoWInvalidOutHash) => return Err(PoWInvalidOutHash),
                     Err(e) => {
@@ -569,7 +569,7 @@ async fn retrieve_peer_headers_sequence_ranking(
 
     // Verify trigger proposal header hash and calculate its rank
     let (_, target_distance_sq, hash_distance_sq) =
-        match header_rank(&module, &proposal.block.header) {
+        match header_rank(&mut module, &proposal.block.header) {
             Ok(tuple) => tuple,
             Err(PoWInvalidOutHash) => return Err(PoWInvalidOutHash),
             Err(e) => return Err(DatabaseError(format!("Computing header rank failed: {e}"))),

+ 1 - 1
bin/darkfid/src/tests/harness.rs

@@ -260,7 +260,7 @@ impl Harness {
         verify_block(
             &fork.overlay,
             &fork.diffs,
-            &fork.module,
+            &mut fork.module,
             &block,
             &previous,
             self.alice.validator.read().await.verify_fees,

+ 1 - 1
bin/darkfid/src/tests/mod.rs

@@ -79,7 +79,7 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     let alice = th.alice.validator.read().await;
     fork = Fork::new(alice.consensus.blockchain.clone(), alice.consensus.module.clone()).await?;
     // Append block3 to fork and generate the next one
-    verify_block(&fork.overlay, &fork.diffs, &fork.module, &block3, &block2, alice.verify_fees)
+    verify_block(&fork.overlay, &fork.diffs, &mut fork.module, &block3, &block2, alice.verify_fees)
         .await?;
     drop(alice);
     let block6 = th.generate_next_block(&mut fork).await?;

+ 5 - 2
src/validator/mod.rs

@@ -531,7 +531,9 @@ impl Validator {
         // Validate and insert each block
         for block in blocks {
             // Verify block
-            match verify_block(&overlay, &diffs, &module, block, previous, self.verify_fees).await {
+            match verify_block(&overlay, &diffs, &mut module, block, previous, self.verify_fees)
+                .await
+            {
                 Ok(()) => { /* Do nothing */ }
                 // Skip already existing block
                 Err(Error::BlockAlreadyExists(_)) => {
@@ -761,7 +763,8 @@ impl Validator {
 
             // Verify block
             if let Err(e) =
-                verify_block(&overlay, &diffs, &module, &block, &previous, self.verify_fees).await
+                verify_block(&overlay, &diffs, &mut module, &block, &previous, self.verify_fees)
+                    .await
             {
                 error!(target: "validator::validate_blockchain", "Erroneous block found in set: {e}");
                 return Err(Error::BlockIsInvalid(block.hash().as_string()))

+ 4 - 4
src/validator/pow.rs

@@ -282,7 +282,7 @@ impl PoWModule {
     }
 
     /// Verify provided block timestamp and hash.
-    pub fn verify_current_block(&self, header: &Header) -> Result<()> {
+    pub fn verify_current_block(&mut self, header: &Header) -> Result<()> {
         // First we verify the block's timestamp
         if !self.verify_current_timestamp(header.timestamp)? {
             return Err(Error::PoWInvalidTimestamp)
@@ -293,7 +293,7 @@ impl PoWModule {
     }
 
     /// Verify provided block hash is less than provided mine target.
-    pub fn verify_block_target(&self, header: &Header, target: &BigUint) -> Result<BigUint> {
+    pub fn verify_block_target(&mut self, header: &Header, target: &BigUint) -> Result<BigUint> {
         let verifier_setup = Instant::now();
 
         // Grab verifier output hash based on block PoW data
@@ -348,7 +348,7 @@ impl PoWModule {
     }
 
     /// Verify provided block corresponds to next mine target.
-    pub fn verify_block_hash(&self, header: &Header) -> Result<()> {
+    pub fn verify_block_hash(&mut self, header: &Header) -> Result<()> {
         // Grab the next mine target
         let target = self.next_mine_target()?;
 
@@ -734,7 +734,7 @@ mod tests {
         genesis_block.header.timestamp = 0.into();
         blockchain.add_block(&genesis_block)?;
 
-        let module = PoWModule::new(blockchain, DEFAULT_TEST_DIFFICULTY_TARGET, None, None)?;
+        let mut module = PoWModule::new(blockchain, DEFAULT_TEST_DIFFICULTY_TARGET, None, None)?;
 
         let (_, recvr) = smol::channel::bounded(1);
 

+ 27 - 87
src/validator/randomx_factory.rs

@@ -17,62 +17,21 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{
-    collections::HashMap,
-    fmt,
-    sync::{Arc, RwLock},
-    time::Instant,
-};
+use std::{collections::HashMap, sync::Arc, time::Instant};
 
 use randomx::{RandomXCache, RandomXFlags, RandomXVM};
 use tracing::{debug, warn};
 
 use crate::Result;
 
-/// The RandomX light mode virtual machine instance used to verify
-/// mining.
-#[derive(Clone)]
-pub struct RandomXVMInstance {
-    instance: Arc<RwLock<RandomXVM>>,
-}
-
-impl RandomXVMInstance {
-    /// Generate a new RandomX virtual machine instance operating in
-    /// light mode. Memory required per VM in light mode is 256MB.
-    fn create(key: &[u8]) -> Result<Self> {
-        let flags = RandomXFlags::get_recommended_flags();
-        let (flags, cache) = match RandomXCache::new(flags, key) {
-            Ok(cache) => (flags, cache),
-            Err(err) => {
-                warn!(target: "validator::randomx", "[VALIDATOR] Error initializing RandomX cache with flags {flags:?}: {err}");
-                warn!(target: "validator::randomx", "[VALIDATOR] Falling back to default flags");
-                let flags = RandomXFlags::DEFAULT;
-                let cache = RandomXCache::new(flags, key)?;
-                (flags, cache)
-            }
-        };
-
-        let vm = RandomXVM::new(flags, Some(cache), None)?;
-        debug!(target: "validator::randomx", "[VALIDATOR] RandomX VM started with flags = {flags:?}");
-
-        Ok(Self { instance: Arc::new(RwLock::new(vm)) })
-    }
-
-    /// Calculate the RandomX mining hash.
-    pub fn calculate_hash(&self, input: &[u8]) -> Result<Vec<u8>> {
-        let lock = self.instance.write().unwrap();
-        Ok(lock.calculate_hash(input)?)
-    }
-}
-
-unsafe impl Send for RandomXVMInstance {}
-unsafe impl Sync for RandomXVMInstance {}
+/// Atomic pointer to a RandomX light mode virtual machine instance.
+pub type RandomXVMInstance = Arc<RandomXVM>;
 
 /// The RandomX factory that manages the creation of RandomX VMs.
 #[derive(Clone, Debug)]
 pub struct RandomXFactory {
-    /// Threadsafe impl of the inner impl
-    inner: Arc<RwLock<RandomXFactoryInner>>,
+    vms: HashMap<Vec<u8>, (Instant, RandomXVMInstance)>,
+    max_vms: usize,
 }
 
 impl Default for RandomXFactory {
@@ -84,41 +43,11 @@ impl Default for RandomXFactory {
 impl RandomXFactory {
     /// Create a new RandomXFactory with the specified maximum number of VMs.
     pub fn new(max_vms: usize) -> Self {
-        Self { inner: Arc::new(RwLock::new(RandomXFactoryInner::new(max_vms))) }
+        Self { vms: HashMap::new(), max_vms }
     }
 
     /// Create a new RandomX VM instance with the specified key.
-    pub fn create(&self, key: &[u8]) -> Result<RandomXVMInstance> {
-        let res;
-        {
-            let mut inner = self.inner.write().unwrap();
-            res = inner.create(key)?;
-        }
-        Ok(res)
-    }
-
-    /// Auxiliary function to get the number of VMs currently
-    /// allocated.
-    pub fn get_count(&self) -> Result<usize> {
-        let inner = self.inner.read().unwrap();
-        Ok(inner.get_count())
-    }
-}
-
-struct RandomXFactoryInner {
-    vms: HashMap<Vec<u8>, (Instant, RandomXVMInstance)>,
-    max_vms: usize,
-}
-
-impl RandomXFactoryInner {
-    /// Create a new RandomXFactoryInner.
-    pub(crate) fn new(max_vms: usize) -> Self {
-        debug!(target: "validator::randomx", "[VALIDATOR] RandomXFactory started with {max_vms} max VMs");
-        Self { vms: Default::default(), max_vms }
-    }
-
-    /// Create a new RandomXVMInstance.
-    pub(crate) fn create(&mut self, key: &[u8]) -> Result<RandomXVMInstance> {
+    pub fn create(&mut self, key: &[u8]) -> Result<RandomXVMInstance> {
         if let Some(entry) = self.vms.get_mut(key) {
             let vm = entry.1.clone();
             entry.0 = Instant::now();
@@ -133,19 +62,30 @@ impl RandomXFactoryInner {
             }
         }
 
-        let vm = RandomXVMInstance::create(key)?;
+        // Generate a new RandomX virtual machine instance operating in
+        // light mode. Memory required per VM in light mode is 256MB.
+        let flags = RandomXFlags::get_recommended_flags();
+        let (flags, cache) = match RandomXCache::new(flags, key) {
+            Ok(cache) => (flags, cache),
+            Err(err) => {
+                warn!(target: "validator::randomx", "[VALIDATOR] Error initializing RandomX cache with flags {flags:?}: {err}");
+                warn!(target: "validator::randomx", "[VALIDATOR] Falling back to default flags");
+                let flags = RandomXFlags::DEFAULT;
+                let cache = RandomXCache::new(flags, key)?;
+                (flags, cache)
+            }
+        };
+
+        let vm = Arc::new(RandomXVM::new(flags, Some(cache), None)?);
+        debug!(target: "validator::randomx", "[VALIDATOR] RandomX VM started with flags = {flags:?}");
+
         self.vms.insert(Vec::from(key), (Instant::now(), vm.clone()));
         Ok(vm)
     }
 
-    /// Get the number of VMs currently allocated
-    pub(crate) fn get_count(&self) -> usize {
+    /// Auxiliary function to get the number of VMs currently
+    /// allocated.
+    pub fn get_count(&self) -> usize {
         self.vms.len()
     }
 }
-
-impl fmt::Debug for RandomXFactoryInner {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("RandomXFactory").field("max_vms", &self.max_vms).finish()
-    }
-}

+ 1 - 1
src/validator/utils.rs

@@ -119,7 +119,7 @@ pub async fn deploy_native_contracts(
 /// from max 32 bytes int, along with its squared RandomX hash number
 /// distance from max 32 bytes int.
 /// Genesis block has rank (0, 0).
-pub fn header_rank(module: &PoWModule, header: &Header) -> Result<(BigUint, BigUint, BigUint)> {
+pub fn header_rank(module: &mut PoWModule, header: &Header) -> Result<(BigUint, BigUint, BigUint)> {
     // Grab next mine target and difficulty
     let (target, difficulty) = module.next_mine_target_and_difficulty()?;
 

+ 10 - 6
src/validator/verification.rs

@@ -152,7 +152,11 @@ pub async fn verify_genesis_block(
 ///     5. Block header Proof of Work data are valid
 ///     6. Block hash is valid based on PoWModule validation
 /// Additional validity rules can be applied.
-pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, module: &PoWModule) -> Result<()> {
+pub fn validate_block(
+    block: &BlockInfo,
+    previous: &BlockInfo,
+    module: &mut PoWModule,
+) -> Result<()> {
     // Check block version (1)
     if block.header.version != block_version(block.header.height) {
         return Err(Error::BlockIsInvalid(block.hash().as_string()))
@@ -200,7 +204,7 @@ pub fn validate_blockchain(
     for (index, block) in blocks[1..].iter().enumerate() {
         let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
         let full_block = &full_blocks[1];
-        validate_block(full_block, &full_blocks[0], &module)?;
+        validate_block(full_block, &full_blocks[0], &mut module)?;
         // Update PoW module
         module.append(&full_block.header, &module.next_difficulty()?)?;
     }
@@ -215,7 +219,7 @@ pub fn validate_blockchain(
 pub async fn verify_block(
     overlay: &BlockchainOverlayPtr,
     diffs: &[SledDbOverlayStateDiff],
-    module: &PoWModule,
+    module: &mut PoWModule,
     block: &BlockInfo,
     previous: &BlockInfo,
     verify_fees: bool,
@@ -1116,7 +1120,7 @@ pub async fn verify_proposal(
     }
 
     // Check if proposal extends any existing forks
-    let (fork, index) = consensus.find_extended_fork(proposal).await?;
+    let (mut fork, index) = consensus.find_extended_fork(proposal).await?;
 
     // Grab overlay last block
     let previous = fork.overlay.lock().unwrap().last_block()?;
@@ -1125,7 +1129,7 @@ pub async fn verify_proposal(
     if let Err(e) = verify_block(
         &fork.overlay,
         &fork.diffs,
-        &fork.module,
+        &mut fork.module,
         &proposal.block,
         &previous,
         verify_fees,
@@ -1170,7 +1174,7 @@ pub async fn verify_fork_proposal(
     if let Err(e) = verify_block(
         &fork.overlay,
         &fork.diffs,
-        &fork.module,
+        &mut fork.module,
         &proposal.block,
         &previous,
         verify_fees,