From 6fb721e8615cb7ed60abcc1a088396e332e64f8e Mon Sep 17 00:00:00 2001
From: Svyatoslav Nikolsky <svyatonik@gmail.com>
Date: Mon, 19 Aug 2019 14:44:16 +0300
Subject: [PATCH] Value lifetime is returned from blockchain cache (#3403)

* value range in blockchain cache

* revert me (testing for spurious failure)

* Revert "revert me (testing for spurious failure)"

This reverts commit 21a4a3cf5ee14e003541b779c41351e4f5e1122a.
---
 .../core/client/db/src/cache/list_cache.rs    | 19 +++++++++++--------
 substrate/core/client/db/src/cache/mod.rs     | 17 +++++++++++++++--
 substrate/core/client/db/src/light.rs         | 18 +++++++++++++-----
 substrate/core/client/src/blockchain.rs       |  8 +++++++-
 substrate/core/consensus/aura/src/lib.rs      |  4 ++--
 substrate/core/consensus/babe/src/lib.rs      |  4 ++--
 6 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/substrate/core/client/db/src/cache/list_cache.rs b/substrate/core/client/db/src/cache/list_cache.rs
index 9e54fdbb62e..188012e78f0 100644
--- a/substrate/core/client/db/src/cache/list_cache.rs
+++ b/substrate/core/client/db/src/cache/list_cache.rs
@@ -130,7 +130,10 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
 	}
 
 	/// Get value valid at block.
-	pub fn value_at_block(&self, at: &ComplexBlockId<Block>) -> ClientResult<Option<T>> {
+	pub fn value_at_block(
+		&self,
+		at: &ComplexBlockId<Block>,
+	) -> ClientResult<Option<(ComplexBlockId<Block>, Option<ComplexBlockId<Block>>, T)>> {
 		let head = if at.number <= self.best_finalized_block.number {
 			// if the block is older than the best known finalized block
 			// => we're should search for the finalized value
@@ -164,7 +167,7 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
 
 		match head {
 			Some(head) => head.search_best_before(&self.storage, at.number)
-				.map(|e| e.map(|e| e.0.value)),
+				.map(|e| e.map(|e| (e.0.valid_from, e.1, e.0.value))),
 			None => Ok(None),
 		}
 	}
@@ -677,7 +680,7 @@ pub mod tests {
 				.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 })
 				.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }),
 			1024, test_id(100)
-		).value_at_block(&test_id(50)).unwrap(), Some(30));
+		).value_at_block(&test_id(50)).unwrap(), Some((test_id(30), Some(test_id(100)), 30)));
 		// when block is the best finalized block AND value is some
 		// ---> [100]
 		assert_eq!(ListCache::new(
@@ -687,7 +690,7 @@ pub mod tests {
 				.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 })
 				.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }),
 			1024, test_id(100)
-		).value_at_block(&test_id(100)).unwrap(), Some(100));
+		).value_at_block(&test_id(100)).unwrap(), Some((test_id(100), None, 100)));
 		// when block is parallel to the best finalized block
 		// ---- 100
 		// ---> [100]
@@ -708,7 +711,7 @@ pub mod tests {
 				.with_id(50, H256::from_low_u64_be(50))
 				.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 }),
 			1024, test_id(100)
-		).value_at_block(&test_id(200)).unwrap(), Some(100));
+		).value_at_block(&test_id(200)).unwrap(), Some((test_id(100), None, 100)));
 
 		// when block is later than last finalized block AND there are no matching forks
 		// AND block is connected to finalized block AND finalized value is Some
@@ -724,7 +727,7 @@ pub mod tests {
 				.with_header(test_header(4))
 				.with_header(fork_header(0, 2, 3)),
 			1024, test_id(2)
-		).value_at_block(&fork_id(0, 2, 3)).unwrap(), Some(2));
+		).value_at_block(&fork_id(0, 2, 3)).unwrap(), Some((correct_id(2), None, 2)));
 		// when block is later than last finalized block AND there are no matching forks
 		// AND block is not connected to finalized block
 		//    ---   2  --- 3
