Skip to content

Commit

Permalink
ed25519_verify: Support using dalek for historical blocks (paritytech…
Browse files Browse the repository at this point in the history
…#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>
  • Loading branch information
librelois and andresilva committed Nov 30, 2022
1 parent 10db72c commit 385446f
Show file tree
Hide file tree
Showing 17 changed files with 324 additions and 101 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 8 additions & 15 deletions client/api/src/call_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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.
Expand All @@ -56,20 +58,14 @@ 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.
///
/// 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,
Expand All @@ -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
///
Expand Down
128 changes: 103 additions & 25 deletions client/api/src/execution_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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.
Expand All @@ -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>>,
Expand All @@ -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(),
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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 {
Expand All @@ -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 _));
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down
1 change: 0 additions & 1 deletion client/finality-grandpa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,6 @@ where
"GrandpaApi_grandpa_authorities",
&[],
ExecutionStrategy::NativeElseWasm,
None,
)
.and_then(|call_result| {
Decode::decode(&mut &call_result[..]).map_err(|err| {
Expand Down
1 change: 0 additions & 1 deletion client/rpc/src/state/state_full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ where
&method,
&call_data,
self.client.execution_extensions().strategies().other,
None,
)
.map(Into::into)
})
Expand Down
2 changes: 1 addition & 1 deletion client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,14 +310,14 @@ where
executor,
spawn_handle,
config.clone(),
execution_extensions,
)?;
crate::client::Client::new(
backend,
executor,
genesis_storage,
fork_blocks,
bad_blocks,
execution_extensions,
prometheus_registry,
telemetry,
config,
Expand Down
Loading

0 comments on commit 385446f

Please sign in to comment.