Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 3940128

Browse files
NikVolfchemebkchr
committed
Batch signature verification (#5023)
* create parallel tasks extension * make type system happy * basic externalities * test for dynamic extensions * batching test * remove premature verify_batch * shnschnorrkel batch * alter test * shnschnorrkel test * executive batching * some docs * also multi/any signatgures * error propagation * styling * make verification extension optional * experimental ed25519 parallelization * some merge fallout * utilize task executor * merge fallout * utilize task executor more * another merge fallout * feature-gate sp-io * arrange toml * fix no-std * sr25519 batching and refactoring * add docs * fix name * add newline * fix block import test * long sr25519 test * blocking instead of parking * move everything in crypto * return batch_verify to check :) * use condvars * use multi-threaded executor for benches * don't call via host interface * try no spawning * add true * cleanup * straighten batching * remove signature check from this test (?) * remove now pointless test * remove another now useless test * fix warnings * Revert "remove another now useless test" This reverts commit bbdec24. * rethink the sp-io-part * Revert "remove now pointless test" This reverts commit 4d55306. * fix wording * add wording * add todo and fix * return check and fix * add logging in sp-io * Update primitives/io/src/batch_verifier.rs Co-Authored-By: cheme <[email protected]> * address review and use std condvar * account for early exit * address reivew * address review * more suggestions * add docs for batch verification * remove unused * more review suggestions * move to sp-runtime * add expects * remove blocks * use entry * Update primitives/io/src/batch_verifier.rs Co-Authored-By: Bastian Köcher <[email protected]> * Update primitives/externalities/src/extensions.rs Co-Authored-By: Bastian Köcher <[email protected]> * update overlooked note * remove stupid return * Update primitives/io/src/lib.rs Co-Authored-By: Bastian Köcher <[email protected]> * Update primitives/io/src/lib.rs Co-Authored-By: Bastian Köcher <[email protected]> * fix wording * bump spec_version Co-authored-by: cheme <[email protected]> Co-authored-by: Bastian Köcher <[email protected]> # Conflicts: # bin/node/runtime/src/lib.rs # frame/executive/Cargo.toml
1 parent 0132d52 commit 3940128

File tree

21 files changed

+712
-48
lines changed

21 files changed

+712
-48
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/node/runtime/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
127127
// and set impl_version to 0. If only runtime
128128
// implementation changes and behavior does not, then leave spec_version as
129129
// is and increment impl_version.
130-
spec_version: 240,
131-
impl_version: 1,
130+
spec_version: 242,
131+
impl_version: 0,
132132
apis: RUNTIME_API_VERSIONS,
133133
};
134134

bin/node/testing/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ sp-blockchain = { version = "2.0.0-alpha.5", path = "../../../primitives/blockch
4747
log = "0.4.8"
4848
tempfile = "3.1.0"
4949
fs_extra = "1"
50+
futures = "0.3.1"
5051

5152
[dev-dependencies]
5253
criterion = "0.3.0"

bin/node/testing/src/bench.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use node_runtime::{
4747
AccountId,
4848
Signature,
4949
};
50-
use sp_core::{ExecutionContext, blake2_256};
50+
use sp_core::{ExecutionContext, blake2_256, traits::CloneableSpawn};
5151
use sp_api::ProvideRuntimeApi;
5252
use sp_block_builder::BlockBuilder;
5353
use sp_inherents::InherentData;
@@ -57,6 +57,7 @@ use sc_client_api::{
5757
};
5858
use sp_core::{Pair, Public, sr25519, ed25519};
5959
use sc_block_builder::BlockBuilderProvider;
60+
use futures::{executor, task};
6061

6162
/// Keyring full of accounts for benching.
6263
///
@@ -142,6 +143,36 @@ impl BlockType {
142143
}
143144
}
144145

