Skip to content
This repository was archived by the owner on May 23, 2024. It is now read-only.

Commit 85fa0ab

Browse files
authored
Introduce code_substitute (paritytech#8898)
This introduces a new field `code_substitute` into the chain spec. This can be used to substitute the on-chain wasm starting from a given block until there is another wasm on chain (determined through the `spec_version`). This can be used to fix broken on chain wasm runtimes.
1 parent 3c2bd9a commit 85fa0ab

File tree

12 files changed

+286
-43
lines changed

12 files changed

+286
-43
lines changed

bin/node/testing/src/client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub type Backend = sc_client_db::Backend<node_primitives::Block>;
3232
/// Test client type.
3333
pub type Client = client::Client<
3434
Backend,
35-
client::LocalCallExecutor<Backend, Executor>,
35+
client::LocalCallExecutor<node_primitives::Block, Backend, Executor>,
3636
node_primitives::Block,
3737
node_runtime::RuntimeApi,
3838
>;
@@ -63,7 +63,7 @@ pub trait TestClientBuilderExt: Sized {
6363

6464
impl TestClientBuilderExt for substrate_test_client::TestClientBuilder<
6565
node_primitives::Block,
66-
client::LocalCallExecutor<Backend, Executor>,
66+
client::LocalCallExecutor<node_primitives::Block, Backend, Executor>,
6767
Backend,
6868
GenesisParameters,
6969
> {

client/chain-spec/src/chain_spec.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
use std::{borrow::Cow, fs::File, path::PathBuf, sync::Arc, collections::HashMap};
2323
use serde::{Serialize, Deserialize};
24-
use sp_core::storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild};
24+
use sp_core::{storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild}, Bytes};
2525
use sp_runtime::BuildStorage;
2626
use serde_json as json;
2727
use crate::{RuntimeGenesis, ChainType, extension::GetExtension, Properties};
@@ -160,6 +160,12 @@ struct ClientSpec<E> {
160160
#[serde(skip_serializing)]
161161
genesis: serde::de::IgnoredAny,
162162
light_sync_state: Option<SerializableLightSyncState>,
163+
/// Mapping from `block_hash` to `wasm_code`.
164+
///
165+
/// The given `wasm_code` will be used to substitute the on-chain wasm code from the given
166+
/// block hash onwards.
167+
#[serde(default)]
168+
code_substitutes: HashMap<String, Bytes>,
163169
}
164170

165171
/// A type denoting empty extensions.
@@ -249,6 +255,7 @@ impl<G, E> ChainSpec<G, E> {
249255
consensus_engine: (),
250256
genesis: Default::default(),
251257
light_sync_state: None,
258+
code_substitutes: HashMap::new(),
252259
};
253260

254261
ChainSpec {
@@ -395,6 +402,10 @@ where
395402
fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState) {
396403
ChainSpec::set_light_sync_state(self, light_sync_state)
397404
}
405+
406+
fn code_substitutes(&self) -> std::collections::HashMap<String, Vec<u8>> {
407+
self.client_spec.code_substitutes.iter().map(|(h, c)| (h.clone(), c.0.clone())).collect()
408+
}
398409
}
399410

400411
/// Hardcoded infomation that allows light clients to sync quickly.

client/chain-spec/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ pub trait ChainSpec: BuildStorage + Send + Sync {
161161
fn set_storage(&mut self, storage: Storage);
162162
/// Hardcode infomation to allow light clients to sync quickly into the chain spec.
163163
fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState);
164+
/// Returns code substitutes that should be used for the on chain wasm.
165+
fn code_substitutes(&self) -> std::collections::HashMap<String, Vec<u8>>;
164166
}
165167

