Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
df8a6d4
session: runtime api for generating session membership proofs
andresilva Aug 17, 2019
a94834e
grandpa: add runtime api for creating equivocation report txs
andresilva Aug 17, 2019
30213e4
grandpa: submit signed equivocation report transactions
andresilva Oct 18, 2019
071b3f4
grandpa: use proper equivocation report type
andresilva Oct 18, 2019
6d98987
grandpa: report equivocations
andresilva Oct 19, 2019
9df0d4d
grandpa: validate equivocation proof
andresilva Oct 21, 2019
e4435d7
grandpa: update to finality-grandpa 0.9.1
andresilva Oct 21, 2019
0497a8a
grandpa: fix encoding of session membership proof
andresilva Oct 22, 2019
b8c1c99
grandpa: initialize set id session mapping for genesis session
andresilva Oct 22, 2019
18b0d9d
grandpa: fix bug in set_id session validation
andresilva Oct 22, 2019
4848cc3
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Nov 27, 2019
c0d5453
fix compilation
andresilva Nov 27, 2019
97770a5
cleanup from merge conflicts
andresilva Nov 27, 2019
d395ff6
cleanup crate tomls
andresilva Nov 27, 2019
3962bb0
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Nov 27, 2019
a31208c
grandpa: refactor equivocation handling to separate trait
andresilva Nov 28, 2019
3d3ef58
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Dec 10, 2019
4d0b227
node-template: fix compilation
andresilva Dec 10, 2019
8e0cbf6
fix test compilation
andresilva Dec 10, 2019
2cc3d7f
bump finality-grandpa to v0.10.2
andresilva Dec 10, 2019
beb09d5
rpc: fix runtime version test
andresilva Dec 11, 2019
b9c1651
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Dec 11, 2019
d2f0a7f
CHERRY-PICK #4200: Add documentation to SubmitSignedTransaction and a…
andresilva Dec 11, 2019
044c7f0
grandpa: skip block initialization on report submission method
andresilva Dec 11, 2019
17af714
primitives: allow transaction pool access by default for offchain calls
andresilva Dec 11, 2019
29e4f94
grandpa: unused parameters
andresilva Dec 11, 2019
e69c86e
grandpa: remove unused method
andresilva Dec 11, 2019
856087d
grandpa: enable equivocation reporting
andresilva Dec 11, 2019
4e4a27b
grandpa: add workaround for parameter encoding
andresilva Dec 11, 2019
47aab63
grandpa: fix localized_payload calls in tests
andresilva Dec 11, 2019
8fc3cd3
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Dec 12, 2019
da9d216
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Dec 16, 2019
721baad
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Jan 27, 2020
44b3fde
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Jan 27, 2020
9e1586e
fix submit_report_equivocation_extrinsic in runtimes
andresilva Jan 28, 2020
a3c62ea
node: fix submit transaction test compilation
andresilva Jan 28, 2020
bd814e7
node: bump spec_version
andresilva Jan 28, 2020
9ac2c45
rpc: fix api version test
andresilva Jan 29, 2020
2059062
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Jan 31, 2020
ec2c737
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Feb 14, 2020
ee3f6b0
grandpa: allow custom equivocation offence type
andresilva Feb 24, 2020
45d2550
grandpa: add test for authorities::next_change_height
andresilva Feb 25, 2020
3b4c910
grandpa: cleanup report_equivocation function
andresilva Feb 25, 2020
aced659
node: move reporting app crypto to node-primitives
andresilva Feb 25, 2020
1c3d8a0
grandpa: move equivocation traits to own module
andresilva Feb 25, 2020
45d6d9d
grandpa: rename app-crypto crate import
andresilva Feb 25, 2020
a04d98c
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Feb 26, 2020
34a89c2
grandpa: export equivocation types
andresilva Feb 26, 2020
bcc0497
node: bump spec_version
andresilva Feb 26, 2020
2c8e808
grandpa: rename EquivocationReport to EquivocationProof
andresilva Feb 26, 2020
bea38e7
grandpa: add missing docs to primitives
andresilva Feb 26, 2020
c182781
grandpa: add missing docs to equivocation
andresilva Feb 26, 2020
0ee7249
node: fix compilation
andresilva Feb 26, 2020
18ad970
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Feb 26, 2020
237f516
grandpa: add missing docs to pallet
andresilva Feb 26, 2020
a99bea6
node: bump spec_version
andresilva Feb 26, 2020
443856b
fix whitespace
andresilva Mar 2, 2020
f8459bc
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Mar 13, 2020
6372736
grandpa: return error on offence reporting
andresilva Mar 13, 2020
2f05c6d
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Mar 25, 2020
6828d57
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Mar 30, 2020
18eb560
grandpa: expose session and validator count in proofs through traits
andresilva Apr 1, 2020
9a04120
grandpa: use strong key in module KeyOwnerProofSystem
andresilva Apr 1, 2020
5be01ef
grandpa: move key ownership proof to grandpa runtime api
andresilva Apr 1, 2020
5a24915
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Apr 1, 2020
d3304fe
grandpa: remove unnecessary cloning when checking equivocation proof
andresilva Apr 2, 2020
71d60de
grandpa: make report_equivocation a method in Environment
andresilva Apr 2, 2020
bd4c74f
support: implement KeyOwnerProofSystem for ()
andresilva Apr 8, 2020
668363f
grandpa: move KeyOwnerProofSystem to module trait
andresilva Apr 8, 2020
7658f31
test-utils: fix runtime compilation
andresilva Apr 8, 2020
97d8fb8
grandpa: fix test compilation
andresilva Apr 8, 2020
7926ab9
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Apr 8, 2020
712c78d
grandpa: fix test compilation after merge
andresilva Apr 8, 2020
ae2f6bf
grandpa: simplify transaction submission types
andresilva Apr 8, 2020
b6ef6e3
grandpa: validate equivocation report in signed extension
andresilva Apr 8, 2020
7d3cfd7
client: fix test
andresilva Apr 8, 2020
a15f483
node: use ValidateEquivocationReport signed extension
andresilva Apr 8, 2020
dca1a80
grandpa: expose key ownership proof under opaque type
andresilva Apr 8, 2020
f686072
grandpa: better docs on key ownership proofs
andresilva Apr 9, 2020
a91c2eb
grandpa: add note about signed extension
andresilva Apr 9, 2020
a3e8314
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Apr 9, 2020
88d4f32
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Apr 14, 2020
837ebbf
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Apr 17, 2020
ca43f21
grandpa: add ValidateEquivocationReport::new
andresilva Apr 17, 2020
75ec8e8
grandpa: remove skip_initialize_block from runtime api
andresilva Apr 20, 2020
e787d7f
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva Apr 27, 2020
ea73e51
grandpa: use new offchain transaction submission API
andresilva Apr 27, 2020
4468c00
grandpa: take set_id in generate_key_ownership_proof
andresilva Apr 27, 2020
e8981b7
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva May 5, 2020
873fe19
grandpa: update to finality-grandpa v0.12.2
andresilva May 5, 2020
207e0dc
grandpa: cleanup usages of AuthoritySet::current
andresilva May 5, 2020
94990ce
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva May 5, 2020
a237061
grandpa: fix test
andresilva May 5, 2020
769d1d5
grandpa: add mocking utilities for equivocation reporting
andresilva May 5, 2020
f66ba8b
grandpa: add test for equivocation reporting
andresilva May 5, 2020
76eeee1
grandpa: move SetIdSession initialization
andresilva May 5, 2020
34e4040
grandpa: add more tests
andresilva May 5, 2020
b34ebde
node: enable historical session manager
andresilva May 5, 2020
999b671
node: bump spec_version
andresilva May 5, 2020
7f08cc1
node: use strong key types in KeyOwnerProofSystem definitions
andresilva May 6, 2020
ee614f1
Merge branch 'master' into andre/report-grandpa-equivocations
andresilva May 6, 2020
d390b74
grandpa: export GrandpaEquivocationOffence type
andresilva May 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
CHERRY-PICK #4200: Add documentation to SubmitSignedTransaction and a…
…ctually make it work