146+
/// Benchmarking task executor.
147+
///
148+
/// Uses multiple threads as the regular executable.
149+
#[derive(Debug, Clone)]
150+
pub struct TaskExecutor {
151+
pool: executor::ThreadPool,
152+
}
153+
154+
impl TaskExecutor {
155+
fn new() -> Self {
156+
Self {
157+
pool: executor::ThreadPool::new()
158+
.expect("Failed to create task executor")
159+
}
160+
}
161+
}
162+
163+
impl task::Spawn for TaskExecutor {
164+
fn spawn_obj(&self, future: task::FutureObj<'static, ()>)
165+
-> Result<(), task::SpawnError> {
166+
self.pool.spawn_obj(future)
167+
}
168+
}
169+
170+
impl CloneableSpawn for TaskExecutor {
171+
fn clone(&self) -> Box<dyn CloneableSpawn> {
172+
Box::new(Clone::clone(self))
173+
}
174+
}
175+
145176
impl BenchDb {
146177
/// New immutable benchmarking database.
147178
///
@@ -168,8 +199,8 @@ impl BenchDb {
168199
/// and keep it there until struct is dropped.
169200
///
170201
/// You can `clone` this database or you can `create_context` from it
171-
/// (which also do `clone`) to run actual operation against new database
172-
/// which will be identical to this.
202+
/// (which also does `clone`) to run actual operation against new database
203+
/// which will be identical to the original.
173204
pub fn new(keyring_length: usize) -> Self {
174205
Self::with_key_types(keyring_length, KeyTypes::Sr25519)
175206
}
@@ -197,7 +228,7 @@ impl BenchDb {
197228
None,
198229
None,
199230
ExecutionExtensions::new(profile.into_execution_strategies(), None),
200-
sp_core::tasks::executor(),
231+
Box::new(TaskExecutor::new()),
201232
None,
202233
).expect("Should not fail");
203234

client/transaction-pool/src/testing/pool.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use futures::executor::block_on;
2020
use txpool::{self, Pool};
2121
use sp_runtime::{
2222
generic::BlockId,
23-
transaction_validity::{ValidTransaction, InvalidTransaction, TransactionSource},
23+
transaction_validity::{ValidTransaction, TransactionSource, InvalidTransaction},
2424
};
2525
use substrate_test_runtime_client::{
2626
runtime::{Block, Hash, Index, Header, Extrinsic, Transfer},
@@ -263,7 +263,6 @@ fn should_not_retain_invalid_hashes_from_retracted() {
263263
let event = block_event_with_retracted(1, vec![retracted_hash]);
264264

265265
block_on(pool.maintain(event));
266-
// maintenance is in background
267266
block_on(notifier.next());
268267

269268
assert_eq!(pool.status().ready, 0);
@@ -701,6 +700,6 @@ fn should_not_accept_old_signatures() {
701700
Err(error::Error::Pool(
702701
sp_transaction_pool::error::Error::InvalidTransaction(InvalidTransaction::BadProof)
703702
)),
704-
"Should be invalid transactiono with bad proof",
703+
"Should be invalid transaction with bad proof",
705704
);
706705
}

frame/executive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ frame-system = { version = "2.0.0-alpha.5", default-features = false, path = "..
1515
serde = { version = "1.0.101", optional = true }
1616
sp-runtime = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/runtime" }
1717
sp-std = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/std" }
18+
sp-io = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/io" }
1819

1920
[dev-dependencies]
2021
hex-literal = "0.2.1"

frame/executive/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,13 @@ where
237237
// any initial checks
238238
Self::initial_checks(&block);
239239

240+
let batching_safeguard = sp_runtime::SignatureBatching::start();
240241
// execute extrinsics
241242
let (header, extrinsics) = block.deconstruct();
242243
Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number());
244+
if !sp_runtime::SignatureBatching::verify(batching_safeguard) {
245+
panic!("Signature verification failed.");
246+
}
243247

244248
// any final checks
245249
Self::final_checks(&header);

primitives/core/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ sha2 = { version = "0.8.0", default-features = false, optional = true }
4444
hex = { version = "0.4", default-features = false, optional = true }
4545
twox-hash = { version = "1.5.0", default-features = false, optional = true }
4646
libsecp256k1 = { version = "0.3.2", default-features = false, features = ["hmac"], optional = true }
47+
merlin = { version = "2.0", default-features = false, optional = true }
4748

4849
sp-runtime-interface = { version = "2.0.0-alpha.5", default-features = false, path = "../runtime-interface" }
4950

@@ -94,7 +95,6 @@ std = [
9495
"schnorrkel/std",
9596
"regex",
9697
"num-traits/std",
97-
"libsecp256k1/std",
9898
"tiny-keccak",
9999
"sp-debug-derive/std",
100100
"sp-externalities",
@@ -103,6 +103,7 @@ std = [
103103
"zeroize/alloc",
104104
"futures",
105105
"futures/thread-pool",
106+
"libsecp256k1/std",
106107
]
107108

108109
# This feature enables all crypto primitives for `no_std` builds like microcontrollers
@@ -118,6 +119,7 @@ full_crypto = [
118119
"twox-hash",
119120
"libsecp256k1",
120121
"sp-runtime-interface/disable_target_static_assertions",
122+
"merlin",
121123
]
122124

123125
[package.metadata.docs.rs]

primitives/core/src/sr25519.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,45 @@ impl CryptoType for Pair {
611611
type Pair = Pair;
612612
}
613613

614+
/// Batch verification.
615+
///
616+
/// `messages`, `signatures` and `pub_keys` should all have equal length.
617+
///
618+
/// Returns `true` if all signatures are correct, `false` otherwise.
619+
#[cfg(feature = "std")]
620+
pub fn verify_batch(
621+
messages: Vec<&[u8]>,
622+
signatures: Vec<&Signature>,
623+
pub_keys: Vec<&Public>,
624+
) -> bool {
625+
let mut sr_pub_keys = Vec::with_capacity(pub_keys.len());
626+
for pub_key in pub_keys {
627+
match schnorrkel::PublicKey::from_bytes(pub_key.as_ref()) {
628+
Ok(pk) => sr_pub_keys.push(pk),
629+
Err(_) => return false,
630+
};
631+
}
632+
633+
let mut sr_signatures = Vec::with_capacity(signatures.len());
634+
for signature in signatures {
635+
match schnorrkel::Signature::from_bytes(signature.as_ref()) {
636+
Ok(s) => sr_signatures.push(s),
637+
Err(_) => return false
638+
};
639+
}
640+
641+
let mut messages: Vec<merlin::Transcript> = messages.into_iter().map(
642+
|msg| signing_context(SIGNING_CTX).bytes(msg)
643+
).collect();
644+
645+
schnorrkel::verify_batch(
646+
&mut messages,
647+
&sr_signatures,
648+
&sr_pub_keys,
649+
true,
650+
).is_ok()
651+
}
652+
614653
#[cfg(test)]
615654
mod compatibility_test {
616655
use super::*;

primitives/externalities/src/extensions.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
//!
2222
//! It is required that each extension implements the [`Extension`] trait.
2323
24-
use std::{collections::HashMap, any::{Any, TypeId}, ops::DerefMut};
24+
use std::{collections::HashMap, collections::hash_map::Entry, any::{Any, TypeId}, ops::DerefMut};
25+
use crate::Error;
2526

2627
/// Marker trait for types that should be registered as [`Externalities`](crate::Externalities) extension.
2728
///
@@ -87,6 +88,16 @@ pub trait ExtensionStore {
8788
/// It is advised to use [`ExternalitiesExt::extension`](crate::ExternalitiesExt::extension)
8889
/// instead of this function to get type system support and automatic type downcasting.
8990
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any>;
91+
92+
/// Register extension `extension` with speciifed `type_id`.
93+
///
94+
/// It should return error if extension is already registered.
95+
fn register_extension_with_type_id(&mut self, type_id: TypeId, extension: Box<dyn Extension>) -> Result<(), Error>;
96+
97+
/// Deregister extension with speicifed 'type_id' and drop it.
98+
///
99+
/// It should return error if extension is not registered.
100+
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), Error>;
90101
}
91102

92103
/// Stores extensions that should be made available through the externalities.
@@ -95,6 +106,12 @@ pub struct Extensions {
95106
extensions: HashMap<TypeId, Box<dyn Extension>>,
96107
}
97108

109+
impl std::fmt::Debug for Extensions {
110+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111+
write!(f, "Extensions: ({})", self.extensions.len())
112+
}
113+
}
114+
98115
impl Extensions {
99116
/// Create new instance of `Self`.
100117
pub fn new() -> Self {
@@ -106,10 +123,23 @@ impl Extensions {
106123
self.extensions.insert(ext.type_id(), Box::new(ext));
107124
}
108125

126+
/// Register extension `ext`.
127+
pub fn register_with_type_id(&mut self, type_id: TypeId, extension: Box<dyn Extension>) -> Result<(), Error> {
128+
match self.extensions.entry(type_id) {
129+
Entry::Vacant(vacant) => { vacant.insert(extension); Ok(()) },
130+
Entry::Occupied(_) => Err(Error::ExtensionAlreadyRegistered),
131+
}
132+
}
133+
109134
/// Return a mutable reference to the requested extension.
110135
pub fn get_mut(&mut self, ext_type_id: TypeId) -> Option<&mut dyn Any> {
111136
self.extensions.get_mut(&ext_type_id).map(DerefMut::deref_mut).map(Extension::as_mut_any)
112137
}
138+
139+
/// Deregister extension of type `E`.
140+
pub fn deregister(&mut self, type_id: TypeId) -> Option<Box<dyn Extension>> {
141+
self.extensions.remove(&type_id)
142+
}
113143
}
114144

115145
#[cfg(test)]

0 commit comments

Comments
 (0)