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:
Bastian Köcher
2022-11-27 16:34:07 +01:00
committed by GitHub
parent 0068716b5a
commit 0c934a9352
18 changed files with 325 additions and 100 deletions
+103 -25
View File
@@ -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,