Squashed commit of the following:

commit dc8d71c
Author: Tomasz Drwięga <[email protected]>
Date:   Tue Dec 3 16:29:33 2019 +0100

    Split the method to avoid confusing type error message.

commit 0c4c037
Author: Tomasz Drwięga <[email protected]>
Date:   Tue Dec 3 16:19:55 2019 +0100

    Make accounts optional, fix logic.

commit d715f64
Author: Tomasz Drwięga <[email protected]>
Date:   Tue Dec 3 10:06:20 2019 +0100

    Remove warning.

commit 3f38218
Merge: f85b890 368318c
Author: Tomasz Drwięga <[email protected]>
Date:   Tue Dec 3 07:08:05 2019 +0100

    Merge branch 'master' into td-signed-transactions

commit f85b890
Merge: f8c9540 d8d5da2
Author: Tomasz Drwięga <[email protected]>
Date:   Mon Dec 2 13:57:25 2019 +0100

    Merge branch 'master' into td-signed-transactions

commit f8c9540
Author: Tomasz Drwięga <[email protected]>
Date:   Mon Nov 25 17:34:52 2019 +0100

    Forgotten import.

commit a645b90
Author: Tomasz Drwięga <[email protected]>
Date:   Mon Nov 25 17:32:10 2019 +0100

    Fix naming and bounds.

