diff --git a/core/lib/dal/.sqlx/query-2b1136c7781bcdbd9d5d1e6f900fe400fcba3bfc5cf1b7c2b801508f6673d94e.json b/core/lib/dal/.sqlx/query-2b1136c7781bcdbd9d5d1e6f900fe400fcba3bfc5cf1b7c2b801508f6673d94e.json deleted file mode 100644 index 58b1236e6f6..00000000000 --- a/core/lib/dal/.sqlx/query-2b1136c7781bcdbd9d5d1e6f900fe400fcba3bfc5cf1b7c2b801508f6673d94e.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n bytecode\n FROM\n factory_deps\n WHERE\n bytecode_hash = $1\n AND miniblock_number <= $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "bytecode", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Bytea", - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "2b1136c7781bcdbd9d5d1e6f900fe400fcba3bfc5cf1b7c2b801508f6673d94e" -} diff --git a/core/lib/dal/.sqlx/query-fe3aa7ce9cd799026de57bdb943a4a992bee16a2d3d84be2aafc27af8468b64e.json b/core/lib/dal/.sqlx/query-fe3aa7ce9cd799026de57bdb943a4a992bee16a2d3d84be2aafc27af8468b64e.json new file mode 100644 index 00000000000..76144c3000a --- /dev/null +++ b/core/lib/dal/.sqlx/query-fe3aa7ce9cd799026de57bdb943a4a992bee16a2d3d84be2aafc27af8468b64e.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n bytecode,\n miniblock_number\n FROM\n factory_deps\n WHERE\n bytecode_hash = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "bytecode", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "miniblock_number", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Bytea" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "fe3aa7ce9cd799026de57bdb943a4a992bee16a2d3d84be2aafc27af8468b64e" +} diff --git a/core/lib/dal/src/storage_web3_dal.rs b/core/lib/dal/src/storage_web3_dal.rs index 6583bc1410b..cc250a6cefc 100644 --- a/core/lib/dal/src/storage_web3_dal.rs +++ b/core/lib/dal/src/storage_web3_dal.rs @@ -241,30 +241,27 @@ impl StorageWeb3Dal<'_, '_> { Ok(row.map(|row| row.bytecode)) } - /// This method doesn't check if block with number equals to `block_number` - /// is present in the database. For such blocks `None` will be returned. - pub async fn get_factory_dep_unchecked( + /// Given bytecode hash, returns `bytecode` and `miniblock_number` at which it was inserted. + pub async fn get_factory_dep( &mut self, hash: H256, - block_number: MiniblockNumber, - ) -> sqlx::Result>> { + ) -> sqlx::Result, MiniblockNumber)>> { let row = sqlx::query!( r#" SELECT - bytecode + bytecode, + miniblock_number FROM factory_deps WHERE bytecode_hash = $1 - AND miniblock_number <= $2 "#, hash.as_bytes(), - i64::from(block_number.0) ) .fetch_optional(self.storage.conn()) .await?; - Ok(row.map(|row| row.bytecode)) + Ok(row.map(|row| (row.bytecode, MiniblockNumber(row.miniblock_number as u32)))) } } diff --git a/core/lib/state/src/cache/lru_cache.rs b/core/lib/state/src/cache/lru_cache.rs index 608ab4da115..0e0f3541117 100644 --- a/core/lib/state/src/cache/lru_cache.rs +++ b/core/lib/state/src/cache/lru_cache.rs @@ -100,6 +100,12 @@ mod tests { use crate::cache::{lru_cache::LruCache, *}; + impl CacheValue for Vec { + fn cache_weight(&self) -> u32 { + self.len().try_into().expect("Cached bytes are too large") + } + } + #[test] fn cache_with_zero_capacity() { let zero_cache = LruCache::>::new("test", 0); diff --git a/core/lib/state/src/postgres/mod.rs b/core/lib/state/src/postgres/mod.rs index 51e6014afac..de58a860630 100644 --- a/core/lib/state/src/postgres/mod.rs +++ b/core/lib/state/src/postgres/mod.rs @@ -24,12 +24,20 @@ mod metrics; #[cfg(test)] mod tests; +#[derive(Debug, Clone, PartialEq, Eq)] +struct TimestampedFactoryDep { + bytecode: Vec, + inserted_at: MiniblockNumber, +} + /// Type alias for smart contract source code cache. -type FactoryDepsCache = LruCache>; +type FactoryDepsCache = LruCache; -impl CacheValue for Vec { +impl CacheValue for TimestampedFactoryDep { fn cache_weight(&self) -> u32 { - self.len().try_into().expect("Cached bytes are too large") + (self.bytecode.len() + mem::size_of::()) + .try_into() + .expect("Cached bytes are too large") } } @@ -553,17 +561,21 @@ impl ReadStorage for PostgresStorage<'_> { .as_ref() .and_then(|caches| caches.factory_deps.get(&hash)); - let result = cached_value.or_else(|| { + let value = cached_value.or_else(|| { let mut dal = self.connection.storage_web3_dal(); let value = self .rt_handle - .block_on(dal.get_factory_dep_unchecked(hash, self.miniblock_number)) - .expect("Failed executing `load_factory_dep`"); + .block_on(dal.get_factory_dep(hash)) + .expect("Failed executing `load_factory_dep`") + .map(|(bytecode, inserted_at)| TimestampedFactoryDep { + bytecode, + inserted_at, + }); if let Some(caches) = &self.caches { // If we receive None, we won't cache it. - if let Some(dep) = value.clone() { - caches.factory_deps.insert(hash, dep); + if let Some(value) = value.clone() { + caches.factory_deps.insert(hash, value); } }; @@ -571,7 +583,11 @@ impl ReadStorage for PostgresStorage<'_> { }); latency.observe(); - result + Some( + value + .filter(|dep| dep.inserted_at <= self.miniblock_number)? + .bytecode, + ) } fn get_enumeration_index(&mut self, key: &StorageKey) -> Option { diff --git a/core/lib/state/src/postgres/tests.rs b/core/lib/state/src/postgres/tests.rs index 1d878a9c631..5e13464dc9b 100644 --- a/core/lib/state/src/postgres/tests.rs +++ b/core/lib/state/src/postgres/tests.rs @@ -231,6 +231,18 @@ fn test_factory_deps_cache(pool: &ConnectionPool, rt_handle: Handle) { ) .unwrap(); + let mut contracts = HashMap::new(); + contracts.insert(H256::from_low_u64_be(1), vec![1, 2, 3, 4]); + storage + .rt_handle + .block_on( + storage + .connection + .factory_deps_dal() + .insert_factory_deps(MiniblockNumber(1), &contracts), + ) + .unwrap(); + // Create the storage that should have the cache filled. let mut storage = PostgresStorage::new( storage.rt_handle, @@ -243,7 +255,40 @@ fn test_factory_deps_cache(pool: &ConnectionPool, rt_handle: Handle) { // Fill the cache let dep = storage.load_factory_dep(zero_addr); assert_eq!(dep, Some(vec![1, 2, 3])); - assert_eq!(caches.factory_deps.get(&zero_addr), Some(vec![1, 2, 3])); + assert_eq!( + caches.factory_deps.get(&zero_addr), + Some(TimestampedFactoryDep { + bytecode: vec![1, 2, 3], + inserted_at: MiniblockNumber(0) + }) + ); + + let dep = storage.load_factory_dep(H256::from_low_u64_be(1)); + assert_eq!(dep, Some(vec![1, 2, 3, 4])); + assert_eq!( + caches.factory_deps.get(&H256::from_low_u64_be(1)), + Some(TimestampedFactoryDep { + bytecode: vec![1, 2, 3, 4], + inserted_at: MiniblockNumber(1) + }) + ); + + // Create storage with `MiniblockNumber(0)`. + let mut storage = PostgresStorage::new( + storage.rt_handle, + storage.connection, + MiniblockNumber(0), + true, + ) + .with_caches(caches.clone()); + + // First bytecode was published at miniblock 0, so it should be visible. + let dep = storage.load_factory_dep(zero_addr); + assert_eq!(dep, Some(vec![1, 2, 3])); + + // Second bytecode was published at miniblock 1, so it shouldn't be visible. + let dep = storage.load_factory_dep(H256::from_low_u64_be(1)); + assert!(dep.is_none()); } #[tokio::test]