substrate_state_trie_migration_rpc/
lib.rs1use 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
74pub 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 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#[derive(Serialize, Deserialize, Clone)]
118#[serde(rename_all = "camelCase")]
119#[serde(deny_unknown_fields)]
120pub struct MigrationStatusResult {
121 pub top_remaining_to_migrate: u64,
123 pub child_remaining_to_migrate: u64,
125 pub total_top: u64,
127 pub total_child: u64,
129}
130
131#[rpc(server)]
133pub trait StateMigrationApi<BlockHash> {
134 #[method(name = "state_trieMigrationStatus", with_extensions)]
140 fn call(&self, at: Option<BlockHash>) -> RpcResult<MigrationStatusResult>;
141}
142
143pub 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 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}