commit bc28c60
Author: Tomasz Drwięga <[email protected]>
Date:   Mon Nov 25 17:01:05 2019 +0100

    Add documentation to signed transactions and actually make them work.
  • Loading branch information
andresilva committed Dec 11, 2019
commit d2f0a7feac4d7fbb2ad2b5154ff81d1168a6b69f
33 changes: 20 additions & 13 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,22 +778,29 @@ impl_runtime_apis! {
#[cfg(test)]
mod tests {
use super::*;
use system::offchain::SubmitSignedTransaction;

fn is_submit_signed_transaction<T>(_arg: T) where
T: SubmitSignedTransaction<
Runtime,
Call,
Extrinsic=UncheckedExtrinsic,
CreateTransaction=Runtime,
Signer=ImOnlineId,
>,
{}
use system::offchain::{SignAndSubmitTransaction, SubmitSignedTransaction};

#[test]
fn validate_bounds() {
let x = SubmitImOnlineTransaction::default();
is_submit_signed_transaction(x);
fn is_submit_signed_transaction<T>() where
T: SubmitSignedTransaction<
Runtime,
Call,
>,
{}

fn is_sign_and_submit_transaction<T>() where
T: SignAndSubmitTransaction<
Runtime,
Call,
Extrinsic=UncheckedExtrinsic,
CreateTransaction=Runtime,
Signer=ImOnlineId,
>,
{}

is_submit_signed_transaction::<SubmitImOnlineTransaction>();
is_sign_and_submit_transaction::<SubmitImOnlineTransaction>();
}

#[test]
Expand Down
248 changes: 206 additions & 42 deletions frame/system/src/offchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,52 @@
//! Module helpers for offchain calls.

use codec::Encode;
use sp_runtime::app_crypto::{self, RuntimeAppPublic};
use sp_std::convert::TryInto;
use sp_std::prelude::Vec;
use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature};
use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount};
use support::debug;

/// Creates runtime-specific signed transaction.
///
/// This trait should be implemented by your `Runtime` to be able
/// to submit `SignedTransaction`s` to the pool from offchain code.
pub trait CreateTransaction<T: crate::Trait, Extrinsic: ExtrinsicT> {
/// A `Public` key representing a particular `AccountId`.
type Public: Clone + IdentifyAccount<AccountId=T::AccountId>;
/// A `Signature` generated by the `Signer`.
type Signature;

/// Attempt to create signed extrinsic data that encodes call from given account.
///
/// Runtime implementation is free to construct the payload to sign and the signature
/// in any way it wants.
/// Returns `None` if signed extrinsic could not be created (either because signing failed
/// or because of any other runtime-specific reason).
fn create_transaction<F: Signer<Self::Public, Self::Signature>>(
call: Extrinsic::Call,
public: Self::Public,
account: T::AccountId,
nonce: T::Index,
) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>;
}

