diff --git a/src/eth/executor/evm_input.rs b/src/eth/executor/evm_input.rs index 54a3e80d2..4a8bc6294 100644 --- a/src/eth/executor/evm_input.rs +++ b/src/eth/executor/evm_input.rs @@ -157,3 +157,16 @@ impl EvmInput { self.to.is_some() && not(self.data.is_empty()) } } + +impl PartialEq<(&TransactionInput, &PendingBlockHeader)> for EvmInput { + fn eq(&self, other: &(&TransactionInput, &PendingBlockHeader)) -> bool { + self.block_number == other.1.number + && self.block_timestamp == *other.1.timestamp + && self.chain_id == other.0.chain_id + && self.data == other.0.input + && self.from == other.0.signer + && self.nonce.is_some_and(|inner| inner == other.0.nonce) + && self.value == other.0.value + && self.to == other.0.to + } +} diff --git a/src/eth/storage/temporary/inmemory.rs b/src/eth/storage/temporary/inmemory.rs index 015628eef..7492fdaac 100644 --- a/src/eth/storage/temporary/inmemory.rs +++ b/src/eth/storage/temporary/inmemory.rs @@ -3,6 +3,9 @@ use std::collections::HashMap; use parking_lot::RwLock; +use parking_lot::RwLockUpgradableReadGuard; +#[cfg(not(feature = "dev"))] +use parking_lot::RwLockWriteGuard; use crate::eth::executor::EvmInput; use crate::eth::primitives::Account; @@ -121,11 +124,10 @@ impl TemporaryStorage for InMemoryTemporaryStorage { fn save_pending_execution(&self, tx: TransactionExecution, check_conflicts: bool) -> Result<(), StratusError> { // check conflicts - let mut pending_block = self.pending_block.write(); + let pending_block = self.pending_block.upgradable_read(); if let TransactionExecution::Local(tx) = &tx { - let expected_input = EvmInput::from_eth_transaction(&tx.input, &pending_block.block.header); - - if expected_input != tx.evm_input { + if tx.evm_input != (&tx.input, &pending_block.block.header) { + let expected_input = EvmInput::from_eth_transaction(&tx.input, &pending_block.block.header); return Err(StratusError::TransactionEvmInputMismatch { expected: Box::new(expected_input), actual: Box::new(tx.evm_input.clone()), @@ -133,6 +135,8 @@ impl TemporaryStorage for InMemoryTemporaryStorage { } } + let mut pending_block = RwLockUpgradableReadGuard::::upgrade(pending_block); + if check_conflicts { if let Some(conflicts) = self.check_conflicts(tx.execution())? { return Err(StratusError::TransactionConflict(conflicts.into())); @@ -179,27 +183,34 @@ impl TemporaryStorage for InMemoryTemporaryStorage { } fn finish_pending_block(&self) -> anyhow::Result { - let mut pending_block = self.pending_block.write(); - - #[cfg(feature = "dev")] - let mut finished_block = pending_block.block.clone(); - #[cfg(not(feature = "dev"))] - let finished_block = pending_block.block.clone(); + let pending_block = self.pending_block.upgradable_read(); + // This has to happen BEFORE creating the new state, because UnixTimeNow::default() may change the offset. #[cfg(feature = "dev")] - { + let finished_block = { + let mut finished_block = pending_block.block.clone(); // Update block timestamp only if evm_setNextBlockTimestamp was called, // otherwise keep the original timestamp from pending block creation if UnixTime::evm_set_next_block_timestamp_was_called() { finished_block.header.timestamp = UnixTimeNow::default(); } - } + finished_block + }; + let next_state = InMemoryTemporaryStorageState::new(pending_block.block.header.number.next_block_number()); + + let mut pending_block = RwLockUpgradableReadGuard::::upgrade(pending_block); let mut latest = self.latest_block.write(); - *latest = Some(std::mem::replace( - &mut *pending_block, - InMemoryTemporaryStorageState::new(finished_block.header.number.next_block_number()), - )); + + *latest = Some(std::mem::replace(&mut *pending_block, next_state)); + + drop(pending_block); + + #[cfg(not(feature = "dev"))] + let finished_block = { + let latest = RwLockWriteGuard::>::downgrade(latest); + latest.as_ref().expect("latest should be Some after finishing the pending block").block.clone() + }; Ok(finished_block) }