166168
impl std::fmt::Debug for dyn ChainSpec {

client/service/src/builder.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use sp_runtime::traits::{
5050
};
5151
use sp_api::{ProvideRuntimeApi, CallApiAt};
5252
use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo};
53-
use std::sync::Arc;
53+
use std::{sync::Arc, str::FromStr};
5454
use wasm_timer::SystemTime;
5555
use sc_telemetry::{
5656
telemetry,
@@ -150,6 +150,7 @@ pub type TFullBackend<TBl> = sc_client_db::Backend<TBl>;
150150

151151
/// Full client call executor type.
152152
pub type TFullCallExecutor<TBl, TExecDisp> = crate::client::LocalCallExecutor<
153+
TBl,
153154
sc_client_db::Backend<TBl>,
154155
NativeExecutor<TExecDisp>,
155156
>;
@@ -172,6 +173,7 @@ pub type TLightCallExecutor<TBl, TExecDisp> = sc_light::GenesisCallExecutor<
172173
HashFor<TBl>
173174
>,
174175
crate::client::LocalCallExecutor<
176+
TBl,
175177
sc_light::Backend<
176178
sc_client_db::light::LightStorage<TBl>,
177179
HashFor<TBl>
@@ -206,7 +208,7 @@ pub type TLightClientWithBackend<TBl, TRtApi, TExecDisp, TBackend> = Client<
206208
TBackend,
207209
sc_light::GenesisCallExecutor<
208210
TBackend,
209-
crate::client::LocalCallExecutor<TBackend, NativeExecutor<TExecDisp>>,
211+
crate::client::LocalCallExecutor<TBl, TBackend, NativeExecutor<TExecDisp>>,
210212
>,
211213
TBl,
212214
TRtApi,
@@ -295,6 +297,7 @@ pub fn new_full_client<TBl, TRtApi, TExecDisp>(
295297
) -> Result<TFullClient<TBl, TRtApi, TExecDisp>, Error> where
296298
TBl: BlockT,
297299
TExecDisp: NativeExecutionDispatch + 'static,
300+
TBl::Hash: FromStr,
298301
{
299302
new_full_parts(config, telemetry).map(|parts| parts.0)
300303
}
@@ -303,9 +306,10 @@ pub fn new_full_client<TBl, TRtApi, TExecDisp>(
303306
pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
304307
config: &Configuration,
305308
telemetry: Option<TelemetryHandle>,
306-
) -> Result<TFullParts<TBl, TRtApi, TExecDisp>, Error> where
309+
) -> Result<TFullParts<TBl, TRtApi, TExecDisp>, Error> where
307310
TBl: BlockT,
308311
TExecDisp: NativeExecutionDispatch + 'static,
312+
TBl::Hash: FromStr,
309313
{
310314
let keystore_container = KeystoreContainer::new(&config.keystore)?;
311315

@@ -349,6 +353,16 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
349353
sc_offchain::OffchainDb::factory_from_backend(&*backend),
350354
);
351355

356+
let wasm_runtime_substitutes = config.chain_spec.code_substitutes().into_iter().map(|(h, c)| {
357+
let hash = TBl::Hash::from_str(&h)
358+
.map_err(|_|
359+
Error::Application(Box::from(
360+
format!("Failed to parse `{}` as block hash for code substitutes.", h)
361+
))
362+
)?;
363+
Ok((hash, c))
364+
}).collect::<Result<std::collections::HashMap<_, _>, Error>>()?;
365+
352366
let client = new_client(
353367
backend.clone(),
354368
executor,
@@ -363,6 +377,7 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
363377
offchain_worker_enabled : config.offchain_worker.enabled,
364378
offchain_indexing_api: config.offchain_worker.indexing_enabled,
365379
wasm_runtime_overrides: config.wasm_runtime_overrides.clone(),
380+
wasm_runtime_substitutes,
366381
},
367382
)?;
368383

@@ -453,11 +468,11 @@ pub fn new_client<E, Block, RA>(
453468
spawn_handle: Box<dyn SpawnNamed>,
454469
prometheus_registry: Option<Registry>,
455470
telemetry: Option<TelemetryHandle>,
456-
config: ClientConfig,
471+
config: ClientConfig<Block>,
457472
) -> Result<
458473
crate::client::Client<
459474
Backend<Block>,
460-
crate::client::LocalCallExecutor<Backend<Block>, E>,
475+
crate::client::LocalCallExecutor<Block, Backend<Block>, E>,
461476
Block,
462477
RA,
463478
>,

client/service/src/client/call_executor.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,47 +32,56 @@ use sp_core::{
3232
};
3333
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
3434
use sc_client_api::{backend, call_executor::CallExecutor};
35-
use super::{client::ClientConfig, wasm_override::WasmOverride};
35+
use super::{client::ClientConfig, wasm_override::WasmOverride, wasm_substitutes::WasmSubstitutes};
3636