/// A trait responsible for signing a payload using given account.
///
/// This trait is usually going to represent a local public key
/// that has ability to sign arbitrary `Payloads`.
///
/// NOTE: Most likely you don't need to implement this trait manually.
/// It has a blanket implementation for all `RuntimeAppPublic` types,
/// so it's enough to pass an application-specific crypto type.
///
/// To easily create `SignedTransaction`s have a look at the
/// `TransactionSubmitter` type.
pub trait Signer<Public, Signature> {
/// Sign any encodable payload with given account and produce a signature.
///
/// Returns `Some` if signing succeeded and `None` in case the `account` couldn't be used.
/// Returns `Some` if signing succeeded and `None` in case the `account` couldn't
/// be used (for instance we couldn't convert it to required application specific crypto).
fn sign<Payload: Encode>(public: Public, payload: &Payload) -> Option<Signature>;
}

Expand All @@ -33,59 +71,43 @@ pub trait Signer<Public, Signature> {
/// This implementation additionaly supports conversion to/from multi-signature/multi-signer
/// wrappers.
/// If the wrapped crypto doesn't match `AppPublic`s crypto `None` is returned.
impl<Public, Signature, AppPublic> Signer<Public, Signature> for AppPublic where
AppPublic: RuntimeAppPublic
+ app_crypto::AppPublic
+ From<<AppPublic as app_crypto::AppPublic>::Generic>,
<AppPublic as RuntimeAppPublic>::Signature: app_crypto::AppSignature,
impl<Public, Signature, TAnyAppPublic> Signer<Public, Signature> for TAnyAppPublic where
TAnyAppPublic: RuntimeAppPublic
+ AppPublic
+ From<<TAnyAppPublic as AppPublic>::Generic>,
<TAnyAppPublic as RuntimeAppPublic>::Signature: AppSignature,
Signature: From<
<<AppPublic as RuntimeAppPublic>::Signature as app_crypto::AppSignature>::Generic
<<TAnyAppPublic as RuntimeAppPublic>::Signature as AppSignature>::Generic
>,
Public: sp_std::convert::TryInto<<AppPublic as app_crypto::AppPublic>::Generic>
Public: TryInto<<TAnyAppPublic as AppPublic>::Generic>
{
fn sign<Payload: Encode>(public: Public, raw_payload: &Payload) -> Option<Signature> {
raw_payload.using_encoded(|payload| {
let public = public.try_into().ok()?;
AppPublic::from(public).sign(&payload)
TAnyAppPublic::from(public).sign(&payload)
.map(
<<AppPublic as RuntimeAppPublic>::Signature as app_crypto::AppSignature>
<<TAnyAppPublic as RuntimeAppPublic>::Signature as AppSignature>
::Generic::from
)
.map(Signature::from)
})
}
}

/// Creates runtime-specific signed transaction.
pub trait CreateTransaction<T: crate::Trait, Extrinsic: ExtrinsicT> {
/// A `Public` key representing a particular `AccountId`.
type Public: IdentifyAccount<AccountId=T::AccountId> + Clone;
/// A `Signature` generated by the `Signer`.
type Signature;

/// Attempt to create signed extrinsic data that encodes call from given account.
///
/// Runtime implementation is free to construct the payload to sign and the signature
/// in any way it wants.
/// Returns `None` if signed extrinsic could not be created (either because signing failed
/// or because of any other runtime-specific reason).
fn create_transaction<F: Signer<Self::Public, Self::Signature>>(
call: Extrinsic::Call,
public: Self::Public,
account: T::AccountId,
nonce: T::Index,
) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>;
}

type PublicOf<T, Call, X> = <
<X as SubmitSignedTransaction<T, Call>>::CreateTransaction as CreateTransaction<
T,
<X as SubmitSignedTransaction<T, Call>>::Extrinsic,
>
/// Retrieves a public key type for given `SignAndSubmitTransaction`.
pub type PublicOf<T, Call, X> =
<
<X as SignAndSubmitTransaction<T, Call>>::CreateTransaction
as
CreateTransaction<T, <X as SignAndSubmitTransaction<T, Call>>::Extrinsic>
>::Public;

/// A trait to sign and submit transactions in offchain calls.
pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
///
/// NOTE: Most likely you should not implement this trait yourself.
/// There is an implementation for `TransactionSubmitter` type, which
/// you should use.
pub trait SignAndSubmitTransaction<T: crate::Trait, Call> {
/// Unchecked extrinsic type.
type Extrinsic: ExtrinsicT<Call=Call> + codec::Encode;

Expand All @@ -107,6 +129,12 @@ pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
let call = call.into();
let id = public.clone().into_account();
let expected = <crate::Module<T>>::account_nonce(&id);
debug::native::debug!(
target: "offchain",
"Creating signed transaction from account: {:?} (nonce: {:?})",
id,
expected,
);
let (call, signature_data) = Self::CreateTransaction
::create_transaction::<Self::Signer>(call, public, id, expected)
.ok_or(())?;
Expand All @@ -116,6 +144,10 @@ pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
}

/// A trait to submit unsigned transactions in offchain calls.
///
/// NOTE: Most likely you should not implement this trait yourself.
/// There is an implementation for `TransactionSubmitter` type, which
/// you should use.
pub trait SubmitUnsignedTransaction<T: crate::Trait, Call> {
/// Unchecked extrinsic type.
type Extrinsic: ExtrinsicT<Call=Call> + codec::Encode;
Expand All @@ -130,9 +162,91 @@ pub trait SubmitUnsignedTransaction<T: crate::Trait, Call> {
}
}

/// A utility trait to easily create signed transactions
/// from accounts in node's local keystore.
///
/// NOTE: Most likely you should not implement this trait yourself.
/// There is an implementation for `TransactionSubmitter` type, which
/// you should use.
pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
/// A `SignAndSubmitTransaction` implementation.
type SignAndSubmit: SignAndSubmitTransaction<T, Call>;

/// Find local keys that match given list of accounts.
///
/// Technically it finds an intersection between given list of `AccountId`s
/// and accounts that are represented by public keys in local keystore.
/// If `None` is passed it returns all accounts in the keystore.
///
/// Returns both public keys and `AccountId`s of accounts that are available.
/// Such accounts can later be used to sign a payload or send signed transactions.
fn find_local_keys(accounts: Option<impl IntoIterator<Item = T::AccountId>>) -> Vec<(
T::AccountId,
PublicOf<T, Call, Self::SignAndSubmit>,
)>;

/// Create and submit signed transactions from supported accounts.
///
/// This method should intersect given list of accounts with the ones
/// supported locally and submit signed transaction containing given `Call`
/// with every of them.
///
/// Returns a vector of results and account ids that were supported.
#[must_use]
fn submit_signed_from(
call: impl Into<Call> + Clone,
accounts: impl IntoIterator<Item= T::AccountId>,
) -> Vec<(T::AccountId, Result<(), ()>)> {
let keys = Self::find_local_keys(Some(accounts));
keys.into_iter().map(|(account, pub_key)| {
let call = call.clone().into();
(
account,
Self::SignAndSubmit::sign_and_submit(call, pub_key)
)
}).collect()
}

/// Create and submit signed transactions from all local accounts.
///
/// This method submits a signed transaction from all local accounts
/// for given application crypto.
///
/// Returns a vector of results and account ids that were supported.
#[must_use]
fn submit_signed(
call: impl Into<Call> + Clone,
) -> Vec<(T::AccountId, Result<(), ()>)> {
let keys = Self::find_local_keys(None as Option<Vec<_>>);
keys.into_iter().map(|(account, pub_key)| {
let call = call.clone().into();
(
account,
Self::SignAndSubmit::sign_and_submit(call, pub_key)
)
}).collect()
}
}


