mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 04:11:09 +00:00
Implement runtime version checks in set_code (#4548)
* Implement runtime version checks in `set_code` Check that the new runtime code given to `set_code` fullfills some requirements: - `spec_name` matches - `spec_version` does not decreases - `impl_version` does not decreases - Either `spec_version` and `impl_version` increase * Make tests almost work * Some fixes after master merge * Fix tests * Add missed file * Make depedency check happy? * Remove leftover `sc-executor` * AHHHHH * Reset debug stuff * Remove some 'static * More 'static * Some docs * Update `Cargo.lock`
This commit is contained in:
committed by
Gavin Wood
parent
437772be9e
commit
afc3318f21
@@ -80,7 +80,7 @@ sp_externalities::decl_extension! {
|
||||
}
|
||||
|
||||
/// Code execution engine.
|
||||
pub trait CodeExecutor: Sized + Send + Sync {
|
||||
pub trait CodeExecutor: Sized + Send + Sync + CallInWasm + Clone + 'static {
|
||||
/// Externalities error type.
|
||||
type Error: Display + Debug + Send + 'static;
|
||||
|
||||
@@ -99,3 +99,30 @@ pub trait CodeExecutor: Sized + Send + Sync {
|
||||
native_call: Option<NC>,
|
||||
) -> (Result<crate::NativeOrEncoded<R>, Self::Error>, bool);
|
||||
}
|
||||
|
||||
/// Something that can call a method in a WASM blob.
|
||||
pub trait CallInWasm: Send + Sync {
|
||||
/// Call the given `method` in the given `wasm_blob` using `call_data` (SCALE encoded arguments)
|
||||
/// to decode the arguments for the method.
|
||||
///
|
||||
/// Returns the SCALE encoded return value of the method.
|
||||
fn call_in_wasm(
|
||||
&self,
|
||||
wasm_blob: &[u8],
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
ext: &mut dyn Externalities,
|
||||
) -> Result<Vec<u8>, String>;
|
||||
}
|
||||
|
||||
sp_externalities::decl_extension! {
|
||||
/// The call-in-wasm extension to register/retrieve from the externalities.
|
||||
pub struct CallInWasmExt(Box<dyn CallInWasm>);
|
||||
}
|
||||
|
||||
impl CallInWasmExt {
|
||||
/// Creates a new instance of `Self`.
|
||||
pub fn new<T: CallInWasm + 'static>(inner: T) -> Self {
|
||||
Self(Box::new(inner))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,4 @@ edition = "2018"
|
||||
[dependencies]
|
||||
sp-storage = { version = "2.0.0", path = "../storage" }
|
||||
sp-std = { version = "2.0.0", path = "../std" }
|
||||
environmental = { version = "1.0.2" }
|
||||
environmental = { version = "1.1.1" }
|
||||
|
||||
@@ -35,7 +35,7 @@ use sp_std::ops::Deref;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_core::{
|
||||
crypto::Pair,
|
||||
traits::KeystoreExt,
|
||||
traits::{KeystoreExt, CallInWasmExt},
|
||||
offchain::{OffchainExt, TransactionPoolExt},
|
||||
hexdisplay::HexDisplay,
|
||||
storage::{ChildStorageKey, ChildInfo},
|
||||
@@ -49,7 +49,7 @@ use sp_core::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use ::sp_trie::{TrieConfiguration, trie_types::Layout};
|
||||
use sp_trie::{TrieConfiguration, trie_types::Layout};
|
||||
|
||||
use sp_runtime_interface::{runtime_interface, Pointer};
|
||||
|
||||
@@ -351,6 +351,25 @@ pub trait Misc {
|
||||
fn print_hex(data: &[u8]) {
|
||||
log::debug!(target: "runtime", "{}", HexDisplay::from(&data));
|
||||
}
|
||||
|
||||
/// Extract the runtime version of the given wasm blob by calling `Core_version`.
|
||||
///
|
||||
/// Returns the SCALE encoded runtime version and `None` if the call failed.
|
||||
///
|
||||
/// # Performance
|
||||
///
|
||||
/// Calling this function is very expensive and should only be done very occasionally.
|
||||
/// For getting the runtime version, it requires instantiating the wasm blob and calling a
|
||||
/// function in this blob.
|
||||
fn runtime_version(&mut self, wasm: &[u8]) -> Option<Vec<u8>> {
|
||||
// Create some dummy externalities, `Core_version` should not write data anyway.
|
||||
let mut ext = sp_state_machine::BasicExternalities::default();
|
||||
|
||||
self.extension::<CallInWasmExt>()
|
||||
.expect("No `CallInWasmExt` associated for the current context!")
|
||||
.call_in_wasm(wasm, "Core_version", &[], &mut ext)
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Interfaces for working with crypto related types from within the runtime.
|
||||
|
||||
@@ -10,7 +10,6 @@ sp-std = { version = "2.0.0", default-features = false, path = "../std" }
|
||||
sp-runtime-interface-proc-macro = { version = "2.0.0", path = "proc-macro" }
|
||||
sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false }
|
||||
environmental = { version = "1.0.2", optional = true }
|
||||
static_assertions = "1.0.0"
|
||||
primitive-types = { version = "0.6.1", default-features = false }
|
||||
|
||||
@@ -29,7 +28,6 @@ std = [
|
||||
"sp-std/std",
|
||||
"codec/std",
|
||||
"sp-externalities",
|
||||
"environmental",
|
||||
"primitive-types/std",
|
||||
]
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ fn call_wasm_method<HF: HostFunctionsT>(method: &str) -> TestExternalities {
|
||||
let mut ext_ext = ext.ext();
|
||||
|
||||
sc_executor::call_in_wasm::<
|
||||
_,
|
||||
(
|
||||
HF,
|
||||
sp_io::SubstrateHostFunctions,
|
||||
|
||||
@@ -53,6 +53,9 @@ pub mod testing;
|
||||
pub mod traits;
|
||||
pub mod transaction_validity;
|
||||
pub mod random_number_generator;
|
||||
mod runtime_string;
|
||||
|
||||
pub use crate::runtime_string::*;
|
||||
|
||||
/// Re-export these since they're only "kind of" generic.
|
||||
pub use generic::{DigestItem, Digest};
|
||||
@@ -92,27 +95,6 @@ impl TypeId for ModuleId {
|
||||
const TYPE_ID: [u8; 4] = *b"modl";
|
||||
}
|
||||
|
||||
/// A String that is a `&'static str` on `no_std` and a `Cow<'static, str>` on `std`.
|
||||
#[cfg(feature = "std")]
|
||||
pub type RuntimeString = std::borrow::Cow<'static, str>;
|
||||
/// A String that is a `&'static str` on `no_std` and a `Cow<'static, str>` on `std`.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub type RuntimeString = &'static str;
|
||||
|
||||
/// Create a const [`RuntimeString`].
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_export]
|
||||
macro_rules! create_runtime_str {
|
||||
( $y:expr ) => {{ std::borrow::Cow::Borrowed($y) }}
|
||||
}
|
||||
|
||||
/// Create a const [`RuntimeString`].
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[macro_export]
|
||||
macro_rules! create_runtime_str {
|
||||
( $y:expr ) => {{ $y }}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use serde::{Serialize, Deserialize, de::DeserializeOwned};
|
||||
use crate::traits::IdentifyAccount;
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{Encode, Decode};
|
||||
use sp_core::RuntimeDebug;
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
/// A string that wraps a `&'static str` in the runtime and `String`/`Vec<u8>` on decode.
|
||||
#[derive(Eq, RuntimeDebug, Clone)]
|
||||
pub enum RuntimeString {
|
||||
/// The borrowed mode that wraps a `&'static str`.
|
||||
Borrowed(&'static str),
|
||||
/// The owned mode that wraps a `String`.
|
||||
#[cfg(feature = "std")]
|
||||
Owned(String),
|
||||
/// The owned mode that wraps a `Vec<u8>`.
|
||||
#[cfg(not(feature = "std"))]
|
||||
Owned(Vec<u8>),
|
||||
}
|
||||
|
||||
impl From<&'static str> for RuntimeString {
|
||||
fn from(data: &'static str) -> Self {
|
||||
Self::Borrowed(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<RuntimeString> for String {
|
||||
fn from(string: RuntimeString) -> Self {
|
||||
match string {
|
||||
RuntimeString::Borrowed(data) => data.to_owned(),
|
||||
RuntimeString::Owned(data) => data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RuntimeString {
|
||||
fn default() -> Self {
|
||||
Self::Borrowed(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RuntimeString {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_ref() == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for RuntimeString {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
match self {
|
||||
Self::Borrowed(val) => val.as_ref(),
|
||||
Self::Owned(val) => val.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for RuntimeString {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Borrowed(val) => val.encode(),
|
||||
Self::Owned(val) => val.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for RuntimeString {
|
||||
fn decode<I: codec::Input>(value: &mut I) -> Result<Self, codec::Error> {
|
||||
Decode::decode(value).map(Self::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::fmt::Display for RuntimeString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Borrowed(val) => write!(f, "{}", val),
|
||||
Self::Owned(val) => write!(f, "{}", val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl serde::Serialize for RuntimeString {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Borrowed(val) => val.serialize(serializer),
|
||||
Self::Owned(val) => val.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'de> serde::Deserialize<'de> for RuntimeString {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
|
||||
String::deserialize(de).map(Self::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a const [`RuntimeString`].
|
||||
#[macro_export]
|
||||
macro_rules! create_runtime_str {
|
||||
( $y:expr ) => {{ $crate::RuntimeString::Borrowed($y) }}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ use hash_db::Hasher;
|
||||
use codec::{Decode, Encode, Codec};
|
||||
use sp_core::{
|
||||
storage::{well_known_keys, ChildInfo}, NativeOrEncoded, NeverNativeValue,
|
||||
traits::CodeExecutor, hexdisplay::HexDisplay
|
||||
traits::{CodeExecutor, CallInWasmExt}, hexdisplay::HexDisplay
|
||||
};
|
||||
use overlayed_changes::OverlayedChangeSet;
|
||||
use sp_externalities::Extensions;
|
||||
@@ -191,7 +191,7 @@ pub struct StateMachine<'a, B, H, N, T, Exec>
|
||||
impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
B: Backend<H>,
|
||||
T: ChangesTrieStorage<H, N>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
@@ -204,8 +204,10 @@ impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
exec: &'a Exec,
|
||||
method: &'a str,
|
||||
call_data: &'a [u8],
|
||||
extensions: Extensions,
|
||||
mut extensions: Extensions,
|
||||
) -> Self {
|
||||
extensions.register(CallInWasmExt::new(exec.clone()));
|
||||
|
||||
Self {
|
||||
backend,
|
||||
exec,
|
||||
@@ -451,7 +453,7 @@ where
|
||||
B: Backend<H>,
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
{
|
||||
let trie_backend = backend.as_trie_backend()
|
||||
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
|
||||
@@ -478,7 +480,7 @@ where
|
||||
S: trie_backend_essence::TrieBackendStorage<H>,
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor,
|
||||
Exec: CodeExecutor + 'static + Clone,
|
||||
{
|
||||
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
|
||||
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, Exec>::new(
|
||||
@@ -504,7 +506,7 @@ pub fn execution_proof_check<H, Exec>(
|
||||
) -> Result<Vec<u8>, Box<dyn Error>>
|
||||
where
|
||||
H: Hasher,
|
||||
Exec: CodeExecutor,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?;
|
||||
@@ -522,7 +524,7 @@ pub fn execution_proof_check_on_trie_backend<H, Exec>(
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
{
|
||||
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, Exec>::new(
|
||||
trie_backend, None, overlay, exec, method, call_data, Extensions::default(),
|
||||
@@ -741,6 +743,7 @@ mod tests {
|
||||
};
|
||||
use sp_core::{Blake2Hasher, map, traits::Externalities, storage::ChildStorageKey};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DummyCodeExecutor {
|
||||
change_changes_trie_config: bool,
|
||||
native_available: bool,
|
||||
@@ -797,6 +800,18 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_core::traits::CallInWasm for DummyCodeExecutor {
|
||||
fn call_in_wasm(
|
||||
&self,
|
||||
_: &[u8],
|
||||
_: &str,
|
||||
_: &[u8],
|
||||
_: &mut dyn Externalities,
|
||||
) -> std::result::Result<Vec<u8>, String> {
|
||||
unimplemented!("Not required in tests.")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_works() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
impl-serde = { version = "0.2.3", optional = true }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.5", default-features = false, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.1.2", default-features = false, features = ["derive"] }
|
||||
sp-std = { version = "2.0.0", default-features = false, path = "../std" }
|
||||
sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" }
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ use std::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use codec::Encode;
|
||||
#[cfg(feature = "std")]
|
||||
use codec::Decode;
|
||||
use codec::{Encode, Decode};
|
||||
use sp_runtime::RuntimeString;
|
||||
pub use sp_runtime::create_runtime_str;
|
||||
#[doc(hidden)]
|
||||
pub use sp_std;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_runtime::{traits::Block as BlockT, generic::BlockId};
|
||||
@@ -37,25 +37,13 @@ use sp_runtime::{traits::Block as BlockT, generic::BlockId};
|
||||
/// The identity of a particular API interface that the runtime might provide.
|
||||
pub type ApiId = [u8; 8];
|
||||
|
||||
/// A vector of pairs of `ApiId` and a `u32` for version. For `"std"` builds, this
|
||||
/// is a `Cow`.
|
||||
#[cfg(feature = "std")]
|
||||
pub type ApisVec = std::borrow::Cow<'static, [(ApiId, u32)]>;
|
||||
/// A vector of pairs of `ApiId` and a `u32` for version. For `"no-std"` builds, this
|
||||
/// is just a reference.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub type ApisVec = &'static [(ApiId, u32)];
|
||||
/// A vector of pairs of `ApiId` and a `u32` for version.
|
||||
pub type ApisVec = sp_std::borrow::Cow<'static, [(ApiId, u32)]>;
|
||||
|
||||
/// Create a vector of Api declarations.
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! create_apis_vec {
|
||||
( $y:expr ) => { std::borrow::Cow::Borrowed(& $y) }
|
||||
}
|
||||
#[macro_export]
|
||||
#[cfg(not(feature = "std"))]
|
||||
macro_rules! create_apis_vec {
|
||||
( $y:expr ) => { & $y }
|
||||
( $y:expr ) => { $crate::sp_std::borrow::Cow::Borrowed(& $y) }
|
||||
}
|
||||
|
||||
/// Runtime version.
|
||||
@@ -63,8 +51,8 @@ macro_rules! create_apis_vec {
|
||||
/// This triplet have different semantics and mis-interpretation could cause problems.
|
||||
/// In particular: bug fixes should result in an increment of `spec_version` and possibly `authoring_version`,
|
||||
/// absolutely not `impl_version` since they change the semantics of the runtime.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Default, sp_runtime::RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Decode))]
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, Default, sp_runtime::RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
pub struct RuntimeVersion {
|
||||
/// Identifies the different Substrate runtimes. There'll be at least polkadot and node.
|
||||
|
||||
Reference in New Issue
Block a user