3737
/// Call executor that executes methods locally, querying all required
3838
/// data from local backend.
39-
pub struct LocalCallExecutor<B, E> {
39+
pub struct LocalCallExecutor<Block: BlockT, B, E> {
4040
backend: Arc<B>,
4141
executor: E,
4242
wasm_override: Option<WasmOverride<E>>,
43+
wasm_substitutes: WasmSubstitutes<Block, E, B>,
4344
spawn_handle: Box<dyn SpawnNamed>,
44-
client_config: ClientConfig,
45+
client_config: ClientConfig<Block>,
4546
}
4647

47-
impl<B, E> LocalCallExecutor<B, E>
48+
impl<Block: BlockT, B, E> LocalCallExecutor<Block, B, E>
4849
where
49-
E: CodeExecutor + RuntimeInfo + Clone + 'static
50+
E: CodeExecutor + RuntimeInfo + Clone + 'static,
51+
B: backend::Backend<Block>,
5052
{
5153
/// Creates new instance of local call executor.
5254
pub fn new(
5355
backend: Arc<B>,
5456
executor: E,
5557
spawn_handle: Box<dyn SpawnNamed>,
56-
client_config: ClientConfig,
58+
client_config: ClientConfig<Block>,
5759
) -> sp_blockchain::Result<Self> {
5860
let wasm_override = client_config.wasm_runtime_overrides
5961
.as_ref()
6062
.map(|p| WasmOverride::new(p.clone(), executor.clone()))
6163
.transpose()?;
6264

65+
let wasm_substitutes = WasmSubstitutes::new(
66+
client_config.wasm_runtime_substitutes.clone(),
67+
executor.clone(),
68+
backend.clone(),
69+
)?;
70+
6371
Ok(LocalCallExecutor {
6472
backend,
6573
executor,
6674
wasm_override,
6775
spawn_handle,
6876
client_config,
77+
wasm_substitutes,
6978
})
7079
}
7180

7281
/// Check if local runtime code overrides are enabled and one is available
7382
/// for the given `BlockId`. If yes, return it; otherwise return the same
7483
/// `RuntimeCode` instance that was passed.
75-
fn check_override<'a, Block>(
84+
fn check_override<'a>(
7685
&'a self,
7786
onchain_code: RuntimeCode<'a>,
7887
id: &BlockId<Block>,
@@ -81,16 +90,16 @@ where
8190
Block: BlockT,
8291
B: backend::Backend<Block>,
8392
{
93+
let spec = self.runtime_version(id)?.spec_version;
8494
let code = if let Some(d) = self.wasm_override
8595
.as_ref()
86-
.map::<sp_blockchain::Result<Option<RuntimeCode>>, _>(|o| {
87-
let spec = self.runtime_version(id)?.spec_version;
88-
Ok(o.get(&spec, onchain_code.heap_pages))
89-
})
90-
.transpose()?
96+
.map(|o| o.get(&spec, onchain_code.heap_pages))
9197
.flatten() {
9298
log::debug!(target: "wasm_overrides", "using WASM override for block {}", id);
9399
d
100+
} else if let Some(s) = self.wasm_substitutes.get(spec, onchain_code.heap_pages, id) {
101+
log::debug!(target: "wasm_substitutes", "Using WASM substitute for block {:?}", id);
102+
s
94103
} else {
95104
log::debug!(
96105
target: "wasm_overrides",
@@ -104,19 +113,20 @@ where
104113
}
105114
}
106115

107-
impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
116+
impl<Block: BlockT, B, E> Clone for LocalCallExecutor<Block, B, E> where E: Clone {
108117
fn clone(&self) -> Self {
109118
LocalCallExecutor {
110119
backend: self.backend.clone(),
111120
executor: self.executor.clone(),
112121
wasm_override: self.wasm_override.clone(),
113122
spawn_handle: self.spawn_handle.clone(),
114123
client_config: self.client_config.clone(),
124+
wasm_substitutes: self.wasm_substitutes.clone(),
115125
}
116126
}
117127
}
118128

119-
impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<B, E>
129+
impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<Block, B, E>
120130
where
121131
B: backend::Backend<Block>,
122132
E: CodeExecutor + RuntimeInfo + Clone + 'static,
@@ -314,7 +324,7 @@ where
314324
}
315325
}
316326

317-
impl<B, E, Block> sp_version::GetRuntimeVersion<Block> for LocalCallExecutor<B, E>
327+
impl<Block, B, E> sp_version::GetRuntimeVersion<Block> for LocalCallExecutor<Block, B, E>
318328
where
319329
B: backend::Backend<Block>,
320330
E: CodeExecutor + RuntimeInfo + Clone + 'static,
@@ -357,11 +367,7 @@ mod tests {
357367

358368
// wasm_runtime_overrides is `None` here because we construct the
359369
// LocalCallExecutor directly later on
360-
let client_config = ClientConfig {
361-
offchain_worker_enabled: false,
362-
offchain_indexing_api: false,
363-
wasm_runtime_overrides: None,
364-
};
370+
let client_config = ClientConfig::default();
365371

366372
// client is used for the convenience of creating and inserting the genesis block.
367373
let _client = substrate_test_runtime_client::client::new_with_backend::<
@@ -383,10 +389,15 @@ mod tests {
383389

384390
let call_executor = LocalCallExecutor {
385391
backend: backend.clone(),
386-
executor,
392+
executor: executor.clone(),
387393
wasm_override: Some(overrides),
388394
spawn_handle: Box::new(TaskExecutor::new()),
389395
client_config,
396+
wasm_substitutes: WasmSubstitutes::new(
397+
Default::default(),
398+
executor.clone(),
399+
backend.clone(),
400+
).unwrap(),
390401
};
391402

392403
let check = call_executor.check_override(onchain_code, &BlockId::Number(Default::default()))

client/service/src/client/client.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ pub struct Client<B, E, Block, RA> where Block: BlockT {
118118
importing_block: RwLock<Option<Block::Hash>>,
119119
block_rules: BlockRules<Block>,
120120
execution_extensions: ExecutionExtensions<Block>,
121-
config: ClientConfig,
121+
config: ClientConfig<Block>,
122122
telemetry: Option<TelemetryHandle>,
123123
_phantom: PhantomData<RA>,
124124
}
@@ -159,10 +159,10 @@ pub fn new_in_mem<E, Block, S, RA>(
159159
prometheus_registry: Option<Registry>,
160160
telemetry: Option<TelemetryHandle>,
161161
spawn_handle: Box<dyn SpawnNamed>,
162-
config: ClientConfig,
162+
config: ClientConfig<Block>,
163163
) -> sp_blockchain::Result<Client<
164164
in_mem::Backend<Block>,
165-
LocalCallExecutor<in_mem::Backend<Block>, E>,
165+
LocalCallExecutor<Block, in_mem::Backend<Block>, E>,
166166
Block,
167167
RA
168168
>> where
@@ -183,14 +183,28 @@ pub fn new_in_mem<E, Block, S, RA>(
183183
}
184184

185185
/// Relevant client configuration items relevant for the client.
186-
#[derive(Debug,Clone,Default)]
187-
pub struct ClientConfig {
186+
#[derive(Debug, Clone)]
187+
pub struct ClientConfig<Block: BlockT> {
188188
/// Enable the offchain worker db.
189189
pub offchain_worker_enabled: bool,
190190
/// If true, allows access from the runtime to write into offchain worker db.
191191
pub offchain_indexing_api: bool,
192192
/// Path where WASM files exist to override the on-chain WASM.
193193
pub wasm_runtime_overrides: Option<PathBuf>,
194+
/// Map of WASM runtime substitute starting at the child of the given block until the runtime
195+
/// version doesn't match anymore.
196+
pub wasm_runtime_substitutes: HashMap<Block::Hash, Vec<u8>>,
197+
}
198+
199+
impl<Block: BlockT> Default for ClientConfig<Block> {
200+
fn default() -> Self {
201+
Self {
202+
offchain_worker_enabled: false,
203+
offchain_indexing_api: false,
204+
wasm_runtime_overrides: None,
205+
wasm_runtime_substitutes: HashMap::new(),
206+
}
207+
}
194208
}
195209

196210
/// Create a client with the explicitly provided backend.
@@ -204,8 +218,8 @@ pub fn new_with_backend<B, E, Block, S, RA>(
204218
spawn_handle: Box<dyn SpawnNamed>,
205219
prometheus_registry: Option<Registry>,
206220
telemetry: Option<TelemetryHandle>,
207-
config: ClientConfig,
208-
) -> sp_blockchain::Result<Client<B, LocalCallExecutor<B, E>, Block, RA>>
221+
config: ClientConfig<Block>,
222+
) -> sp_blockchain::Result<Client<B, LocalCallExecutor<Block, B, E>, Block, RA>>
209223
where
210224
E: CodeExecutor + RuntimeInfo,
211225
S: BuildStorage,
@@ -308,7 +322,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
308322
execution_extensions: ExecutionExtensions<Block>,
309323
prometheus_registry: Option<Registry>,
310324
telemetry: Option<TelemetryHandle>,
311-
config: ClientConfig,
325+
config: ClientConfig<Block>,
312326
) -> sp_blockchain::Result<Self> {
313327
if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() {
314328
let genesis_storage = build_genesis_storage.build_storage()

client/service/src/client/light.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub fn new_light<B, S, RA, E>(
4545
Backend<S, HashFor<B>>,
4646
GenesisCallExecutor<
4747
Backend<S, HashFor<B>>,
48-
LocalCallExecutor<Backend<S, HashFor<B>>, E>
48+
LocalCallExecutor<B, Backend<S, HashFor<B>>, E>
4949
>,
5050
B,
5151
RA

client/service/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ mod call_executor;
5151
mod client;
5252
mod block_rules;
5353
mod wasm_override;
54+
mod wasm_substitutes;
5455

5556
pub use self::{
5657
call_executor::LocalCallExecutor,

0 commit comments

Comments
 (0)