Mmr persist state (#12822)

client/mmr: persisting gadget state across runs

Fixes #12780

* client/mmr: on init do canonicalization catch-up

* client/mmr: add more tests

* client/mmr: persist gadget progress in aux db

* client/mmr: add more tests

* client/mmr: replace async_std with tokio

* remove leftover comment

* address review comments

Signed-off-by: acatangiu <adrian@parity.io>
This commit is contained in:
Adrian Catangiu
2022-12-07 12:19:46 +02:00
committed by GitHub
parent 72a7fa035b
commit 1657feae3b
6 changed files with 480 additions and 85 deletions
@@ -16,12 +16,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::{
future::Future,
sync::{Arc, Mutex},
time::Duration,
};
use crate::MmrGadget;
use parking_lot::Mutex;
use sc_block_builder::BlockBuilderProvider;
use sc_client_api::{
Backend as BackendT, BlockchainEvents, FinalityNotifications, ImportNotifications,
@@ -41,33 +37,34 @@ use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT},
};
use std::{future::Future, sync::Arc, time::Duration};
use substrate_test_runtime_client::{
runtime::{Block, BlockNumber, Hash, Header},
Backend, BlockBuilderExt, Client, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt,
TestClientBuilder, TestClientBuilderExt,
};
use crate::MmrGadget;
use tokio::runtime::Runtime;
type MmrHash = H256;
struct MockRuntimeApiData {
num_blocks: BlockNumber,
pub(crate) struct MockRuntimeApiData {
pub(crate) num_blocks: BlockNumber,
}
#[derive(Clone)]
pub struct MockRuntimeApi {
data: Arc<Mutex<MockRuntimeApiData>>,
pub(crate) struct MockRuntimeApi {
pub(crate) data: Arc<Mutex<MockRuntimeApiData>>,
}
impl MockRuntimeApi {
pub const INDEXING_PREFIX: &'static [u8] = b"mmr_test";
pub(crate) const INDEXING_PREFIX: &'static [u8] = b"mmr_test";
}
pub struct MmrBlock {
block: Block,
leaf_idx: Option<LeafIndex>,
leaf_data: Vec<u8>,
#[derive(Clone, Debug)]
pub(crate) struct MmrBlock {
pub(crate) block: Block,
pub(crate) leaf_idx: Option<LeafIndex>,
pub(crate) leaf_data: Vec<u8>,
}
#[derive(Clone, Copy)]
@@ -90,7 +87,7 @@ impl MmrBlock {
OffchainKeyType::Temp => NodesUtils::node_temp_offchain_key::<Header>(
MockRuntimeApi::INDEXING_PREFIX,
node,
*self.block.header.parent_hash(),
self.parent_hash(),
),
OffchainKeyType::Canon =>
NodesUtils::node_canon_offchain_key(MockRuntimeApi::INDEXING_PREFIX, node),
@@ -98,14 +95,14 @@ impl MmrBlock {
}
}
pub struct MockClient {
client: Mutex<Client<Backend>>,
backend: Arc<Backend>,
runtime_api_params: Arc<Mutex<MockRuntimeApiData>>,
pub(crate) struct MockClient {
pub(crate) client: Mutex<Client<Backend>>,
pub(crate) backend: Arc<Backend>,
pub(crate) runtime_api_params: Arc<Mutex<MockRuntimeApiData>>,
}
impl MockClient {
fn new() -> Self {
pub(crate) fn new() -> Self {
let client_builder = TestClientBuilder::new().enable_offchain_indexing_api();
let (client, backend) = client_builder.build_with_backend();
MockClient {
@@ -115,7 +112,7 @@ impl MockClient {
}
}
fn offchain_db(&self) -> OffchainDb<<Backend as BackendT<Block>>::OffchainStorage> {
pub(crate) fn offchain_db(&self) -> OffchainDb<<Backend as BackendT<Block>>::OffchainStorage> {
OffchainDb::new(self.backend.offchain_storage().unwrap())
}
@@ -125,7 +122,7 @@ impl MockClient {
name: &[u8],
maybe_leaf_idx: Option<LeafIndex>,
) -> MmrBlock {
let mut client = self.client.lock().unwrap();
let mut client = self.client.lock();
let mut block_builder = client.new_block_at(at, Default::default(), false).unwrap();
// Make sure the block has a different hash than its siblings
@@ -157,9 +154,9 @@ impl MockClient {
}
pub fn finalize_block(&self, hash: Hash, maybe_num_mmr_blocks: Option<BlockNumber>) {
let client = self.client.lock().unwrap();
let client = self.client.lock();
if let Some(num_mmr_blocks) = maybe_num_mmr_blocks {
self.runtime_api_params.lock().unwrap().num_blocks = num_mmr_blocks;
self.runtime_api_params.lock().num_blocks = num_mmr_blocks;
}
client.finalize_block(hash, None).unwrap();
@@ -216,7 +213,7 @@ impl HeaderMetadata<Block> for MockClient {
type Error = <Client<Backend> as HeaderMetadata<Block>>::Error;
fn header_metadata(&self, hash: Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
self.client.lock().unwrap().header_metadata(hash)
self.client.lock().header_metadata(hash)
}
fn insert_header_metadata(&self, _hash: Hash, _header_metadata: CachedHeaderMetadata<Block>) {
@@ -230,23 +227,23 @@ impl HeaderMetadata<Block> for MockClient {
impl HeaderBackend<Block> for MockClient {
fn header(&self, id: BlockId<Block>) -> sc_client_api::blockchain::Result<Option<Header>> {
self.client.lock().unwrap().header(&id)
self.client.lock().header(&id)
}
fn info(&self) -> Info<Block> {
self.client.lock().unwrap().info()
self.client.lock().info()
}
fn status(&self, id: BlockId<Block>) -> sc_client_api::blockchain::Result<BlockStatus> {
self.client.lock().unwrap().status(id)
self.client.lock().status(id)
}
fn number(&self, hash: Hash) -> sc_client_api::blockchain::Result<Option<BlockNumber>> {
self.client.lock().unwrap().number(hash)
self.client.lock().number(hash)
}
fn hash(&self, number: BlockNumber) -> sc_client_api::blockchain::Result<Option<Hash>> {
self.client.lock().unwrap().hash(number)
self.client.lock().hash(number)
}
}
@@ -256,7 +253,7 @@ impl BlockchainEvents<Block> for MockClient {
}
fn finality_notification_stream(&self) -> FinalityNotifications<Block> {
self.client.lock().unwrap().finality_notification_stream()
self.client.lock().finality_notification_stream()
}
fn storage_changes_notification_stream(
@@ -283,7 +280,7 @@ sp_api::mock_impl_runtime_apis! {
}
fn mmr_leaf_count(&self) -> Result<LeafIndex, mmr::Error> {
Ok(self.data.lock().unwrap().num_blocks)
Ok(self.data.lock().num_blocks)
}
fn generate_proof(
@@ -310,13 +307,38 @@ sp_api::mock_impl_runtime_apis! {
}
}
pub fn run_test_with_mmr_gadget<F, Fut>(f: F)
pub(crate) fn run_test_with_mmr_gadget<F, Fut>(post_gadget: F)
where
F: FnOnce(Arc<MockClient>) -> Fut + 'static,
Fut: Future<Output = ()>,
{
let runtime = tokio::runtime::Runtime::new().unwrap();
run_test_with_mmr_gadget_pre_post(|_| async {}, post_gadget);
}
pub(crate) fn run_test_with_mmr_gadget_pre_post<F, G, RetF, RetG>(pre_gadget: F, post_gadget: G)
where
F: FnOnce(Arc<MockClient>) -> RetF + 'static,
G: FnOnce(Arc<MockClient>) -> RetG + 'static,
RetF: Future<Output = ()>,
RetG: Future<Output = ()>,
{
let client = Arc::new(MockClient::new());
run_test_with_mmr_gadget_pre_post_using_client(client, pre_gadget, post_gadget)
}
pub(crate) fn run_test_with_mmr_gadget_pre_post_using_client<F, G, RetF, RetG>(
client: Arc<MockClient>,
pre_gadget: F,
post_gadget: G,
) where
F: FnOnce(Arc<MockClient>) -> RetF + 'static,
G: FnOnce(Arc<MockClient>) -> RetG + 'static,
RetF: Future<Output = ()>,
RetG: Future<Output = ()>,
{
let client_clone = client.clone();
let runtime = Runtime::new().unwrap();
runtime.block_on(async move { pre_gadget(client_clone).await });
let client_clone = client.clone();
runtime.spawn(async move {
@@ -327,6 +349,6 @@ where
runtime.block_on(async move {
tokio::time::sleep(Duration::from_millis(200)).await;
f(client).await
post_gadget(client).await
});
}