@@ -754,7 +757,7 @@ pub mod tests {
 				.with_header(test_header(4))
 				.with_header(test_header(5)),
 			1024, test_id(2)
-		).value_at_block(&correct_id(5)).unwrap(), Some(4));
+		).value_at_block(&correct_id(5)).unwrap(), Some((correct_id(4), None, 4)));
 		// when block is later than last finalized block AND it does not fits unfinalized fork
 		// AND it is connected to the finalized block AND finalized value is Some
 		// ---> [2] ----------> [4]
@@ -769,7 +772,7 @@ pub mod tests {
 				.with_header(test_header(4))
 				.with_header(fork_header(0, 2, 3)),
 			1024, test_id(2)
-		).value_at_block(&fork_id(0, 2, 3)).unwrap(), Some(2));
+		).value_at_block(&fork_id(0, 2, 3)).unwrap(), Some((correct_id(2), None, 2)));
 	}
 
 	#[test]
diff --git a/substrate/core/client/db/src/cache/mod.rs b/substrate/core/client/db/src/cache/mod.rs
index 4d452b37d96..68245a54eee 100644
--- a/substrate/core/client/db/src/cache/mod.rs
+++ b/substrate/core/client/db/src/cache/mod.rs
@@ -294,7 +294,11 @@ impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
 		Ok(())
 	}
 
-	fn get_at(&self, key: &CacheKeyId, at: &BlockId<Block>) -> Option<Vec<u8>> {
+	fn get_at(
+		&self,
+		key: &CacheKeyId,
+		at: &BlockId<Block>,
+	) -> Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)> {
 		let cache = self.0.read();
 		let storage = cache.cache_at.get(key)?.storage();
 		let db = storage.db();
@@ -318,7 +322,16 @@ impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
 			},
 		};
 
-		cache.cache_at.get(key)?.value_at_block(&at).ok()?
+		cache.cache_at
+			.get(key)?
+			.value_at_block(&at)
+			.map(|block_and_value| block_and_value.map(|(begin_block, end_block, value)|
+				(
+					(begin_block.number, begin_block.hash),
+					end_block.map(|end_block| (end_block.number, end_block.hash)),
+					value,
+				)))
+			.ok()?
 	}
 }
 
diff --git a/substrate/core/client/db/src/light.rs b/substrate/core/client/db/src/light.rs
index 3e60e9e7a1c..2ee2c2ec545 100644
--- a/substrate/core/client/db/src/light.rs
+++ b/substrate/core/client/db/src/light.rs
@@ -562,10 +562,10 @@ pub(crate) mod tests {
 		header
 	}
 
