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
+66 -3
View File
@@ -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()));
})
}
}