/// A default type used to submit transactions to the pool.
pub struct TransactionSubmitter<S, C, E> {
_signer: sp_std::marker::PhantomData<(S, C, E)>,
///
/// This is passed into each runtime as an opaque associated type that can have either of:
/// - [`SignAndSubmitTransaction`]
/// - [`SubmitUnsignedTransaction`]
/// - [`SubmitSignedTransaction`]
/// and used accordingly.
///
/// This struct should be constructed by providing the following generic parameters:
/// * `Signer` - Usually the application specific key type (see `app_crypto`).
/// * `CreateTransaction` - A type that is able to produce signed transactions,
/// usually it's going to be the entire `Runtime` object.
/// * `Extrinsic` - A runtime-specific type for in-block extrinsics.
///
/// If you only need the ability to submit unsigned transactions,
/// you may substitute both `Signer` and `CreateTransaction` with any type.
pub struct TransactionSubmitter<Signer, CreateTransaction, Extrinsic> {
_signer: sp_std::marker::PhantomData<(Signer, CreateTransaction, Extrinsic)>,
}

impl<S, C, E> Default for TransactionSubmitter<S, C, E> {
Expand All @@ -144,7 +258,7 @@ impl<S, C, E> Default for TransactionSubmitter<S, C, E> {
}

/// A blanket implementation to simplify creation of transaction signer & submitter in the runtime.
impl<T, E, S, C, Call> SubmitSignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
impl<T, E, S, C, Call> SignAndSubmitTransaction<T, Call> for TransactionSubmitter<S, C, E> where
T: crate::Trait,
C: CreateTransaction<T, E>,
S: Signer<<C as CreateTransaction<T, E>>::Public, <C as CreateTransaction<T, E>>::Signature>,
Expand All @@ -155,10 +269,60 @@ impl<T, E, S, C, Call> SubmitSignedTransaction<T, Call> for TransactionSubmitter
type Signer = S;
}

/// A blanket impl to use the same submitter for usigned transactions as well.
/// A blanket implementation to use the same submitter for unsigned transactions as well.
impl<T, E, S, C, Call> SubmitUnsignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
T: crate::Trait,
E: ExtrinsicT<Call=Call> + codec::Encode,
{
type Extrinsic = E;
}

/// A blanket implementation to support local keystore of application-crypto types.
impl<T, C, E, S, Call> SubmitSignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
T: crate::Trait,
C: CreateTransaction<T, E>,
E: ExtrinsicT<Call=Call> + codec::Encode,
S: Signer<<C as CreateTransaction<T, E>>::Public, <C as CreateTransaction<T, E>>::Signature>,
// Make sure we can unwrap the app crypto key.
S: RuntimeAppPublic + AppPublic + Into<<S as AppPublic>::Generic>,
// Make sure we can convert from wrapped crypto to public key (e.g. `MultiSigner`)
S::Generic: Into<PublicOf<T, Call, Self>>,
// For simplicity we require the same trait to implement `SignAndSubmitTransaction` too.
Self: SignAndSubmitTransaction<T, Call, Signer = S, Extrinsic = E, CreateTransaction = C>,
{
type SignAndSubmit = Self;

fn find_local_keys(accounts: Option<impl IntoIterator<Item = T::AccountId>>) -> Vec<(
T::AccountId,
PublicOf<T, Call, Self::SignAndSubmit>,
)> {
// Convert app-specific keys into generic ones.
let local_accounts_and_keys = S::all()
.into_iter()
.map(|app_key| {
// unwrap app-crypto
let generic_pub_key: <S as AppPublic>::Generic = app_key.into();
// convert to expected public key type (might be MultiSigner)
let signer_pub_key: PublicOf<T, Call, Self::SignAndSubmit> = generic_pub_key.into();
// lookup accountid for that pubkey
let account = signer_pub_key.clone().into_account();
(account, signer_pub_key)
}).collect::<Vec<_>>();

if let Some(accounts) = accounts {
let mut local_accounts_and_keys = local_accounts_and_keys;
// sort by accountId to allow bin-search.
local_accounts_and_keys.sort_by(|a, b| a.0.cmp(&b.0));

// get all the matching accounts
accounts.into_iter().filter_map(|acc| {
let idx = local_accounts_and_keys.binary_search_by(|a| a.0.cmp(&acc)).ok()?;
local_accounts_and_keys.get(idx).cloned()
}).collect()
} else {
// just return all account ids and keys
local_accounts_and_keys
}
}
}