-	pub fn insert_block<F: Fn() -> Header>(
+	pub fn insert_block<F: FnMut() -> Header>(
 		db: &LightStorage<Block>,
 		cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
-		header: F,
+		mut header: F,
 	) -> Hash {
 		let header = header();
 		let hash = header.hash();
@@ -850,7 +850,7 @@ pub(crate) mod tests {
 
 		fn get_authorities(cache: &dyn BlockchainCache<Block>, at: BlockId<Block>) -> Option<Vec<AuthorityId>> {
 			cache.get_at(&well_known_cache_keys::AUTHORITIES, &at)
-				.and_then(|val| Decode::decode(&mut &val[..]).ok())
+				.and_then(|(_, _, val)| Decode::decode(&mut &val[..]).ok())
 		}
 
 		let auth1 = || AuthorityId::from_raw([1u8; 32]);
@@ -1036,7 +1036,12 @@ pub(crate) mod tests {
 		assert_eq!(db.cache().get_at(b"test", &BlockId::Number(0)), None);
 
 		// insert genesis block (no value for cache is provided)
-		insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
+		let mut genesis_hash = None;
+		insert_block(&db, HashMap::new(), || {
+			let header = default_header(&Default::default(), 0);
+			genesis_hash = Some(header.hash());
+			header
+		});
 
 		// after genesis is inserted => None
 		assert_eq!(db.cache().get_at(b"test", &BlockId::Number(0)), None);
@@ -1045,6 +1050,9 @@ pub(crate) mod tests {
 		db.cache().initialize(b"test", vec![42]).unwrap();
 
 		// after genesis is inserted + cache is initialized => Some
-		assert_eq!(db.cache().get_at(b"test", &BlockId::Number(0)), Some(vec![42]));
+		assert_eq!(
+			db.cache().get_at(b"test", &BlockId::Number(0)),
+			Some(((0, genesis_hash.unwrap()), None, vec![42])),
+		);
 	}
 }
diff --git a/substrate/core/client/src/blockchain.rs b/substrate/core/client/src/blockchain.rs
index 2bf61df704d..f4a4f8a4aa9 100644
--- a/substrate/core/client/src/blockchain.rs
+++ b/substrate/core/client/src/blockchain.rs
@@ -106,7 +106,13 @@ pub trait Cache<Block: BlockT>: Send + Sync {
 	/// Otherwise cache may end up in inconsistent state.
 	fn initialize(&self, key: &well_known_cache_keys::Id, value_at_genesis: Vec<u8>) -> Result<()>;
 	/// Returns cached value by the given key.
-	fn get_at(&self, key: &well_known_cache_keys::Id, block: &BlockId<Block>) -> Option<Vec<u8>>;
+	///
+	/// Returned tuple is the range where value has been active and the value itself.
+	fn get_at(
+		&self,
+		key: &well_known_cache_keys::Id,
+		block: &BlockId<Block>,
+	) -> Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>;
 }
 
 /// Blockchain info
diff --git a/substrate/core/consensus/aura/src/lib.rs b/substrate/core/consensus/aura/src/lib.rs
index e2e36fdbfba..3d4148c52df 100644
--- a/substrate/core/consensus/aura/src/lib.rs
+++ b/substrate/core/consensus/aura/src/lib.rs
@@ -582,7 +582,7 @@ fn initialize_authorities_cache<A, B, C>(client: &C) -> Result<(), ConsensusErro
 	let genesis_id = BlockId::Number(Zero::zero());
 	let genesis_authorities: Option<Vec<A>> = cache
 		.get_at(&well_known_cache_keys::AUTHORITIES, &genesis_id)
-		.and_then(|v| Decode::decode(&mut &v[..]).ok());
+		.and_then(|(_, _, v)| Decode::decode(&mut &v[..]).ok());
 	if genesis_authorities.is_some() {
 		return Ok(());
 	}
@@ -610,7 +610,7 @@ fn authorities<A, B, C>(client: &C, at: &BlockId<B>) -> Result<Vec<A>, Consensus
 		.cache()
 		.and_then(|cache| cache
 			.get_at(&well_known_cache_keys::AUTHORITIES, at)
-			.and_then(|v| Decode::decode(&mut &v[..]).ok())
+			.and_then(|(_, _, v)| Decode::decode(&mut &v[..]).ok())
 		)
 		.or_else(|| AuraApi::authorities(&*client.runtime_api(), at).ok())
 		.ok_or_else(|| consensus_common::Error::InvalidAuthoritiesSet.into())
diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs
index e29de64de2b..6d049dd6be1 100644
--- a/substrate/core/consensus/babe/src/lib.rs
+++ b/substrate/core/consensus/babe/src/lib.rs
@@ -952,7 +952,7 @@ fn epoch_from_cache<B, C>(client: &C, at: &BlockId<B>) -> Option<MaybeSpanEpoch>
 	client.cache()
 		.and_then(|cache| cache
 			.get_at(&well_known_cache_keys::EPOCH, at)
-			.and_then(|v| Decode::decode(&mut &v[..]).ok()))
+			.and_then(|(_, _, v)| Decode::decode(&mut &v[..]).ok()))
 }
 
 /// Extract current epoch from runtime.
@@ -1202,7 +1202,7 @@ fn initialize_authorities_cache<B, C>(client: &C) -> Result<(), ConsensusError>
 	let genesis_id = BlockId::Number(Zero::zero());
 	let genesis_epoch: Option<MaybeSpanEpoch> = cache
 		.get_at(&well_known_cache_keys::EPOCH, &genesis_id)
-		.and_then(|v| Decode::decode(&mut &v[..]).ok());
+		.and_then(|(_, _, v)| Decode::decode(&mut &v[..]).ok());
 	if genesis_epoch.is_some() {
 		return Ok(());
 	}
-- 
GitLab