referrerpolicy=no-referrer-when-downgrade

substrate_state_trie_migration_rpc/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Rpc for state migration.
19
20use jsonrpsee::{
21	core::RpcResult,
22	proc_macros::rpc,
23	types::error::{ErrorCode, ErrorObject, ErrorObjectOwned},
24	Extensions,
25};
26use sc_client_api::TrieCacheContext;
27use sc_rpc_api::check_if_safe;
28use serde::{Deserialize, Serialize};
29use sp_runtime::traits::Block as BlockT;
30use std::sync::Arc;
31
32use sp_core::{
33	storage::{ChildInfo, ChildType, PrefixedStorageKey},
34	Hasher,
35};
36use sp_state_machine::backend::AsTrieBackend;
37use sp_trie::{
38	trie_types::{TrieDB, TrieDBBuilder},
39	KeySpacedDB, Trie,
40};
41use trie_db::{
42	node::{NodePlan, ValuePlan},
43	TrieDBNodeIterator,
44};
45
46fn count_migrate<'a, H: Hasher>(
47	storage: &'a dyn trie_db::HashDBRef<H, Vec<u8>>,
48	root: &'a H::Out,
49) -> std::result::Result<(u64, u64, TrieDB<'a, 'a, H>), String> {
50	let mut nb = 0u64;
51	let mut total_nb = 0u64;
52	let trie = TrieDBBuilder::new(storage, root).build();
53	let iter_node =
54		TrieDBNodeIterator::new(&trie).map_err(|e| format!("TrieDB node iterator error: {}", e))?;
55	for node in iter_node {
56		let node = node.map_err(|e| format!("TrieDB node iterator error: {}", e))?;
57		match node.2.node_plan() {
58			NodePlan::Leaf { value, .. } | NodePlan::NibbledBranch { value: Some(value), .. } => {
59				total_nb += 1;
60				if let ValuePlan::Inline(range) = value {
61					if (range.end - range.start) as u32 >=
62						sp_core::storage::TRIE_VALUE_NODE_THRESHOLD
63					{
64						nb += 1;
65					}
66				}
67			},
68			_ => (),
69		}
70	}
71	Ok((nb, total_nb, trie))
72}
73
74/// Check trie migration status.
75pub fn migration_status<H, B>(backend: &B) -> std::result::Result<MigrationStatusResult, String>
76where
77	H: Hasher,
78	H::Out: codec::Codec,
79	B: AsTrieBackend<H>,
80{
81	let trie_backend = backend.as_trie_backend();
82	let essence = trie_backend.essence();
83	let (top_remaining_to_migrate, total_top, trie) = count_migrate(essence, essence.root())?;
84
85	let mut child_remaining_to_migrate = 0;
86	let mut total_child = 0;
87	let mut child_roots: Vec<(ChildInfo, Vec<u8>)> = Vec::new();
88	// get all child trie roots
89	for key_value in trie.iter().map_err(|e| format!("TrieDB node iterator error: {}", e))? {
90		let (key, value) = key_value.map_err(|e| format!("TrieDB node iterator error: {}", e))?;
91		if key[..].starts_with(sp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX)
92		{
93			let prefixed_key = PrefixedStorageKey::new(key);
94			let (_type, unprefixed) = ChildType::from_prefixed_key(&prefixed_key).unwrap();
95			child_roots.push((ChildInfo::new_default(unprefixed), value));
96		}
97	}
98	for (child_info, root) in child_roots {
99		let mut child_root = H::Out::default();
100		let storage = KeySpacedDB::new(essence, child_info.keyspace());
101
102		child_root.as_mut()[..].copy_from_slice(&root[..]);
103		let (nb, total_top, _) = count_migrate(&storage, &child_root)?;
104		child_remaining_to_migrate += nb;
105		total_child += total_top;
106	}
107
108	Ok(MigrationStatusResult {
109		top_remaining_to_migrate,
110		child_remaining_to_migrate,
111		total_top,
112		total_child,
113	})
114}
115
116/// Current state migration status.
117#[derive(Serialize, Deserialize, Clone)]
118#[serde(rename_all = "camelCase")]
119#[serde(deny_unknown_fields)]
120pub struct MigrationStatusResult {
121	/// Number of top items that should migrate.
122	pub top_remaining_to_migrate: u64,
123	/// Number of child items that should migrate.
124	pub child_remaining_to_migrate: u64,
125	/// Number of top items that we will iterate on.
126	pub total_top: u64,
127	/// Number of child items that we will iterate on.
128	pub total_child: u64,
129}
130
131/// Migration RPC methods.
132#[rpc(server)]
133pub trait StateMigrationApi<BlockHash> {
134	/// Check current migration state.
135	///
136	/// This call is performed locally without submitting any transactions. Thus executing this
137	/// won't change any state. Nonetheless it is a VERY costly call that should be
138	/// only exposed to trusted peers.
139	#[method(name = "state_trieMigrationStatus", with_extensions)]
140	fn call(&self, at: Option<BlockHash>) -> RpcResult<MigrationStatusResult>;
141}
142
143/// An implementation of state migration specific RPC methods.
144pub struct StateMigration<C, B, BA> {
145	client: Arc<C>,
146	backend: Arc<BA>,
147	_marker: std::marker::PhantomData<(B, BA)>,
148}
149
150impl<C, B, BA> StateMigration<C, B, BA> {
151	/// Create new state migration rpc for the given reference to the client.
152	pub fn new(client: Arc<C>, backend: Arc<BA>) -> Self {
153		StateMigration { client, backend, _marker: Default::default() }
154	}
155}
156
157impl<C, B, BA> StateMigrationApiServer<<B as BlockT>::Hash> for StateMigration<C, B, BA>
158where
159	B: BlockT,
160	C: Send + Sync + 'static + sc_client_api::HeaderBackend<B>,
161	BA: 'static + sc_client_api::backend::Backend<B>,
162{
163	fn call(
164		&self,
165		ext: &Extensions,
166		at: Option<<B as BlockT>::Hash>,
167	) -> RpcResult<MigrationStatusResult> {
168		check_if_safe(ext)?;
169
170		let hash = at.unwrap_or_else(|| self.client.info().best_hash);
171		let state = self
172			.backend
173			.state_at(hash, TrieCacheContext::Untrusted)
174			.map_err(error_into_rpc_err)?;
175		migration_status(&state).map_err(error_into_rpc_err)
176	}
177}
178
179fn error_into_rpc_err(err: impl std::fmt::Display) -> ErrorObjectOwned {
180	ErrorObject::owned(
181		ErrorCode::InternalError.code(),
182		"Error while checking migration state",
183		Some(err.to_string()),
184	)
185}