mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 01:11:10 +00:00
ed25519_verify: Support using dalek for historical blocks (#12661)
* ed25519_verify: Support using dalek for historical blocks The switch from `ed25519-dalek` to `ed25519-zebra` was actually a breaking change. `ed25519-zebra` is more permissive. To support historical blocks when syncing a chain this pull request introduces an externalities extension `UseDalekExt`. This extension is just used as a signaling mechanism to `ed25519_verify` to use `ed25519-dalek` when it is present. Together with `ExtensionBeforeBlock` it can be used to setup a node in way to sync historical blocks that require `ed25519-dalek`, because they included a transaction that verified differently as when using `ed25519-zebra`. This feature can be enabled in the following way. In the chain service file, directly after the client is created, the following code should be added: ``` use sc_client_api::ExecutorProvider; client.execution_extensions().set_extensions_factory( sc_client_api::execution_extensions::ExtensionBeforeBlock::<Block, sp_io::UseDalekExt>::new(BLOCK_NUMBER_UNTIL_DALEK_SHOULD_BE_USED) ); ``` * Fix doc * More fixes * Update client/api/src/execution_extensions.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Fix merge and warning * Fix docs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
Generated
+2
@@ -8667,6 +8667,7 @@ dependencies = [
|
||||
"sp-consensus",
|
||||
"sp-core",
|
||||
"sp-externalities",
|
||||
"sp-io",
|
||||
"sp-panic-handler",
|
||||
"sp-runtime",
|
||||
"sp-state-machine",
|
||||
@@ -9660,6 +9661,7 @@ name = "sp-io"
|
||||
version = "7.0.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"ed25519-dalek",
|
||||
"futures",
|
||||
"hash-db",
|
||||
"libsecp256k1",
|
||||
|
||||
@@ -19,13 +19,12 @@
|
||||
//! A method call executor interface.
|
||||
|
||||
use sc_executor::{RuntimeVersion, RuntimeVersionOf};
|
||||
use sp_externalities::Extensions;
|
||||
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||
use sp_state_machine::{ExecutionManager, ExecutionStrategy, OverlayedChanges, StorageProof};
|
||||
use sp_state_machine::{ExecutionStrategy, OverlayedChanges, StorageProof};
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::execution_extensions::ExecutionExtensions;
|
||||
use sp_api::{ProofRecorder, StorageTransactionCache};
|
||||
use sp_api::{ExecutionContext, ProofRecorder, StorageTransactionCache};
|
||||
|
||||
/// Executor Provider
|
||||
pub trait ExecutorProvider<Block: BlockT> {
|
||||
@@ -47,6 +46,9 @@ pub trait CallExecutor<B: BlockT>: RuntimeVersionOf {
|
||||
/// The backend used by the node.
|
||||
type Backend: crate::backend::Backend<B>;
|
||||
|
||||
/// Returns the [`ExecutionExtensions`].
|
||||
fn execution_extensions(&self) -> &ExecutionExtensions<B>;
|
||||
|
||||
/// Execute a call to a contract on top of state in a block of given hash.
|
||||
///
|
||||
/// No changes are made.
|
||||
@@ -56,7 +58,6 @@ pub trait CallExecutor<B: BlockT>: RuntimeVersionOf {
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
strategy: ExecutionStrategy,
|
||||
extensions: Option<Extensions>,
|
||||
) -> Result<Vec<u8>, sp_blockchain::Error>;
|
||||
|
||||
/// Execute a contextual call on top of state in a block of a given hash.
|
||||
@@ -64,12 +65,7 @@ pub trait CallExecutor<B: BlockT>: RuntimeVersionOf {
|
||||
/// No changes are made.
|
||||
/// Before executing the method, passed header is installed as the current header
|
||||
/// of the execution context.
|
||||
fn contextual_call<
|
||||
EM: Fn(
|
||||
Result<Vec<u8>, Self::Error>,
|
||||
Result<Vec<u8>, Self::Error>,
|
||||
) -> Result<Vec<u8>, Self::Error>,
|
||||
>(
|
||||
fn contextual_call(
|
||||
&self,
|
||||
at: &BlockId<B>,
|
||||
method: &str,
|
||||
@@ -80,12 +76,9 @@ pub trait CallExecutor<B: BlockT>: RuntimeVersionOf {
|
||||
StorageTransactionCache<B, <Self::Backend as crate::backend::Backend<B>>::State>,
|
||||
>,
|
||||
>,
|
||||
execution_manager: ExecutionManager<EM>,
|
||||
proof_recorder: &Option<ProofRecorder<B>>,
|
||||
extensions: Option<Extensions>,
|
||||
) -> sp_blockchain::Result<Vec<u8>>
|
||||
where
|
||||
ExecutionManager<EM>: Clone;
|
||||
context: ExecutionContext,
|
||||
) -> sp_blockchain::Result<Vec<u8>>;
|
||||
|
||||
/// Extract RuntimeVersion of given block
|
||||
///
|
||||
|
||||
@@ -29,12 +29,18 @@ use sp_core::{
|
||||
offchain::{self, OffchainDbExt, OffchainWorkerExt, TransactionPoolExt},
|
||||
ExecutionContext,
|
||||
};
|
||||
use sp_externalities::Extensions;
|
||||
use sp_externalities::{Extension, Extensions};
|
||||
use sp_keystore::{KeystoreExt, SyncCryptoStorePtr};
|
||||
use sp_runtime::{generic::BlockId, traits};
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, NumberFor},
|
||||
};
|
||||
pub use sp_state_machine::ExecutionStrategy;
|
||||
use sp_state_machine::{DefaultHandler, ExecutionManager};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
/// Execution strategies settings.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -63,18 +69,81 @@ impl Default for ExecutionStrategies {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the starting set of ExternalitiesExtensions based upon the given capabilities
|
||||
pub trait ExtensionsFactory: Send + Sync {
|
||||
/// Make `Extensions` for given `Capabilities`.
|
||||
fn extensions_for(&self, capabilities: offchain::Capabilities) -> Extensions;
|
||||
/// Generate the starting set of [`Extensions`].
|
||||
///
|
||||
/// These [`Extensions`] are passed to the environment a runtime is executed in.
|
||||
pub trait ExtensionsFactory<Block: BlockT>: Send + Sync {
|
||||
/// Create [`Extensions`] for the given input.
|
||||
///
|
||||
/// - `block_hash`: The hash of the block in the context that extensions will be used.
|
||||
/// - `block_number`: The number of the block in the context that extensions will be used.
|
||||
/// - `capabilities`: The capabilities
|
||||
fn extensions_for(
|
||||
&self,
|
||||
block_hash: Block::Hash,
|
||||
block_number: NumberFor<Block>,
|
||||
capabilities: offchain::Capabilities,
|
||||
) -> Extensions;
|
||||
}
|
||||
|
||||
impl ExtensionsFactory for () {
|
||||
fn extensions_for(&self, _capabilities: offchain::Capabilities) -> Extensions {
|
||||
impl<Block: BlockT> ExtensionsFactory<Block> for () {
|
||||
fn extensions_for(
|
||||
&self,
|
||||
_: Block::Hash,
|
||||
_: NumberFor<Block>,
|
||||
_capabilities: offchain::Capabilities,
|
||||
) -> Extensions {
|
||||
Extensions::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT, T: ExtensionsFactory<Block>> ExtensionsFactory<Block> for Vec<T> {
|
||||
fn extensions_for(
|
||||
&self,
|
||||
block_hash: Block::Hash,
|
||||
block_number: NumberFor<Block>,
|
||||
capabilities: offchain::Capabilities,
|
||||
) -> Extensions {
|
||||
let mut exts = Extensions::new();
|
||||
exts.extend(self.iter().map(|e| e.extensions_for(block_hash, block_number, capabilities)));
|
||||
exts
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`ExtensionsFactory`] that registers an [`Extension`] before a certain block.
|
||||
pub struct ExtensionBeforeBlock<Block: BlockT, Ext> {
|
||||
before: NumberFor<Block>,
|
||||
_marker: PhantomData<fn(Ext) -> Ext>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT, Ext> ExtensionBeforeBlock<Block, Ext> {
|
||||
/// Create the extension factory.
|
||||
///
|
||||
/// - `before`: The block number until the extension should be registered.
|
||||
pub fn new(before: NumberFor<Block>) -> Self {
|
||||
Self { before, _marker: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT, Ext: Default + Extension> ExtensionsFactory<Block>
|
||||
for ExtensionBeforeBlock<Block, Ext>
|
||||
{
|
||||
fn extensions_for(
|
||||
&self,
|
||||
_: Block::Hash,
|
||||
block_number: NumberFor<Block>,
|
||||
_: offchain::Capabilities,
|
||||
) -> Extensions {
|
||||
let mut exts = Extensions::new();
|
||||
|
||||
if block_number < self.before {
|
||||
exts.register(Ext::default());
|
||||
}
|
||||
|
||||
exts
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Offchain DB accessor object.
|
||||
pub trait DbExternalitiesFactory: Send + Sync {
|
||||
/// Create [`offchain::DbExternalities`] instance.
|
||||
@@ -92,7 +161,7 @@ impl<T: offchain::DbExternalities + Clone + Sync + Send + 'static> DbExternaliti
|
||||
/// This crate aggregates extensions available for the offchain calls
|
||||
/// and is responsible for producing a correct `Extensions` object.
|
||||
/// for each call, based on required `Capabilities`.
|
||||
pub struct ExecutionExtensions<Block: traits::Block> {
|
||||
pub struct ExecutionExtensions<Block: BlockT> {
|
||||
strategies: ExecutionStrategies,
|
||||
keystore: Option<SyncCryptoStorePtr>,
|
||||
offchain_db: Option<Box<dyn DbExternalitiesFactory>>,
|
||||
@@ -103,10 +172,10 @@ pub struct ExecutionExtensions<Block: traits::Block> {
|
||||
// That's also the reason why it's being registered lazily instead of
|
||||
// during initialization.
|
||||
transaction_pool: RwLock<Option<Weak<dyn OffchainSubmitTransaction<Block>>>>,
|
||||
extensions_factory: RwLock<Box<dyn ExtensionsFactory>>,
|
||||
extensions_factory: RwLock<Box<dyn ExtensionsFactory<Block>>>,
|
||||
}
|
||||
|
||||
impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
|
||||
impl<Block: BlockT> Default for ExecutionExtensions<Block> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
strategies: Default::default(),
|
||||
@@ -118,7 +187,7 @@ impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
impl<Block: BlockT> ExecutionExtensions<Block> {
|
||||
/// Create new `ExecutionExtensions` given a `keystore` and `ExecutionStrategies`.
|
||||
pub fn new(
|
||||
strategies: ExecutionStrategies,
|
||||
@@ -142,8 +211,8 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
}
|
||||
|
||||
/// Set the new extensions_factory
|
||||
pub fn set_extensions_factory(&self, maker: Box<dyn ExtensionsFactory>) {
|
||||
*self.extensions_factory.write() = maker;
|
||||
pub fn set_extensions_factory(&self, maker: impl ExtensionsFactory<Block> + 'static) {
|
||||
*self.extensions_factory.write() = Box::new(maker);
|
||||
}
|
||||
|
||||
/// Register transaction pool extension.
|
||||
@@ -156,10 +225,18 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
|
||||
/// Based on the execution context and capabilities it produces
|
||||
/// the extensions object to support desired set of APIs.
|
||||
pub fn extensions(&self, at: &BlockId<Block>, context: ExecutionContext) -> Extensions {
|
||||
pub fn extensions(
|
||||
&self,
|
||||
block_hash: Block::Hash,
|
||||
block_number: NumberFor<Block>,
|
||||
context: ExecutionContext,
|
||||
) -> Extensions {
|
||||
let capabilities = context.capabilities();
|
||||
|
||||
let mut extensions = self.extensions_factory.read().extensions_for(capabilities);
|
||||
let mut extensions =
|
||||
self.extensions_factory
|
||||
.read()
|
||||
.extensions_for(block_hash, block_number, capabilities);
|
||||
|
||||
if capabilities.contains(offchain::Capabilities::KEYSTORE) {
|
||||
if let Some(ref keystore) = self.keystore {
|
||||
@@ -169,10 +246,10 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
|
||||
if capabilities.contains(offchain::Capabilities::TRANSACTION_POOL) {
|
||||
if let Some(pool) = self.transaction_pool.read().as_ref().and_then(|x| x.upgrade()) {
|
||||
extensions
|
||||
.register(TransactionPoolExt(
|
||||
Box::new(TransactionPoolAdapter { at: *at, pool }) as _,
|
||||
));
|
||||
extensions.register(TransactionPoolExt(Box::new(TransactionPoolAdapter {
|
||||
at: BlockId::Hash(block_hash),
|
||||
pool,
|
||||
}) as _));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +280,8 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
/// the right manager and extensions object to support desired set of APIs.
|
||||
pub fn manager_and_extensions<E: std::fmt::Debug>(
|
||||
&self,
|
||||
at: &BlockId<Block>,
|
||||
block_hash: Block::Hash,
|
||||
block_number: NumberFor<Block>,
|
||||
context: ExecutionContext,
|
||||
) -> (ExecutionManager<DefaultHandler<E>>, Extensions) {
|
||||
let manager = match context {
|
||||
@@ -215,17 +293,17 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
ExecutionContext::OffchainCall(_) => self.strategies.other.get_manager(),
|
||||
};
|
||||
|
||||
(manager, self.extensions(at, context))
|
||||
(manager, self.extensions(block_hash, block_number, context))
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type to pass `BlockId` to the actual transaction pool.
|
||||
struct TransactionPoolAdapter<Block: traits::Block> {
|
||||
struct TransactionPoolAdapter<Block: BlockT> {
|
||||
at: BlockId<Block>,
|
||||
pool: Arc<dyn OffchainSubmitTransaction<Block>>,
|
||||
}
|
||||
|
||||
impl<Block: traits::Block> offchain::TransactionPool for TransactionPoolAdapter<Block> {
|
||||
impl<Block: BlockT> offchain::TransactionPool for TransactionPoolAdapter<Block> {
|
||||
fn submit_transaction(&mut self, data: Vec<u8>) -> Result<(), ()> {
|
||||
let xt = match Block::Extrinsic::decode(&mut &*data) {
|
||||
Ok(xt) => xt,
|
||||
|
||||
@@ -477,7 +477,6 @@ where
|
||||
"GrandpaApi_grandpa_authorities",
|
||||
&[],
|
||||
ExecutionStrategy::NativeElseWasm,
|
||||
None,
|
||||
)
|
||||
.and_then(|call_result| {
|
||||
Decode::decode(&mut &call_result[..]).map_err(|err| {
|
||||
|
||||
@@ -200,7 +200,6 @@ where
|
||||
&method,
|
||||
&call_data,
|
||||
self.client.execution_extensions().strategies().other,
|
||||
None,
|
||||
)
|
||||
.map(Into::into)
|
||||
})
|
||||
|
||||
@@ -311,6 +311,7 @@ where
|
||||
executor,
|
||||
spawn_handle,
|
||||
config.clone(),
|
||||
execution_extensions,
|
||||
)?;
|
||||
crate::client::Client::new(
|
||||
backend,
|
||||
@@ -318,7 +319,6 @@ where
|
||||
genesis_storage,
|
||||
fork_blocks,
|
||||
bad_blocks,
|
||||
execution_extensions,
|
||||
prometheus_registry,
|
||||
telemetry,
|
||||
config,
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{client::ClientConfig, wasm_override::WasmOverride, wasm_substitutes::WasmSubstitutes};
|
||||
use sc_client_api::{backend, call_executor::CallExecutor, HeaderBackend};
|
||||
use sc_client_api::{
|
||||
backend, call_executor::CallExecutor, execution_extensions::ExecutionExtensions, HeaderBackend,
|
||||
};
|
||||
use sc_executor::{RuntimeVersion, RuntimeVersionOf};
|
||||
use sp_api::{ProofRecorder, StorageTransactionCache};
|
||||
use sp_api::{ExecutionContext, ProofRecorder, StorageTransactionCache};
|
||||
use sp_core::traits::{CodeExecutor, RuntimeCode, SpawnNamed};
|
||||
use sp_externalities::Extensions;
|
||||
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||
use sp_state_machine::{
|
||||
backend::AsTrieBackend, ExecutionManager, ExecutionStrategy, Ext, OverlayedChanges,
|
||||
StateMachine, StorageProof,
|
||||
backend::AsTrieBackend, ExecutionStrategy, Ext, OverlayedChanges, StateMachine, StorageProof,
|
||||
};
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct LocalCallExecutor<Block: BlockT, B, E> {
|
||||
wasm_substitutes: WasmSubstitutes<Block, E, B>,
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
client_config: ClientConfig<Block>,
|
||||
execution_extensions: Arc<ExecutionExtensions<Block>>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT, B, E> LocalCallExecutor<Block, B, E>
|
||||
@@ -51,6 +52,7 @@ where
|
||||
executor: E,
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
client_config: ClientConfig<Block>,
|
||||
execution_extensions: ExecutionExtensions<Block>,
|
||||
) -> sp_blockchain::Result<Self> {
|
||||
let wasm_override = client_config
|
||||
.wasm_runtime_overrides
|
||||
@@ -71,6 +73,7 @@ where
|
||||
spawn_handle,
|
||||
client_config,
|
||||
wasm_substitutes,
|
||||
execution_extensions: Arc::new(execution_extensions),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -124,6 +127,7 @@ where
|
||||
spawn_handle: self.spawn_handle.clone(),
|
||||
client_config: self.client_config.clone(),
|
||||
wasm_substitutes: self.wasm_substitutes.clone(),
|
||||
execution_extensions: self.execution_extensions.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,30 +142,41 @@ where
|
||||
|
||||
type Backend = B;
|
||||
|
||||
fn execution_extensions(&self) -> &ExecutionExtensions<Block> {
|
||||
&self.execution_extensions
|
||||
}
|
||||
|
||||
fn call(
|
||||
&self,
|
||||
at: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
strategy: ExecutionStrategy,
|
||||
extensions: Option<Extensions>,
|
||||
) -> sp_blockchain::Result<Vec<u8>> {
|
||||
let mut changes = OverlayedChanges::default();
|
||||
let at_hash = self.backend.blockchain().expect_block_hash_from_id(at)?;
|
||||
let at_number = self.backend.blockchain().expect_block_number_from_id(at)?;
|
||||
let state = self.backend.state_at(at_hash)?;
|
||||
|
||||
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
|
||||
let runtime_code =
|
||||
state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?;
|
||||
|
||||
let runtime_code = self.check_override(runtime_code, at)?;
|
||||
|
||||
let extensions = self.execution_extensions.extensions(
|
||||
at_hash,
|
||||
at_number,
|
||||
ExecutionContext::OffchainCall(None),
|
||||
);
|
||||
|
||||
let mut sm = StateMachine::new(
|
||||
&state,
|
||||
&mut changes,
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
extensions.unwrap_or_default(),
|
||||
extensions,
|
||||
&runtime_code,
|
||||
self.spawn_handle.clone(),
|
||||
)
|
||||
@@ -171,30 +186,25 @@ where
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn contextual_call<
|
||||
EM: Fn(
|
||||
Result<Vec<u8>, Self::Error>,
|
||||
Result<Vec<u8>, Self::Error>,
|
||||
) -> Result<Vec<u8>, Self::Error>,
|
||||
>(
|
||||
fn contextual_call(
|
||||
&self,
|
||||
at: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
storage_transaction_cache: Option<&RefCell<StorageTransactionCache<Block, B::State>>>,
|
||||
execution_manager: ExecutionManager<EM>,
|
||||
recorder: &Option<ProofRecorder<Block>>,
|
||||
extensions: Option<Extensions>,
|
||||
) -> Result<Vec<u8>, sp_blockchain::Error>
|
||||
where
|
||||
ExecutionManager<EM>: Clone,
|
||||
{
|
||||
context: ExecutionContext,
|
||||
) -> Result<Vec<u8>, sp_blockchain::Error> {
|
||||
let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut());
|
||||
|
||||
let at_hash = self.backend.blockchain().expect_block_hash_from_id(at)?;
|
||||
let at_number = self.backend.blockchain().expect_block_number_from_id(at)?;
|
||||
let state = self.backend.state_at(at_hash)?;
|
||||
|
||||
let (execution_manager, extensions) =
|
||||
self.execution_extensions.manager_and_extensions(at_hash, at_number, context);
|
||||
|
||||
let changes = &mut *changes.borrow_mut();
|
||||
|
||||
// It is important to extract the runtime code here before we create the proof
|
||||
@@ -220,7 +230,7 @@ where
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
extensions.unwrap_or_default(),
|
||||
extensions,
|
||||
&runtime_code,
|
||||
self.spawn_handle.clone(),
|
||||
)
|
||||
@@ -235,7 +245,7 @@ where
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
extensions.unwrap_or_default(),
|
||||
extensions,
|
||||
&runtime_code,
|
||||
self.spawn_handle.clone(),
|
||||
)
|
||||
@@ -269,6 +279,7 @@ where
|
||||
call_data: &[u8],
|
||||
) -> sp_blockchain::Result<(Vec<u8>, StorageProof)> {
|
||||
let at_hash = self.backend.blockchain().expect_block_hash_from_id(at)?;
|
||||
let at_number = self.backend.blockchain().expect_block_number_from_id(at)?;
|
||||
let state = self.backend.state_at(at_hash)?;
|
||||
|
||||
let trie_backend = state.as_trie_backend();
|
||||
@@ -286,6 +297,11 @@ where
|
||||
method,
|
||||
call_data,
|
||||
&runtime_code,
|
||||
self.execution_extensions.extensions(
|
||||
at_hash,
|
||||
at_number,
|
||||
ExecutionContext::OffchainCall(None),
|
||||
),
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -392,6 +408,11 @@ mod tests {
|
||||
backend.clone(),
|
||||
)
|
||||
.unwrap(),
|
||||
execution_extensions: Arc::new(ExecutionExtensions::new(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
};
|
||||
|
||||
let check = call_executor
|
||||
|
||||
@@ -67,7 +67,8 @@ use sp_keystore::SyncCryptoStorePtr;
|
||||
use sp_runtime::{
|
||||
generic::{BlockId, SignedBlock},
|
||||
traits::{
|
||||
Block as BlockT, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion, Zero,
|
||||
Block as BlockT, BlockIdTo, HashFor, Header as HeaderT, NumberFor, One,
|
||||
SaturatedConversion, Zero,
|
||||
},
|
||||
BuildStorage, Digest, Justification, Justifications, StateVersion,
|
||||
};
|
||||
@@ -113,7 +114,6 @@ where
|
||||
// Holds the block hash currently being imported. TODO: replace this with block queue.
|
||||
importing_block: RwLock<Option<Block::Hash>>,
|
||||
block_rules: BlockRules<Block>,
|
||||
execution_extensions: ExecutionExtensions<Block>,
|
||||
config: ClientConfig<Block>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
_phantom: PhantomData<RA>,
|
||||
@@ -230,20 +230,26 @@ where
|
||||
Block: BlockT,
|
||||
B: backend::LocalBackend<Block> + 'static,
|
||||
{
|
||||
let call_executor =
|
||||
LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?;
|
||||
let extensions = ExecutionExtensions::new(
|
||||
Default::default(),
|
||||
keystore,
|
||||
sc_offchain::OffchainDb::factory_from_backend(&*backend),
|
||||
);
|
||||
|
||||
let call_executor = LocalCallExecutor::new(
|
||||
backend.clone(),
|
||||
executor,
|
||||
spawn_handle,
|
||||
config.clone(),
|
||||
extensions,
|
||||
)?;
|
||||
|
||||
Client::new(
|
||||
backend,
|
||||
call_executor,
|
||||
build_genesis_storage,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
extensions,
|
||||
prometheus_registry,
|
||||
telemetry,
|
||||
config,
|
||||
@@ -347,7 +353,6 @@ where
|
||||
build_genesis_storage: &dyn BuildStorage,
|
||||
fork_blocks: ForkBlocks<Block>,
|
||||
bad_blocks: BadBlocks<Block>,
|
||||
execution_extensions: ExecutionExtensions<Block>,
|
||||
prometheus_registry: Option<Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
config: ClientConfig<Block>,
|
||||
@@ -394,7 +399,6 @@ where
|
||||
finality_actions: Default::default(),
|
||||
importing_block: Default::default(),
|
||||
block_rules: BlockRules::new(fork_blocks, bad_blocks),
|
||||
execution_extensions,
|
||||
config,
|
||||
telemetry,
|
||||
_phantom: Default::default(),
|
||||
@@ -1386,7 +1390,7 @@ where
|
||||
}
|
||||
|
||||
fn execution_extensions(&self) -> &ExecutionExtensions<Block> {
|
||||
&self.execution_extensions
|
||||
self.executor.execution_extensions()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1580,7 +1584,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block, RA> sp_runtime::traits::BlockIdTo<Block> for Client<B, E, Block, RA>
|
||||
impl<B, E, Block, RA> BlockIdTo<Block> for Client<B, E, Block, RA>
|
||||
where
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block> + Send + Sync,
|
||||
@@ -1637,7 +1641,7 @@ where
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block, Backend = B> + Send + Sync,
|
||||
Block: BlockT,
|
||||
RA: ConstructRuntimeApi<Block, Self>,
|
||||
RA: ConstructRuntimeApi<Block, Self> + Send + Sync,
|
||||
{
|
||||
type Api = <RA as ConstructRuntimeApi<Block, Self>>::RuntimeApi;
|
||||
|
||||
@@ -1651,6 +1655,7 @@ where
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block, Backend = B> + Send + Sync,
|
||||
Block: BlockT,
|
||||
RA: Send + Sync,
|
||||
{
|
||||
type StateBackend = B::State;
|
||||
|
||||
@@ -1658,21 +1663,15 @@ where
|
||||
&self,
|
||||
params: CallApiAtParams<Block, B::State>,
|
||||
) -> Result<Vec<u8>, sp_api::ApiError> {
|
||||
let at = params.at;
|
||||
|
||||
let (manager, extensions) =
|
||||
self.execution_extensions.manager_and_extensions(at, params.context);
|
||||
|
||||
self.executor
|
||||
.contextual_call(
|
||||
at,
|
||||
params.at,
|
||||
params.function,
|
||||
¶ms.arguments,
|
||||
params.overlayed_changes,
|
||||
Some(params.storage_transaction_cache),
|
||||
manager,
|
||||
params.recorder,
|
||||
Some(extensions),
|
||||
params.context,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -40,5 +40,6 @@ sp-state-machine = { version = "0.13.0", path = "../../../primitives/state-machi
|
||||
sp-storage = { version = "7.0.0", path = "../../../primitives/storage" }
|
||||
sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" }
|
||||
sp-trie = { version = "7.0.0", path = "../../../primitives/trie" }
|
||||
sp-io = { version = "7.0.0", path = "../../../primitives/io" }
|
||||
substrate-test-runtime = { version = "2.0.0", path = "../../../test-utils/runtime" }
|
||||
substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
|
||||
|
||||
@@ -20,7 +20,8 @@ use futures::executor::block_on;
|
||||
use parity_scale_codec::{Decode, Encode, Joiner};
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_client_api::{
|
||||
in_mem, BlockBackend, BlockchainEvents, FinalityNotifications, HeaderBackend, StorageProvider,
|
||||
in_mem, BlockBackend, BlockchainEvents, ExecutorProvider, FinalityNotifications, HeaderBackend,
|
||||
StorageProvider,
|
||||
};
|
||||
use sc_client_db::{Backend, BlocksPruning, DatabaseSettings, DatabaseSource, PruningMode};
|
||||
use sc_consensus::{
|
||||
@@ -1875,3 +1876,40 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi
|
||||
let tree_route = notification.tree_route.unwrap();
|
||||
assert_eq!(tree_route.enacted()[0].hash, b1.hash());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_dalek_ext_works() {
|
||||
fn zero_ed_pub() -> sp_core::ed25519::Public {
|
||||
sp_core::ed25519::Public([0u8; 32])
|
||||
}
|
||||
|
||||
fn zero_ed_sig() -> sp_core::ed25519::Signature {
|
||||
sp_core::ed25519::Signature::from_raw([0u8; 64])
|
||||
}
|
||||
|
||||
let mut client = TestClientBuilder::new().build();
|
||||
|
||||
client.execution_extensions().set_extensions_factory(
|
||||
sc_client_api::execution_extensions::ExtensionBeforeBlock::<Block, sp_io::UseDalekExt>::new(
|
||||
1,
|
||||
),
|
||||
);
|
||||
|
||||
let a1 = client
|
||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block;
|
||||
block_on(client.import(BlockOrigin::NetworkInitialSync, a1.clone())).unwrap();
|
||||
|
||||
// On block zero it will use dalek and then on block 1 it will use zebra
|
||||
assert!(!client
|
||||
.runtime_api()
|
||||
.verify_ed25519(&BlockId::Number(0), zero_ed_sig(), zero_ed_pub(), vec![])
|
||||
.unwrap());
|
||||
assert!(client
|
||||
.runtime_api()
|
||||
.verify_ed25519(&BlockId::Number(1), zero_ed_sig(), zero_ed_pub(), vec![])
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
@@ -625,11 +625,7 @@ mod tests {
|
||||
let _guard2 = span2.enter();
|
||||
// emit event
|
||||
tracing::event!(target: "test_target", tracing::Level::INFO, "test_event1");
|
||||
for msg in rx.recv() {
|
||||
if !msg {
|
||||
break
|
||||
}
|
||||
}
|
||||
let _ = rx.recv();
|
||||
// guard2 and span2 dropped / exited
|
||||
});
|
||||
|
||||
|
||||
@@ -157,8 +157,8 @@ pub trait RuntimeAppPublic: Sized {
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Something that bound to a fixed `RuntimeAppPublic`.
|
||||
/// Something that bound to a fixed [`RuntimeAppPublic`].
|
||||
pub trait BoundToRuntimeAppPublic {
|
||||
/// The `RuntimeAppPublic` this type is bound to.
|
||||
/// The [`RuntimeAppPublic`] this type is bound to.
|
||||
type Public: RuntimeAppPublic;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,19 @@ macro_rules! decl_extension {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
$( #[ $attr:meta ] )*
|
||||
$vis:vis struct $ext_name:ident;
|
||||
) => {
|
||||
$( #[ $attr ] )*
|
||||
$vis struct $ext_name;
|
||||
|
||||
impl $crate::Extension for $ext_name {
|
||||
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +125,7 @@ pub trait ExtensionStore {
|
||||
extension: Box<dyn Extension>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Deregister extension with speicifed 'type_id' and drop it.
|
||||
/// Deregister extension with specified 'type_id' and drop it.
|
||||
///
|
||||
/// It should return error if extension is not registered.
|
||||
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), Error>;
|
||||
@@ -179,6 +192,13 @@ impl Extensions {
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<Extensions> for Extensions {
|
||||
fn extend<T: IntoIterator<Item = Extensions>>(&mut self, iter: T) {
|
||||
iter.into_iter()
|
||||
.for_each(|ext| self.extensions.extend(ext.extensions.into_iter()));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -34,6 +34,7 @@ parking_lot = { version = "0.12.1", optional = true }
|
||||
secp256k1 = { version = "0.24.0", features = ["recovery", "global-context"], optional = true }
|
||||
tracing = { version = "0.1.29", default-features = false }
|
||||
tracing-core = { version = "0.1.28", default-features = false}
|
||||
ed25519-dalek = { version = "1.0.1", default-features = false, optional = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -57,6 +58,7 @@ std = [
|
||||
"log",
|
||||
"futures",
|
||||
"parking_lot",
|
||||
"ed25519-dalek",
|
||||
]
|
||||
|
||||
with-tracing = [
|
||||
|
||||
@@ -698,6 +698,34 @@ pub trait Misc {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
sp_externalities::decl_extension! {
|
||||
/// Extension to signal to [`crypt::ed25519_verify`] to use the dalek crate.
|
||||
///
|
||||
/// The switch from `ed25519-dalek` to `ed25519-zebra` was a breaking change.
|
||||
/// `ed25519-zebra` is more permissive when it comes to the verification of signatures.
|
||||
/// This means that some chains may fail to sync from genesis when using `ed25519-zebra`.
|
||||
/// So, this extension can be registered to the runtime execution environment to signal
|
||||
/// that `ed25519-dalek` should be used for verification. The extension can be registered
|
||||
/// in the following way:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// client.execution_extensions().set_extensions_factory(
|
||||
/// // Let the `UseDalekExt` extension being registered for each runtime invocation
|
||||
/// // until the execution happens in the context of block `1000`.
|
||||
/// sc_client_api::execution_extensions::ExtensionBeforeBlock::<Block, UseDalekExt>::new(1000)
|
||||
/// );
|
||||
/// ```
|
||||
pub struct UseDalekExt;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Default for UseDalekExt {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Interfaces for working with crypto related types from within the runtime.
|
||||
#[runtime_interface]
|
||||
pub trait Crypto {
|
||||
@@ -747,13 +775,32 @@ pub trait Crypto {
|
||||
///
|
||||
/// Returns `true` when the verification was successful.
|
||||
fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pub_key: &ed25519::Public) -> bool {
|
||||
ed25519::Pair::verify(sig, msg, pub_key)
|
||||
// We don't want to force everyone needing to call the function in an externalities context.
|
||||
// So, we assume that we should not use dalek when we are not in externalities context.
|
||||
// Otherwise, we check if the extension is present.
|
||||
if sp_externalities::with_externalities(|mut e| e.extension::<UseDalekExt>().is_some())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
use ed25519_dalek::Verifier;
|
||||
|
||||
let public_key = if let Ok(vk) = ed25519_dalek::PublicKey::from_bytes(&pub_key.0) {
|
||||
vk
|
||||
} else {
|
||||
return false
|
||||
};
|
||||
|
||||
let sig = ed25519_dalek::Signature::from(sig.0);
|
||||
|
||||
public_key.verify(msg, &sig).is_ok()
|
||||
} else {
|
||||
ed25519::Pair::verify(sig, msg, pub_key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a `ed25519` signature for batch verification.
|
||||
///
|
||||
/// Batch verification must be enabled by calling [`start_batch_verify`].
|
||||
/// If batch verification is not enabled, the signature will be verified immediatley.
|
||||
/// If batch verification is not enabled, the signature will be verified immediately.
|
||||
/// To get the result of the batch verification, [`finish_batch_verify`]
|
||||
/// needs to be called.
|
||||
///
|
||||
@@ -780,7 +827,7 @@ pub trait Crypto {
|
||||
/// Register a `sr25519` signature for batch verification.
|
||||
///
|
||||
/// Batch verification must be enabled by calling [`start_batch_verify`].
|
||||
/// If batch verification is not enabled, the signature will be verified immediatley.
|
||||
/// If batch verification is not enabled, the signature will be verified immediately.
|
||||
/// To get the result of the batch verification, [`finish_batch_verify`]
|
||||
/// needs to be called.
|
||||
///
|
||||
@@ -1977,4 +2024,20 @@ mod tests {
|
||||
assert!(!crypto::finish_batch_verify());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_dalek_ext_works() {
|
||||
let mut ext = BasicExternalities::default();
|
||||
ext.register_extension(UseDalekExt::default());
|
||||
|
||||
// With dalek the zero signature should fail to verify.
|
||||
ext.execute_with(|| {
|
||||
assert!(!crypto::ed25519_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()));
|
||||
});
|
||||
|
||||
// But with zebra it should work.
|
||||
BasicExternalities::default().execute_with(|| {
|
||||
assert!(crypto::ed25519_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +532,7 @@ mod execution {
|
||||
method,
|
||||
call_data,
|
||||
runtime_code,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -552,6 +553,7 @@ mod execution {
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
runtime_code: &RuntimeCode,
|
||||
extensions: Extensions,
|
||||
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
|
||||
where
|
||||
S: trie_backend_essence::TrieBackendStorage<H>,
|
||||
@@ -569,7 +571,7 @@ mod execution {
|
||||
exec,
|
||||
method,
|
||||
call_data,
|
||||
Extensions::default(),
|
||||
extensions,
|
||||
runtime_code,
|
||||
spawn_handle,
|
||||
)
|
||||
|
||||
@@ -229,11 +229,6 @@ impl<Block: BlockT, ExecutorDispatch, Backend, G: GenesisInit>
|
||||
&storage,
|
||||
self.fork_blocks,
|
||||
self.bad_blocks,
|
||||
ExecutionExtensions::new(
|
||||
self.execution_strategies,
|
||||
self.keystore,
|
||||
sc_offchain::OffchainDb::factory_from_backend(&*self.backend),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
ClientConfig {
|
||||
@@ -285,6 +280,11 @@ impl<Block: BlockT, D, Backend, G: GenesisInit>
|
||||
executor,
|
||||
Box::new(sp_core::testing::TaskExecutor::new()),
|
||||
Default::default(),
|
||||
ExecutionExtensions::new(
|
||||
self.execution_strategies.clone(),
|
||||
self.keystore.clone(),
|
||||
sc_offchain::OffchainDb::factory_from_backend(&*self.backend),
|
||||
),
|
||||
)
|
||||
.expect("Creates LocalCallExecutor");
|
||||
|
||||
|
||||
@@ -378,6 +378,8 @@ cfg_if! {
|
||||
fn test_multiple_arguments(data: Vec<u8>, other: Vec<u8>, num: u32);
|
||||
/// Traces log "Hey I'm runtime."
|
||||
fn do_trace_log();
|
||||
/// Verify the given signature, public & message bundle.
|
||||
fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec<u8>) -> bool;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -428,6 +430,8 @@ cfg_if! {
|
||||
fn test_multiple_arguments(data: Vec<u8>, other: Vec<u8>, num: u32);
|
||||
/// Traces log "Hey I'm runtime."
|
||||
fn do_trace_log();
|
||||
/// Verify the given signature, public & message bundle.
|
||||
fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec<u8>) -> bool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -863,6 +867,10 @@ cfg_if! {
|
||||
fn do_trace_log() {
|
||||
log::trace!("Hey I'm runtime");
|
||||
}
|
||||
|
||||
fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec<u8>) -> bool {
|
||||
sp_io::crypto::ed25519_verify(&sig, &message, &public)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
|
||||
@@ -1137,6 +1145,10 @@ cfg_if! {
|
||||
fn do_trace_log() {
|
||||
log::trace!("Hey I'm runtime: {}", log::STATIC_MAX_LEVEL);
|
||||
}
|
||||
|
||||
fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec<u8>) -> bool {
|
||||
sp_io::crypto::ed25519_verify(&sig, &message, &public)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
|
||||
|
||||
Reference in New Issue
Block a user