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
+1 -1
View File
@@ -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
+17 -18
View File
@@ -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,
&params.arguments,
params.overlayed_changes,
Some(params.storage_transaction_cache),
manager,
params.recorder,
Some(extensions),
params.context,
)
.map_err(Into::into)
}