Support for multiple signature scheme for BEEFY primitves (#14373)

* Merged BEEFY primitives with generic signature and keyset commitment support from old pull to current code

* - Add bls-experimental feature to application-crypto and beefy primitives
- Fix remaining crypto -> ecdsa_crypto
- code build but not tests

* Make beefy primitive tests compile

* move bls related beefy primitives code and test behind bls-experimental flag

* Make BEEFY clients complies with BEEFY API depending on AuthorityId

* - Rename `BeefyAuthoritySet.root` → `BeefyAuthoritySet.keyset_commitment`.
- Remove apk proof keyset_commitment from `BeefyAuthoritySet`.
- Fix failing signed commitment and signature to witness test.
- Make client compatible with BeefyAPI generic on AuthorityId.
- `crypto` → `ecdsa_crypto` in BEEFY client and frame.

* Commit Cargo lock remove ark-serialize from BEEFY primitives

* Use Codec instead of Encode + Decode in primitives/consensus/beefy/src/lib.rs

Co-authored-by: Davide Galassi <davxy@datawok.net>

* - Make `BeefyApi` generic over Signature type.
- Make new `BeeyApi` functinos also generic over AuthorityId and Signature

* Unmake BeefyAPI generic over Signature. Recover Signature type from AuthId.

* - dont use hex or hex-literal use array-bytes instead in beefy primitives and bls crypto.
- CamelCase ECDSA and BLS everywhere.

* Move the definition of BEEFY key type from `primitives/beefy` to `crypto.rs` according to new convention.

* - Add bls377_generate_new to `sp-io` and `application_crypto::bls`.
- Add `bls-experimental` to `sp-io`

Does not compile because PassByCodec can not derive PassBy using customly implemented PassByIner.

* Implement PassBy for `bls::Public` manually

* fix Beefy `KEY_TYPE` in `frame/beefy` tests to come from `sp-core::key_types` enum

* specify both generic for `hex2array_unchecked` in `sp-core/bls.rs`

* Rename `crypto`→`ecdsa_crypto` in `primitives/consensus/beefy/src/test_utils.rs` docs

* remove commented-out code in `primitives/consensus/beefy/src/commitment.rs`

Co-authored-by: Davide Galassi <davxy@datawok.net>

* Fix inconsistency in panic message in  `primitives/io/src/lib.rs`

Co-authored-by: Davide Galassi <davxy@datawok.net>

* Remove redundant feature activation in `primitives/io/Cargo.toml`

Co-authored-by: Davide Galassi <davxy@datawok.net>

* - make `w3f-bls` a dev-dependancy only for beefy primitives.

- clean up comments.

Co-authored-by: Davide Galassi <davxy@datawok.net>

* export BEEFY KEY_TYPE from primitives/consensus/beefy
make `frame/consensus/beefy` in dependent of sp_crypto_app
use consistent naming in the beefy primitive tests.

* - implement `BeefyAuthorityId` for `bls_crypto::AuthorityId`.
- implement `bls_verify_works` test for BEEFY `bls_crypto`.

* Remove BEEFY `ecdsa_n_bls_crypto` for now for later re-introduction

* Make commitment and witness BEEFY tests not use Keystore.

* put `bls_beefy_verify_works` test under `bls-experimental` flag.

* bump up Runtime `BeefyAPI` to version 3 due to introducing generic AuthorityId.

* reuse code and encapsulate w3f-bls backend in sp-core as most as possible

Co-authored-by: Davide Galassi <davxy@datawok.net>

* Make comments in primities BEEFY `commitment.rs` and `witness.rs``tests convention conforming

* Use master dep versions

* Trivial change. Mostly to trigger CI

* Apply suggestions from code review

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Fix Cargo.toml

* Trigger CI with cumulus companion

* Trigger CI after polkadot companion change

---------

Co-authored-by: Davide Galassi <davxy@datawok.net>
Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
drskalman
2023-08-02 15:42:04 +02:00
committed by GitHub
parent 8dc3bd729b
commit 3fef703e30
29 changed files with 502 additions and 192 deletions
+1 -2
View File
@@ -10614,11 +10614,11 @@ dependencies = [
"sp-application-crypto",
"sp-core",
"sp-io",
"sp-keystore",
"sp-mmr-primitives",
"sp-runtime",
"sp-std",
"strum",
"w3f-bls",
]
[[package]]
@@ -10675,7 +10675,6 @@ dependencies = [
"futures",
"hash-db",
"hash256-std-hasher",
"hex-literal",
"impl-serde",
"lazy_static",
"libsecp256k1",
@@ -41,7 +41,7 @@ use crate::{
LOG_TARGET,
};
use sp_consensus_beefy::{
crypto::{AuthorityId, Signature},
ecdsa_crypto::{AuthorityId, Signature},
ValidatorSet, ValidatorSetId, VoteMessage,
};
@@ -476,9 +476,10 @@ pub(crate) mod tests {
use super::*;
use crate::keystore::BeefyKeystore;
use sc_network_test::Block;
use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE;
use sp_consensus_beefy::{
crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload,
SignedCommitment, VoteMessage, KEY_TYPE,
ecdsa_crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload,
SignedCommitment, VoteMessage,
};
use sp_keystore::{testing::MemoryKeystore, Keystore};
@@ -536,7 +537,7 @@ pub(crate) mod tests {
pub fn sign_commitment<BN: Encode>(who: &Keyring, commitment: &Commitment<BN>) -> Signature {
let store = MemoryKeystore::new();
store.ecdsa_generate_new(KEY_TYPE, Some(&who.to_seed())).unwrap();
store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap();
let beefy_keystore: BeefyKeystore = Some(store.into()).into();
beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap()
}
@@ -26,7 +26,7 @@ use sc_network::{
request_responses::{IfDisconnected, RequestFailure},
NetworkRequest, PeerId, ProtocolName,
};
use sp_consensus_beefy::{crypto::AuthorityId, ValidatorSet};
use sp_consensus_beefy::{ecdsa_crypto::AuthorityId, ValidatorSet};
use sp_runtime::traits::{Block, NumberFor};
use std::{collections::VecDeque, result::Result, sync::Arc};
@@ -22,7 +22,7 @@ use log::debug;
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sp_consensus::Error as ConsensusError;
use sp_consensus_beefy::{BeefyApi, BEEFY_ENGINE_ID};
use sp_consensus_beefy::{ecdsa_crypto::AuthorityId, BeefyApi, BEEFY_ENGINE_ID};
use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT, NumberFor},
EncodedJustification,
@@ -83,7 +83,7 @@ where
Block: BlockT,
BE: Backend<Block>,
Runtime: ProvideRuntimeApi<Block>,
Runtime::Api: BeefyApi<Block> + Send,
Runtime::Api: BeefyApi<Block, AuthorityId> + Send,
{
fn decode_and_verify(
&self,
@@ -125,7 +125,7 @@ where
> + Send
+ Sync,
Runtime: ProvideRuntimeApi<Block> + Send + Sync,
Runtime::Api: BeefyApi<Block>,
Runtime::Api: BeefyApi<Block, AuthorityId>,
{
type Error = ConsensusError;
type Transaction = TransactionFor<Runtime, Block>;
@@ -20,7 +20,7 @@ use crate::keystore::BeefyKeystore;
use codec::{DecodeAll, Encode};
use sp_consensus::Error as ConsensusError;
use sp_consensus_beefy::{
crypto::{AuthorityId, Signature},
ecdsa_crypto::{AuthorityId, Signature},
ValidatorSet, ValidatorSetId, VersionedFinalityProof,
};
use sp_runtime::traits::{Block as BlockT, NumberFor};
@@ -16,15 +16,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use sp_application_crypto::RuntimeAppPublic;
use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, RuntimeAppPublic};
use sp_core::keccak_256;
use sp_keystore::KeystorePtr;
use log::warn;
use sp_consensus_beefy::{
crypto::{Public, Signature},
BeefyAuthorityId, KEY_TYPE,
ecdsa_crypto::{Public, Signature},
BeefyAuthorityId,
};
use crate::{error, LOG_TARGET};
@@ -50,7 +50,7 @@ impl BeefyKeystore {
// we do check for multiple private keys as a key store sanity check.
let public: Vec<Public> = keys
.iter()
.filter(|k| store.has_keys(&[(k.to_raw_vec(), KEY_TYPE)]))
.filter(|k| store.has_keys(&[(k.to_raw_vec(), BEEFY_KEY_TYPE)]))
.cloned()
.collect();
@@ -78,7 +78,7 @@ impl BeefyKeystore {
let public = public.as_ref();
let sig = store
.ecdsa_sign_prehashed(KEY_TYPE, public, &msg)
.ecdsa_sign_prehashed(BEEFY_KEY_TYPE, public, &msg)
.map_err(|e| error::Error::Keystore(e.to_string()))?
.ok_or_else(|| error::Error::Signature("ecdsa_sign_prehashed() failed".to_string()))?;
@@ -96,7 +96,7 @@ impl BeefyKeystore {
let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?;
let pk: Vec<Public> =
store.ecdsa_public_keys(KEY_TYPE).drain(..).map(Public::from).collect();
store.ecdsa_public_keys(BEEFY_KEY_TYPE).drain(..).map(Public::from).collect();
Ok(pk)
}
@@ -117,7 +117,7 @@ impl From<Option<KeystorePtr>> for BeefyKeystore {
#[cfg(test)]
pub mod tests {
use sp_consensus_beefy::{crypto, Keyring};
use sp_consensus_beefy::{ecdsa_crypto, Keyring};
use sp_core::{ecdsa, Pair};
use sp_keystore::testing::MemoryKeystore;
@@ -156,35 +156,51 @@ pub mod tests {
#[test]
fn pair_works() {
let want = crypto::Pair::from_string("//Alice", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Alice", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Alice.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//Bob", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Bob", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Bob.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//Charlie", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Charlie", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Charlie.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//Dave", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Dave", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Dave.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//Eve", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Eve", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Eve.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//Ferdie", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Ferdie", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Ferdie.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//One", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//One", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::One.pair().to_raw_vec();
assert_eq!(want, got);
let want = crypto::Pair::from_string("//Two", None).expect("Pair failed").to_raw_vec();
let want = ecdsa_crypto::Pair::from_string("//Two", None)
.expect("Pair failed")
.to_raw_vec();
let got = Keyring::Two.pair().to_raw_vec();
assert_eq!(want, got);
}
@@ -193,8 +209,8 @@ pub mod tests {
fn authority_id_works() {
let store = keystore();
let alice: crypto::Public = store
.ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Alice.to_seed()))
let alice: ecdsa_crypto::Public = store
.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed()))
.ok()
.unwrap()
.into();
@@ -219,8 +235,8 @@ pub mod tests {
fn sign_works() {
let store = keystore();
let alice: crypto::Public = store
.ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Alice.to_seed()))
let alice: ecdsa_crypto::Public = store
.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed()))
.ok()
.unwrap()
.into();
@@ -239,7 +255,10 @@ pub mod tests {
fn sign_error() {
let store = keystore();
store.ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Bob.to_seed())).ok().unwrap();
store
.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Bob.to_seed()))
.ok()
.unwrap();
let store: BeefyKeystore = Some(store).into();
@@ -268,8 +287,8 @@ pub mod tests {
fn verify_works() {
let store = keystore();
let alice: crypto::Public = store
.ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Alice.to_seed()))
let alice: ecdsa_crypto::Public = store
.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed()))
.ok()
.unwrap()
.into();
@@ -305,11 +324,11 @@ pub mod tests {
let _ = add_key(TEST_TYPE, None);
// BEEFY keys
let _ = add_key(KEY_TYPE, Some(Keyring::Dave.to_seed().as_str()));
let _ = add_key(KEY_TYPE, Some(Keyring::Eve.to_seed().as_str()));
let _ = add_key(BEEFY_KEY_TYPE, Some(Keyring::Dave.to_seed().as_str()));
let _ = add_key(BEEFY_KEY_TYPE, Some(Keyring::Eve.to_seed().as_str()));
let key1: crypto::Public = add_key(KEY_TYPE, None).into();
let key2: crypto::Public = add_key(KEY_TYPE, None).into();
let key1: ecdsa_crypto::Public = add_key(BEEFY_KEY_TYPE, None).into();
let key2: ecdsa_crypto::Public = add_key(BEEFY_KEY_TYPE, None).into();
let store: BeefyKeystore = Some(store).into();
+8 -7
View File
@@ -46,7 +46,8 @@ use sp_blockchain::{
};
use sp_consensus::{Error as ConsensusError, SyncOracle};
use sp_consensus_beefy::{
crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID,
ecdsa_crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet,
BEEFY_ENGINE_ID,
};
use sp_keystore::KeystorePtr;
use sp_mmr_primitives::MmrApi;
@@ -142,7 +143,7 @@ where
+ Send
+ Sync,
RuntimeApi: ProvideRuntimeApi<B> + Send + Sync,
RuntimeApi::Api: BeefyApi<B>,
RuntimeApi::Api: BeefyApi<B, AuthorityId>,
{
// Voter -> RPC links
let (to_rpc_justif_sender, from_voter_justif_stream) =
@@ -224,7 +225,7 @@ pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
C: Client<B, BE> + BlockBackend<B>,
P: PayloadProvider<B>,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B> + MmrApi<B, MmrRootHash, NumberFor<B>>,
R::Api: BeefyApi<B, AuthorityId> + MmrApi<B, MmrRootHash, NumberFor<B>>,
N: GossipNetwork<B> + NetworkRequest + Send + Sync + 'static,
S: GossipSyncing<B> + SyncOracle + 'static,
{
@@ -339,7 +340,7 @@ where
B: Block,
BE: Backend<B>,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B>,
R::Api: BeefyApi<B, AuthorityId>,
{
// Initialize voter state from AUX DB if compatible.
crate::aux_schema::load_persistent(backend)?
@@ -374,7 +375,7 @@ where
B: Block,
BE: Backend<B>,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B>,
R::Api: BeefyApi<B, AuthorityId>,
{
let beefy_genesis = runtime
.runtime_api()
@@ -478,7 +479,7 @@ async fn wait_for_runtime_pallet<B, R>(
where
B: Block,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B>,
R::Api: BeefyApi<B, AuthorityId>,
{
info!(target: LOG_TARGET, "🥩 BEEFY gadget waiting for BEEFY pallet to become available...");
loop {
@@ -518,7 +519,7 @@ fn expect_validator_set<B, R>(
where
B: Block,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B>,
R::Api: BeefyApi<B, AuthorityId>,
{
runtime
.runtime_api()
@@ -21,7 +21,7 @@ use crate::LOG_TARGET;
use codec::{Decode, Encode};
use log::debug;
use sp_consensus_beefy::{
crypto::{AuthorityId, Signature},
ecdsa_crypto::{AuthorityId, Signature},
Commitment, EquivocationProof, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage,
};
use sp_runtime::traits::{Block, NumberFor};
@@ -48,14 +48,15 @@ use sc_network_test::{
use sc_utils::notification::NotificationReceiver;
use serde::{Deserialize, Serialize};
use sp_api::{ApiRef, ProvideRuntimeApi};
use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE;
use sp_consensus::BlockOrigin;
use sp_consensus_beefy::{
crypto::{AuthorityId, Signature},
ecdsa_crypto::{AuthorityId, Signature},
known_payloads,
mmr::{find_mmr_root_digest, MmrRootProvider},
BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash,
OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId,
VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType,
VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID,
};
use sp_core::H256;
use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr};
@@ -293,7 +294,7 @@ impl ProvideRuntimeApi<Block> for TestApi {
}
}
sp_api::mock_impl_runtime_apis! {
impl BeefyApi<Block> for RuntimeApi {
impl BeefyApi<Block, AuthorityId> for RuntimeApi {
fn beefy_genesis() -> Option<NumberFor<Block>> {
Some(self.inner.beefy_genesis)
}
@@ -352,7 +353,7 @@ pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec<AuthorityId> {
pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> KeystorePtr {
let keystore = MemoryKeystore::new();
keystore
.ecdsa_generate_new(BeefyKeyType, Some(&authority.to_seed()))
.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&authority.to_seed()))
.expect("Creates authority key");
keystore.into()
}
@@ -386,7 +387,7 @@ fn initialize_beefy<API>(
) -> impl Future<Output = ()>
where
API: ProvideRuntimeApi<Block> + Sync + Send,
API::Api: BeefyApi<Block> + MmrApi<Block, MmrRootHash, NumberFor<Block>>,
API::Api: BeefyApi<Block, AuthorityId> + MmrApi<Block, MmrRootHash, NumberFor<Block>>,
{
let tasks = FuturesUnordered::new();
@@ -41,7 +41,7 @@ use sp_arithmetic::traits::{AtLeast32Bit, Saturating};
use sp_consensus::SyncOracle;
use sp_consensus_beefy::{
check_equivocation_proof,
crypto::{AuthorityId, Signature},
ecdsa_crypto::{AuthorityId, Signature},
BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet,
VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID,
};
@@ -348,7 +348,7 @@ where
P: PayloadProvider<B>,
S: SyncOracle,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B>,
R::Api: BeefyApi<B, AuthorityId>,
{
fn best_grandpa_block(&self) -> NumberFor<B> {
*self.persisted_state.voting_oracle.best_grandpa_block_header.number()
+7 -6
View File
@@ -73,8 +73,8 @@ where
/// Convert BEEFY secp256k1 public keys into Ethereum addresses
pub struct BeefyEcdsaToEthereum;
impl Convert<sp_consensus_beefy::crypto::AuthorityId, Vec<u8>> for BeefyEcdsaToEthereum {
fn convert(beefy_id: sp_consensus_beefy::crypto::AuthorityId) -> Vec<u8> {
impl Convert<sp_consensus_beefy::ecdsa_crypto::AuthorityId, Vec<u8>> for BeefyEcdsaToEthereum {
fn convert(beefy_id: sp_consensus_beefy::ecdsa_crypto::AuthorityId) -> Vec<u8> {
sp_core::ecdsa::Public::from(beefy_id)
.to_eth_address()
.map(|v| v.to_vec())
@@ -200,11 +200,12 @@ impl<T: Config> Pallet<T> {
.map(T::BeefyAuthorityToMerkleLeaf::convert)
.collect::<Vec<_>>();
let len = beefy_addresses.len() as u32;
let root = binary_merkle_tree::merkle_root::<<T as pallet_mmr::Config>::Hashing, _>(
beefy_addresses,
)
let keyset_commitment = binary_merkle_tree::merkle_root::<
<T as pallet_mmr::Config>::Hashing,
_,
>(beefy_addresses)
.into();
BeefyAuthoritySet { id, len, root }
BeefyAuthoritySet { id, len, keyset_commitment }
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ use sp_runtime::{
use crate as pallet_beefy_mmr;
pub use sp_consensus_beefy::{
crypto::AuthorityId as BeefyId, mmr::BeefyDataProvider, ConsensusLog, BEEFY_ENGINE_ID,
ecdsa_crypto::AuthorityId as BeefyId, mmr::BeefyDataProvider, ConsensusLog, BEEFY_ENGINE_ID,
};
impl_opaque_keys! {
+6 -6
View File
@@ -119,7 +119,7 @@ fn should_contain_valid_leaf_data() {
beefy_next_authority_set: BeefyNextAuthoritySet {
id: 2,
len: 2,
root: array_bytes::hex_n_into_unchecked(
keyset_commitment: array_bytes::hex_n_into_unchecked(
"9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5"
)
},
@@ -144,7 +144,7 @@ fn should_contain_valid_leaf_data() {
beefy_next_authority_set: BeefyNextAuthoritySet {
id: 3,
len: 2,
root: array_bytes::hex_n_into_unchecked(
keyset_commitment: array_bytes::hex_n_into_unchecked(
"9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5"
)
},
@@ -167,12 +167,12 @@ fn should_update_authorities() {
let want = array_bytes::hex_n_into_unchecked::<_, H256, 32>(
"176e73f1bf656478b728e28dd1a7733c98621b8acf830bff585949763dca7a96",
);
assert_eq!(want, auth_set.root);
assert_eq!(want, auth_set.keyset_commitment);
// next authority set should have same validators but different id
assert_eq!(1, next_auth_set.id);
assert_eq!(auth_set.len, next_auth_set.len);
assert_eq!(auth_set.root, next_auth_set.root);
assert_eq!(auth_set.keyset_commitment, next_auth_set.keyset_commitment);
let announced_set = next_auth_set;
init_block(1);
@@ -188,7 +188,7 @@ fn should_update_authorities() {
"9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5",
);
assert_eq!(2, next_auth_set.len);
assert_eq!(want, next_auth_set.root);
assert_eq!(want, next_auth_set.keyset_commitment);
let announced_set = next_auth_set;
init_block(2);
@@ -204,6 +204,6 @@ fn should_update_authorities() {
"9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5",
);
assert_eq!(2, next_auth_set.len);
assert_eq!(want, next_auth_set.root);
assert_eq!(want, next_auth_set.keyset_commitment);
});
}
+3 -3
View File
@@ -41,7 +41,7 @@ use frame_support::{
};
use frame_system::pallet_prelude::BlockNumberFor;
use log::{error, info};
use sp_consensus_beefy::{EquivocationProof, ValidatorSetId, KEY_TYPE};
use sp_consensus_beefy::{EquivocationProof, ValidatorSetId, KEY_TYPE as BEEFY_KEY_TYPE};
use sp_runtime::{
transaction_validity::{
InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
@@ -172,7 +172,7 @@ where
let (equivocation_proof, key_owner_proof) = evidence;
// Check the membership proof to extract the offender's id
let key = (KEY_TYPE, equivocation_proof.offender_id().clone());
let key = (BEEFY_KEY_TYPE, equivocation_proof.offender_id().clone());
let offender = P::check_proof(key, key_owner_proof).ok_or(InvalidTransaction::BadProof)?;
// Check if the offence has already been reported, and if so then we can discard the report.
@@ -206,7 +206,7 @@ where
let validator_set_count = key_owner_proof.validator_count();
// Validate the key ownership proof extracting the id of the offender.
let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof)
let offender = P::check_proof((BEEFY_KEY_TYPE, offender), key_owner_proof)
.ok_or(Error::<T>::InvalidKeyOwnershipProof)?;
// Validate equivocation proof (check votes are different and signatures are valid).
+1 -1
View File
@@ -39,7 +39,7 @@ use sp_staking::{EraIndex, SessionIndex};
use crate as pallet_beefy;
pub use sp_consensus_beefy::{
crypto::{AuthorityId as BeefyId, AuthoritySignature as BeefySignature},
ecdsa_crypto::{AuthorityId as BeefyId, AuthoritySignature as BeefySignature},
ConsensusLog, EquivocationProof, BEEFY_ENGINE_ID,
};
+9 -16
View File
@@ -20,7 +20,7 @@ use std::vec;
use codec::Encode;
use sp_consensus_beefy::{
check_equivocation_proof, generate_equivocation_proof, known_payloads::MMR_ROOT_ID,
Keyring as BeefyKeyring, Payload, ValidatorSet,
Keyring as BeefyKeyring, Payload, ValidatorSet, KEY_TYPE as BEEFY_KEY_TYPE,
};
use sp_runtime::DigestItem;
@@ -297,8 +297,7 @@ fn report_equivocation_current_set_works() {
);
// create the key ownership proof
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
// report the equivocation and the tx should be dispatched successfully
assert_ok!(Beefy::report_equivocation_unsigned(
@@ -354,8 +353,7 @@ fn report_equivocation_old_set_works() {
let equivocation_key = &authorities[equivocation_authority_index];
// create the key ownership proof in the "old" set
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
start_era(2);
@@ -436,8 +434,7 @@ fn report_equivocation_invalid_set_id() {
let equivocation_key = &authorities[equivocation_authority_index];
let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap();
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]);
let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]);
@@ -475,8 +472,7 @@ fn report_equivocation_invalid_session() {
let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap();
// generate a key ownership proof at current era set id
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
start_era(2);
@@ -520,7 +516,7 @@ fn report_equivocation_invalid_key_owner_proof() {
// generate a key ownership proof for the authority at index 1
let invalid_key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &invalid_owner_key)).unwrap();
Historical::prove((BEEFY_KEY_TYPE, &invalid_owner_key)).unwrap();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index];
@@ -568,8 +564,7 @@ fn report_equivocation_invalid_equivocation_proof() {
let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap();
// generate a key ownership proof at set id in era 1
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
let assert_invalid_equivocation_proof = |equivocation_proof| {
assert_err!(
@@ -649,8 +644,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() {
(block_num, payload2, set_id, &equivocation_keyring),
);
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
let call = Call::report_equivocation_unsigned {
equivocation_proof: Box::new(equivocation_proof.clone()),
@@ -755,8 +749,7 @@ fn valid_equivocation_reports_dont_pay_fees() {
);
// create the key ownership proof.
let key_owner_proof =
Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap();
let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
// check the dispatch info for the call.
let info = Call::<Test>::report_equivocation_unsigned {
@@ -54,4 +54,7 @@ full_crypto = [
# This feature adds BLS crypto primitives. It should not be used in production since
# the BLS implementation and interface may still be subject to significant change.
bls-experimental = [ "sp-core/bls-experimental" ]
bls-experimental = [
"sp-core/bls-experimental",
"sp-io/bls-experimental",
]
@@ -16,6 +16,7 @@
// limitations under the License.
//! BLS12-377 crypto applications.
use crate::{KeyTypeId, RuntimePublic};
pub use sp_core::bls::bls377::*;
@@ -26,3 +27,30 @@ mod app {
#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;
pub use app::{Public as AppPublic, Signature as AppSignature};
impl RuntimePublic for Public {
type Signature = Signature;
/// Dummy implementation. Returns an empty vector.
fn all(_key_type: KeyTypeId) -> Vec<Self> {
Vec::new()
}
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
sp_io::crypto::bls377_generate(key_type, seed)
}
/// Dummy implementation. Returns `None`.
fn sign<M: AsRef<[u8]>>(&self, _key_type: KeyTypeId, _msg: &M) -> Option<Self::Signature> {
None
}
/// Dummy implementation. Returns `false`.
fn verify<M: AsRef<[u8]>>(&self, _msg: &M, _signature: &Self::Signature) -> bool {
false
}
fn to_raw_vec(&self) -> Vec<u8> {
sp_core::crypto::ByteArray::to_raw_vec(self)
}
}
@@ -27,7 +27,7 @@ lazy_static = "1.4.0"
[dev-dependencies]
array-bytes = "6.1"
sp-keystore = { version = "0.27.0", path = "../../keystore" }
w3f-bls = { version = "0.1.3", features = ["std"]}
[features]
default = ["std"]
@@ -42,7 +42,6 @@ std = [
"sp-mmr-primitives/std",
"sp-runtime/std",
"sp-std/std",
"sp-keystore/std"
]
# Serde support without relying on std features.
@@ -53,3 +52,10 @@ serde = [
"sp-core/serde",
"sp-runtime/serde",
]
# This feature adds BLS crypto primitives. It should not be used in production since
# the BLS implementation and interface may still be subject to significant change.
bls-experimental = [
"sp-core/bls-experimental",
"sp-application-crypto/bls-experimental",
]
@@ -249,30 +249,60 @@ impl<N, S> From<SignedCommitment<N, S>> for VersionedFinalityProof<N, S> {
#[cfg(test)]
mod tests {
use super::*;
use crate::{crypto, known_payloads, KEY_TYPE};
use crate::{ecdsa_crypto::Signature as EcdsaSignature, known_payloads};
use codec::Decode;
use sp_core::{keccak_256, Pair};
use sp_keystore::{testing::MemoryKeystore, KeystorePtr};
#[cfg(feature = "bls-experimental")]
use crate::bls_crypto::Signature as BlsSignature;
type TestCommitment = Commitment<u128>;
type TestSignedCommitment = SignedCommitment<u128, crypto::Signature>;
type TestVersionedFinalityProof = VersionedFinalityProof<u128, crypto::Signature>;
const LARGE_RAW_COMMITMENT: &[u8] = include_bytes!("../test-res/large-raw-commitment");
// The mock signatures are equivalent to the ones produced by the BEEFY keystore
fn mock_signatures() -> (crypto::Signature, crypto::Signature) {
let store: KeystorePtr = MemoryKeystore::new().into();
// Types for bls-less commitment
type TestEcdsaSignedCommitment = SignedCommitment<u128, EcdsaSignature>;
type TestVersionedFinalityProof = VersionedFinalityProof<u128, EcdsaSignature>;
// Types for commitment supporting aggregatable bls signature
#[cfg(feature = "bls-experimental")]
#[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
struct BlsAggregatableSignature(BlsSignature);
#[cfg(feature = "bls-experimental")]
#[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
struct EcdsaBlsSignaturePair(EcdsaSignature, BlsSignature);
#[cfg(feature = "bls-experimental")]
type TestBlsSignedCommitment = SignedCommitment<u128, EcdsaBlsSignaturePair>;
// Generates mock aggregatable ecdsa signature for generating test commitment
// BLS signatures
fn mock_ecdsa_signatures() -> (EcdsaSignature, EcdsaSignature) {
let alice = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
store.insert(KEY_TYPE, "//Alice", alice.public().as_ref()).unwrap();
let msg = keccak_256(b"This is the first message");
let sig1 = store.ecdsa_sign_prehashed(KEY_TYPE, &alice.public(), &msg).unwrap().unwrap();
let sig1 = alice.sign_prehashed(&msg);
let msg = keccak_256(b"This is the second message");
let sig2 = store.ecdsa_sign_prehashed(KEY_TYPE, &alice.public(), &msg).unwrap().unwrap();
let sig2 = alice.sign_prehashed(&msg);
(sig1.into(), sig2.into())
}
// Generates mock aggregatable bls signature for generating test commitment
// BLS signatures
#[cfg(feature = "bls-experimental")]
fn mock_bls_signatures() -> (BlsSignature, BlsSignature) {
let alice = sp_core::bls::Pair::from_string("//Alice", None).unwrap();
let msg = b"This is the first message";
let sig1 = alice.sign(msg);
let msg = b"This is the second message";
let sig2 = alice.sign(msg);
(sig1.into(), sig2.into())
}
@@ -300,26 +330,26 @@ mod tests {
}
#[test]
fn signed_commitment_encode_decode() {
fn signed_commitment_encode_decode_ecdsa() {
// given
let payload =
Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let sigs = mock_signatures();
let ecdsa_sigs = mock_ecdsa_signatures();
let signed = SignedCommitment {
commitment,
signatures: vec![None, None, Some(sigs.0), Some(sigs.1)],
let ecdsa_signed = SignedCommitment {
commitment: commitment.clone(),
signatures: vec![None, None, Some(ecdsa_sigs.0.clone()), Some(ecdsa_sigs.1.clone())],
};
// when
let encoded = codec::Encode::encode(&signed);
let decoded = TestSignedCommitment::decode(&mut &*encoded);
let encoded = codec::Encode::encode(&ecdsa_signed);
let decoded = TestEcdsaSignedCommitment::decode(&mut &*encoded);
// then
assert_eq!(decoded, Ok(signed));
assert_eq!(decoded, Ok(ecdsa_signed));
assert_eq!(
encoded,
array_bytes::hex2bytes_unchecked(
@@ -334,6 +364,44 @@ mod tests {
);
}
#[test]
#[cfg(feature = "bls-experimental")]
fn signed_commitment_encode_decode_ecdsa_n_bls() {
// given
let payload =
Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let ecdsa_sigs = mock_ecdsa_signatures();
//including bls signature
let bls_signed_msgs = mock_bls_signatures();
let ecdsa_and_bls_signed = SignedCommitment {
commitment,
signatures: vec![
None,
None,
Some(EcdsaBlsSignaturePair(ecdsa_sigs.0, bls_signed_msgs.0)),
Some(EcdsaBlsSignaturePair(ecdsa_sigs.1, bls_signed_msgs.1)),
],
};
//when
let encoded = codec::Encode::encode(&ecdsa_and_bls_signed);
let decoded = TestBlsSignedCommitment::decode(&mut &*encoded);
// then
assert_eq!(decoded, Ok(ecdsa_and_bls_signed));
assert_eq!(
encoded,
array_bytes::hex2bytes_unchecked(
"046d68343048656c6c6f20576f726c642105000000000000000000000000000000000000000000000004300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba01667603fc041cf9d7147d22bf54b15e5778893d6986b71a929747befd3b4d233fbe668bc480e8865116b94db46ca25a01e03c71955f2582604e415da68f2c3c406b9d5f4ad416230ec5453f05ac16a50d8d0923dfb0413cc956ae3fa6334465bd1f2cacec8e9cd606438390fe2a29dc052d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a00df61d3b2be0963eb6caa243cc505d327aec73e1bb7ffe9a14b1354b0c406792ac6d6f47c06987c15dec9993f43eefa001d866fe0850d986702c414840f0d9ec0fdc04832ef91ae37c8d49e2f573ca50cb37f152801d489a19395cb04e5fc8f2ab6954b58a3bcc40ef9b6409d2ff7ef07"
)
);
}
#[test]
fn signed_commitment_count_signatures() {
// given
@@ -342,7 +410,7 @@ mod tests {
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let sigs = mock_signatures();
let sigs = mock_ecdsa_signatures();
let mut signed = SignedCommitment {
commitment,
@@ -389,7 +457,7 @@ mod tests {
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let sigs = mock_signatures();
let sigs = mock_ecdsa_signatures();
let signed = SignedCommitment {
commitment,
@@ -416,7 +484,7 @@ mod tests {
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let sigs = mock_signatures();
let sigs = mock_ecdsa_signatures();
let signatures: Vec<Option<_>> = (0..1024)
.into_iter()
@@ -426,7 +494,7 @@ mod tests {
// when
let encoded = codec::Encode::encode(&signed);
let decoded = TestSignedCommitment::decode(&mut &*encoded);
let decoded = TestEcdsaSignedCommitment::decode(&mut &*encoded);
// then
assert_eq!(decoded, Ok(signed));
+76 -22
View File
@@ -51,7 +51,7 @@ use sp_runtime::traits::{Hash, Keccak256, NumberFor};
use sp_std::prelude::*;
/// Key type for BEEFY module.
pub const KEY_TYPE: sp_application_crypto::KeyTypeId = sp_application_crypto::KeyTypeId(*b"beef");
pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BEEFY;
/// Trait representing BEEFY authority id, including custom signature verification.
///
@@ -63,23 +63,21 @@ pub trait BeefyAuthorityId<MsgHash: Hash>: RuntimeAppPublic {
fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool;
}
/// BEEFY cryptographic types
/// BEEFY cryptographic types for ECDSA crypto
///
/// This module basically introduces three crypto types:
/// - `crypto::Pair`
/// - `crypto::Public`
/// - `crypto::Signature`
/// This module basically introduces four crypto types:
/// - `ecdsa_crypto::Pair`
/// - `ecdsa_crypto::Public`
/// - `ecdsa_crypto::Signature`
/// - `ecdsa_crypto::AuthorityId`
///
/// Your code should use the above types as concrete types for all crypto related
/// functionality.
///
/// The current underlying crypto scheme used is ECDSA. This can be changed,
/// without affecting code restricted against the above listed crypto types.
pub mod crypto {
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic};
pub mod ecdsa_crypto {
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE as BEEFY_KEY_TYPE};
use sp_application_crypto::{app_crypto, ecdsa};
use sp_core::crypto::Wraps;
app_crypto!(ecdsa, crate::KEY_TYPE);
app_crypto!(ecdsa, BEEFY_KEY_TYPE);
/// Identity of a BEEFY authority using ECDSA as its crypto.
pub type AuthorityId = Public;
@@ -104,6 +102,44 @@ pub mod crypto {
}
}
/// BEEFY cryptographic types for BLS crypto
///
/// This module basically introduces four crypto types:
/// - `bls_crypto::Pair`
/// - `bls_crypto::Public`
/// - `bls_crypto::Signature`
/// - `bls_crypto::AuthorityId`
///
/// Your code should use the above types as concrete types for all crypto related
/// functionality.
#[cfg(feature = "bls-experimental")]
pub mod bls_crypto {
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE as BEEFY_KEY_TYPE};
use sp_application_crypto::{app_crypto, bls377};
use sp_core::{bls377::Pair as BlsPair, crypto::Wraps, Pair as _};
app_crypto!(bls377, BEEFY_KEY_TYPE);
/// Identity of a BEEFY authority using BLS as its crypto.
pub type AuthorityId = Public;
/// Signature for a BEEFY authority using BLS as its crypto.
pub type AuthoritySignature = Signature;
impl<MsgHash: Hash> BeefyAuthorityId<MsgHash> for AuthorityId
where
<MsgHash as Hash>::Output: Into<[u8; 32]>,
{
fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
// `w3f-bls` library uses IETF hashing standard and as such does not exposes
// a choice of hash to field function.
// We are directly calling into the library to avoid introducing new host call.
// and because BeefyAuthorityId::verify is being called in the runtime so we don't have
BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
}
}
}
/// The `ConsensusEngineId` of BEEFY.
pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
@@ -304,14 +340,15 @@ impl OpaqueKeyOwnershipProof {
sp_api::decl_runtime_apis! {
/// API necessary for BEEFY voters.
#[api_version(2)]
pub trait BeefyApi
#[api_version(3)]
pub trait BeefyApi<AuthorityId> where
AuthorityId : Codec + RuntimeAppPublic,
{
/// Return the block number where BEEFY consensus is enabled/started
fn beefy_genesis() -> Option<NumberFor<Block>>;
/// Return the current active BEEFY validator set
fn validator_set() -> Option<ValidatorSet<crypto::AuthorityId>>;
fn validator_set() -> Option<ValidatorSet<AuthorityId>>;
/// Submits an unsigned extrinsic to report an equivocation. The caller
/// must provide the equivocation proof and a key ownership proof
@@ -323,7 +360,7 @@ sp_api::decl_runtime_apis! {
/// hardcoded to return `None`). Only useful in an offchain context.
fn submit_report_equivocation_unsigned_extrinsic(
equivocation_proof:
EquivocationProof<NumberFor<Block>, crypto::AuthorityId, crypto::Signature>,
EquivocationProof<NumberFor<Block>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
key_owner_proof: OpaqueKeyOwnershipProof,
) -> Option<()>;
@@ -340,9 +377,10 @@ sp_api::decl_runtime_apis! {
/// older states to be available.
fn generate_key_ownership_proof(
set_id: ValidatorSetId,
authority_id: crypto::AuthorityId,
authority_id: AuthorityId,
) -> Option<OpaqueKeyOwnershipProof>;
}
}
#[cfg(test)]
@@ -366,14 +404,14 @@ mod tests {
}
#[test]
fn beefy_verify_works() {
fn ecdsa_beefy_verify_works() {
let msg = &b"test-message"[..];
let (pair, _) = crypto::Pair::generate();
let (pair, _) = ecdsa_crypto::Pair::generate();
let keccak_256_signature: crypto::Signature =
let keccak_256_signature: ecdsa_crypto::Signature =
pair.as_inner_ref().sign_prehashed(&keccak_256(msg)).into();
let blake2_256_signature: crypto::Signature =
let blake2_256_signature: ecdsa_crypto::Signature =
pair.as_inner_ref().sign_prehashed(&blake2_256(msg)).into();
// Verification works if same hashing function is used when signing and verifying.
@@ -392,7 +430,7 @@ mod tests {
));
// Other public key doesn't work
let (other_pair, _) = crypto::Pair::generate();
let (other_pair, _) = ecdsa_crypto::Pair::generate();
assert!(!BeefyAuthorityId::<Keccak256>::verify(
&other_pair.public(),
&keccak_256_signature,
@@ -404,4 +442,20 @@ mod tests {
msg,
));
}
#[test]
#[cfg(feature = "bls-experimental")]
fn bls_beefy_verify_works() {
let msg = &b"test-message"[..];
let (pair, _) = bls_crypto::Pair::generate();
let signature: bls_crypto::Signature = pair.as_inner_ref().sign(&msg).into();
// Verification works if same hashing function is used when signing and verifying.
assert!(BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &signature, msg));
// Other public key doesn't work
let (other_pair, _) = bls_crypto::Pair::generate();
assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
}
}
@@ -26,7 +26,7 @@
//! but we imagine they will be useful for other chains that either want to bridge with Polkadot
//! or are completely standalone, but heavily inspired by Polkadot.
use crate::{crypto::AuthorityId, ConsensusLog, MmrRootHash, Vec, BEEFY_ENGINE_ID};
use crate::{ecdsa_crypto::AuthorityId, ConsensusLog, MmrRootHash, Vec, BEEFY_ENGINE_ID};
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{
@@ -102,7 +102,7 @@ impl MmrLeafVersion {
/// Details of a BEEFY authority set.
#[derive(Debug, Default, PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BeefyAuthoritySet<MerkleRoot> {
pub struct BeefyAuthoritySet<AuthoritySetCommitment> {
/// Id of the set.
///
/// Id is required to correlate BEEFY signed commitments with the validator set.
@@ -115,12 +115,19 @@ pub struct BeefyAuthoritySet<MerkleRoot> {
/// of signatures. We put set length here, so that these clients can verify the minimal
/// number of required signatures.
pub len: u32,
/// Merkle Root Hash built from BEEFY AuthorityIds.
/// Commitment(s) to BEEFY AuthorityIds.
///
/// This is used by Light Clients to confirm that the commitments are signed by the correct
/// validator set. Light Clients using interactive protocol, might verify only subset of
/// signatures, hence don't require the full list here (will receive inclusion proofs).
pub root: MerkleRoot,
///
/// This could be Merkle Root Hash built from BEEFY ECDSA public keys and/or
/// polynomial commitment to the polynomial interpolating BLS public keys
/// which is used by APK proof based light clients to verify the validity
/// of aggregated BLS keys using APK proofs.
/// Multiple commitments can be tupled together.
pub keyset_commitment: AuthoritySetCommitment,
}
/// Details of the next BEEFY authority set.
@@ -17,13 +17,13 @@
#![cfg(feature = "std")]
use crate::{crypto, Commitment, EquivocationProof, Payload, ValidatorSetId, VoteMessage};
use crate::{ecdsa_crypto, Commitment, EquivocationProof, Payload, ValidatorSetId, VoteMessage};
use codec::Encode;
use sp_core::{ecdsa, keccak_256, Pair};
use std::collections::HashMap;
use strum::IntoEnumIterator;
/// Set of test accounts using [`crate::crypto`] types.
/// Set of test accounts using [`crate::ecdsa_crypto`] types.
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)]
pub enum Keyring {
@@ -39,19 +39,19 @@ pub enum Keyring {
impl Keyring {
/// Sign `msg`.
pub fn sign(self, msg: &[u8]) -> crypto::Signature {
pub fn sign(self, msg: &[u8]) -> ecdsa_crypto::Signature {
// todo: use custom signature hashing type
let msg = keccak_256(msg);
ecdsa::Pair::from(self).sign_prehashed(&msg).into()
}
/// Return key pair.
pub fn pair(self) -> crypto::Pair {
pub fn pair(self) -> ecdsa_crypto::Pair {
ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into()
}
/// Return public key.
pub fn public(self) -> crypto::Public {
pub fn public(self) -> ecdsa_crypto::Public {
self.pair().public()
}
@@ -61,19 +61,19 @@ impl Keyring {
}
/// Get Keyring from public key.
pub fn from_public(who: &crypto::Public) -> Option<Keyring> {
Self::iter().find(|&k| &crypto::Public::from(k) == who)
pub fn from_public(who: &ecdsa_crypto::Public) -> Option<Keyring> {
Self::iter().find(|&k| &ecdsa_crypto::Public::from(k) == who)
}
}
lazy_static::lazy_static! {
static ref PRIVATE_KEYS: HashMap<Keyring, crypto::Pair> =
static ref PRIVATE_KEYS: HashMap<Keyring, ecdsa_crypto::Pair> =
Keyring::iter().map(|i| (i, i.pair())).collect();
static ref PUBLIC_KEYS: HashMap<Keyring, crypto::Public> =
static ref PUBLIC_KEYS: HashMap<Keyring, ecdsa_crypto::Public> =
PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect();
}
impl From<Keyring> for crypto::Pair {
impl From<Keyring> for ecdsa_crypto::Pair {
fn from(k: Keyring) -> Self {
k.pair()
}
@@ -85,7 +85,7 @@ impl From<Keyring> for ecdsa::Pair {
}
}
impl From<Keyring> for crypto::Public {
impl From<Keyring> for ecdsa_crypto::Public {
fn from(k: Keyring) -> Self {
(*PUBLIC_KEYS).get(&k).cloned().unwrap()
}
@@ -95,7 +95,7 @@ impl From<Keyring> for crypto::Public {
pub fn generate_equivocation_proof(
vote1: (u64, Payload, ValidatorSetId, &Keyring),
vote2: (u64, Payload, ValidatorSetId, &Keyring),
) -> EquivocationProof<u64, crypto::Public, crypto::Signature> {
) -> EquivocationProof<u64, ecdsa_crypto::Public, ecdsa_crypto::Signature> {
let signed_vote = |block_number: u64,
payload: Payload,
validator_set_id: ValidatorSetId,
@@ -37,18 +37,21 @@ use crate::commitment::{Commitment, SignedCommitment};
/// Ethereum Mainnet), in a commit-reveal like scheme, where first we submit only the signed
/// commitment witness and later on, the client picks only some signatures to verify at random.
#[derive(Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
pub struct SignedCommitmentWitness<TBlockNumber, TMerkleRoot> {
pub struct SignedCommitmentWitness<TBlockNumber, TSignatureAccumulator> {
/// The full content of the commitment.
pub commitment: Commitment<TBlockNumber>,
/// The bit vector of validators who signed the commitment.
pub signed_by: Vec<bool>, // TODO [ToDr] Consider replacing with bitvec crate
/// A merkle root of signatures in the original signed commitment.
pub signatures_merkle_root: TMerkleRoot,
/// Either a merkle root of signatures in the original signed commitment or a single aggregated
/// BLS signature aggregating all original signatures.
pub signature_accumulator: TSignatureAccumulator,
}
impl<TBlockNumber, TMerkleRoot> SignedCommitmentWitness<TBlockNumber, TMerkleRoot> {
impl<TBlockNumber, TSignatureAccumulator>
SignedCommitmentWitness<TBlockNumber, TSignatureAccumulator>
{
/// Convert [SignedCommitment] into [SignedCommitmentWitness].
///
/// This takes a [SignedCommitment], which contains full signatures
@@ -57,53 +60,85 @@ impl<TBlockNumber, TMerkleRoot> SignedCommitmentWitness<TBlockNumber, TMerkleRoo
/// and a merkle root of all signatures.
///
/// Returns the full list of signatures along with the witness.
pub fn from_signed<TMerkelize, TSignature>(
pub fn from_signed<TSignatureAggregator, TSignature>(
signed: SignedCommitment<TBlockNumber, TSignature>,
merkelize: TMerkelize,
aggregator: TSignatureAggregator,
) -> (Self, Vec<Option<TSignature>>)
where
TMerkelize: FnOnce(&[Option<TSignature>]) -> TMerkleRoot,
TSignatureAggregator: FnOnce(&[Option<TSignature>]) -> TSignatureAccumulator,
{
let SignedCommitment { commitment, signatures } = signed;
let signed_by = signatures.iter().map(|s| s.is_some()).collect();
let signatures_merkle_root = merkelize(&signatures);
let signature_accumulator = aggregator(&signatures);
(Self { commitment, signed_by, signatures_merkle_root }, signatures)
(Self { commitment, signed_by, signature_accumulator }, signatures)
}
}
#[cfg(test)]
mod tests {
use sp_core::{keccak_256, Pair};
use sp_keystore::{testing::MemoryKeystore, KeystorePtr};
use super::*;
use codec::Decode;
use crate::{crypto, known_payloads, Payload, KEY_TYPE};
use crate::{ecdsa_crypto::Signature as EcdsaSignature, known_payloads, Payload};
#[cfg(feature = "bls-experimental")]
use crate::bls_crypto::Signature as BlsSignature;
#[cfg(feature = "bls-experimental")]
use w3f_bls::{
single_pop_aggregator::SignatureAggregatorAssumingPoP, Message, SerializableToBytes,
Signed, TinyBLS377,
};
type TestCommitment = Commitment<u128>;
type TestSignedCommitment = SignedCommitment<u128, crypto::Signature>;
type TestSignedCommitmentWitness =
SignedCommitmentWitness<u128, Vec<Option<crypto::Signature>>>;
// Types for ecdsa signed commitment.
type TestEcdsaSignedCommitment = SignedCommitment<u128, EcdsaSignature>;
type TestEcdsaSignedCommitmentWitness =
SignedCommitmentWitness<u128, Vec<Option<EcdsaSignature>>>;
#[cfg(feature = "bls-experimental")]
#[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
struct EcdsaBlsSignaturePair(EcdsaSignature, BlsSignature);
// types for commitment containing bls signature along side ecdsa signature
#[cfg(feature = "bls-experimental")]
type TestBlsSignedCommitment = SignedCommitment<u128, EcdsaBlsSignaturePair>;
#[cfg(feature = "bls-experimental")]
type TestBlsSignedCommitmentWitness = SignedCommitmentWitness<u128, Vec<u8>>;
// The mock signatures are equivalent to the ones produced by the BEEFY keystore
fn mock_signatures() -> (crypto::Signature, crypto::Signature) {
let store: KeystorePtr = MemoryKeystore::new().into();
fn mock_ecdsa_signatures() -> (EcdsaSignature, EcdsaSignature) {
let alice = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
store.insert(KEY_TYPE, "//Alice", alice.public().as_ref()).unwrap();
let msg = keccak_256(b"This is the first message");
let sig1 = store.ecdsa_sign_prehashed(KEY_TYPE, &alice.public(), &msg).unwrap().unwrap();
let sig1 = alice.sign_prehashed(&msg);
let msg = keccak_256(b"This is the second message");
let sig2 = store.ecdsa_sign_prehashed(KEY_TYPE, &alice.public(), &msg).unwrap().unwrap();
let sig2 = alice.sign_prehashed(&msg);
(sig1.into(), sig2.into())
}
fn signed_commitment() -> TestSignedCommitment {
// Generates mock aggregatable bls signature for generating test commitment
// BLS signatures
#[cfg(feature = "bls-experimental")]
fn mock_bls_signatures() -> (BlsSignature, BlsSignature) {
let alice = sp_core::bls::Pair::from_string("//Alice", None).unwrap();
let msg = b"This is the first message";
let sig1 = alice.sign(msg);
let msg = b"This is the second message";
let sig2 = alice.sign(msg);
(sig1.into(), sig2.into())
}
fn ecdsa_signed_commitment() -> TestEcdsaSignedCommitment {
let payload = Payload::from_single_entry(
known_payloads::MMR_ROOT_ID,
"Hello World!".as_bytes().to_vec(),
@@ -111,35 +146,97 @@ mod tests {
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let sigs = mock_signatures();
let sigs = mock_ecdsa_signatures();
SignedCommitment { commitment, signatures: vec![None, None, Some(sigs.0), Some(sigs.1)] }
}
#[cfg(feature = "bls-experimental")]
fn ecdsa_and_bls_signed_commitment() -> TestBlsSignedCommitment {
let payload = Payload::from_single_entry(
known_payloads::MMR_ROOT_ID,
"Hello World!".as_bytes().to_vec(),
);
let commitment: TestCommitment =
Commitment { payload, block_number: 5, validator_set_id: 0 };
let ecdsa_sigs = mock_ecdsa_signatures();
let bls_sigs = mock_bls_signatures();
SignedCommitment {
commitment,
signatures: vec![
None,
None,
Some(EcdsaBlsSignaturePair(ecdsa_sigs.0, bls_sigs.0)),
Some(EcdsaBlsSignaturePair(ecdsa_sigs.1, bls_sigs.1)),
],
}
}
#[test]
fn should_convert_signed_commitment_to_witness() {
// given
let signed = signed_commitment();
let signed = ecdsa_signed_commitment();
// when
let (witness, signatures) =
TestSignedCommitmentWitness::from_signed(signed, |sigs| sigs.to_vec());
TestEcdsaSignedCommitmentWitness::from_signed(signed, |sigs| sigs.to_vec());
// then
assert_eq!(witness.signatures_merkle_root, signatures);
assert_eq!(witness.signature_accumulator, signatures);
}
#[test]
#[cfg(feature = "bls-experimental")]
fn should_convert_dually_signed_commitment_to_witness() {
// given
let signed = ecdsa_and_bls_signed_commitment();
// when
let (witness, _signatures) =
// from signed take a function as the aggregator
TestBlsSignedCommitmentWitness::from_signed::<_, _>(signed, |sigs| {
// we are going to aggregate the signatures here
let mut aggregatedsigs: SignatureAggregatorAssumingPoP<TinyBLS377> =
SignatureAggregatorAssumingPoP::new(Message::new(b"", b"mock payload"));
for sig in sigs {
match sig {
Some(sig) => {
let serialized_sig : Vec<u8> = (*sig.1).to_vec();
aggregatedsigs.add_signature(
&w3f_bls::Signature::<TinyBLS377>::from_bytes(
serialized_sig.as_slice()
).unwrap()
);
},
None => (),
}
}
(&aggregatedsigs).signature().to_bytes()
});
// We can't use BlsSignature::try_from because it expected 112Bytes (CP (64) + BLS 48)
// single signature while we are having a BLS aggregated signature corresponding to no CP.
w3f_bls::Signature::<TinyBLS377>::from_bytes(witness.signature_accumulator.as_slice())
.unwrap();
}
#[test]
fn should_encode_and_decode_witness() {
// given
let signed = signed_commitment();
let (witness, _) = TestSignedCommitmentWitness::from_signed(signed, |sigs| sigs.to_vec());
// Given
let signed = ecdsa_signed_commitment();
let (witness, _) = TestEcdsaSignedCommitmentWitness::from_signed::<_, _>(
signed,
|sigs: &[std::option::Option<EcdsaSignature>]| sigs.to_vec(),
);
// when
// When
let encoded = codec::Encode::encode(&witness);
let decoded = TestSignedCommitmentWitness::decode(&mut &*encoded);
let decoded = TestEcdsaSignedCommitmentWitness::decode(&mut &*encoded);
// then
// Then
assert_eq!(decoded, Ok(witness));
assert_eq!(
encoded,
-1
View File
@@ -60,7 +60,6 @@ w3f-bls = { version = "0.1.3", default-features = false, optional = true}
criterion = "0.4.0"
serde_json = "1.0"
sp-core-hashing-proc-macro = { version = "9.0.0", path = "./hashing/proc-macro" }
hex-literal = "0.3.4"
[[bench]]
name = "bench"
+19 -12
View File
@@ -34,7 +34,7 @@ use w3f_bls::{DoublePublicKey, DoubleSignature, EngineBLS, SerializableToBytes,
#[cfg(feature = "full_crypto")]
use w3f_bls::{DoublePublicKeyScheme, Keypair, Message, SecretKey};
use sp_runtime_interface::pass_by::PassByInner;
use sp_runtime_interface::pass_by::{self, PassBy, PassByInner};
use sp_std::{convert::TryFrom, marker::PhantomData, ops::Deref};
/// BLS-377 specialized types
@@ -165,6 +165,10 @@ impl<T> PassByInner for Public<T> {
}
}
impl<T> PassBy for Public<T> {
type PassBy = pass_by::Inner<Self, [u8; PUBLIC_KEY_SERIALIZED_SIZE]>;
}
impl<T> AsRef<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public<T> {
fn as_ref(&self) -> &[u8; PUBLIC_KEY_SERIALIZED_SIZE] {
&self.inner
@@ -514,7 +518,6 @@ mod test {
use super::*;
use crate::crypto::DEV_PHRASE;
use bls377::{Pair, Signature};
use hex_literal::hex;
#[test]
fn default_phrase_should_be_used() {
@@ -529,7 +532,9 @@ mod test {
// Only passes if the seed = (seed mod ScalarField)
#[test]
fn seed_and_derive_should_work() {
let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f00");
let seed = array_bytes::hex2array_unchecked(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f00",
);
let pair = Pair::from_seed(&seed);
// we are using hash to field so this is not going to work
// assert_eq!(pair.seed(), seed);
@@ -537,25 +542,27 @@ mod test {
let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
assert_eq!(
derived.to_raw_vec(),
hex!("a4f2269333b3e87c577aa00c4a2cd650b3b30b2e8c286a47c251279ff3a26e0d")
array_bytes::hex2array_unchecked::<_, 32>(
"a4f2269333b3e87c577aa00c4a2cd650b3b30b2e8c286a47c251279ff3a26e0d"
)
);
}
#[test]
fn test_vector_should_work() {
let pair = Pair::from_seed(&hex!(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"
let pair = Pair::from_seed(&array_bytes::hex2array_unchecked(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
));
let public = pair.public();
assert_eq!(
public,
Public::unchecked_from(hex!(
Public::unchecked_from(array_bytes::hex2array_unchecked(
"7a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400"
))
);
let message = b"";
let signature =
hex!("d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b"
array_bytes::hex2array_unchecked("d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b"
);
let signature = Signature::unchecked_from(signature);
assert!(pair.sign(&message[..]) == signature);
@@ -572,13 +579,13 @@ mod test {
let public = pair.public();
assert_eq!(
public,
Public::unchecked_from(hex!(
Public::unchecked_from(array_bytes::hex2array_unchecked(
"6dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780"
))
);
let message = b"";
let signature =
hex!("bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c"
array_bytes::hex2array_unchecked("bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c"
);
let expected_signature = Signature::unchecked_from(signature);
println!("signature is {:?}", pair.sign(&message[..]));
@@ -603,12 +610,12 @@ mod test {
assert_eq!(
public,
Public::unchecked_from(
hex!(
array_bytes::hex2array_unchecked(
"754d2f2bbfa67df54d7e0e951979a18a1e0f45948857752cc2bac6bbb0b1d05e8e48bcc453920bf0c4bbd5993212480112a1fb433f04d74af0a8b700d93dc957ab3207f8d071e948f5aca1a7632c00bdf6d06be05b43e2e6216dccc8a5d55a0071cb2313cfd60b7e9114619cd17c06843b352f0b607a99122f6651df8f02e1ad3697bd208e62af047ddd7b942ba80080")
)
);
let message =
hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"
array_bytes::hex2bytes_unchecked("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"
);
let signature = pair.sign(&message[..]);
println!("Correct signature: {:?}", signature);
+2
View File
@@ -1144,6 +1144,8 @@ pub mod key_types {
pub const ACCOUNT: KeyTypeId = KeyTypeId(*b"acco");
/// Key type for Aura module, built-in. Identified as `aura`.
pub const AURA: KeyTypeId = KeyTypeId(*b"aura");
/// Key type for BEEFY module.
pub const BEEFY: KeyTypeId = KeyTypeId(*b"beef");
/// Key type for ImOnline module, built-in. Identified as `imon`.
pub const IM_ONLINE: KeyTypeId = KeyTypeId(*b"imon");
/// Key type for AuthorityDiscovery module, built-in. Identified as `audi`.
+6
View File
@@ -92,3 +92,9 @@ disable_allocator = []
# host function to be supported by the host. Do *not* enable it for your
# runtime without first upgrading your host client!
improved_panic_error_reporting = []
# This feature adds BLS crypto primitives. It should not be used in production since
# the BLS implementation and interface may still be subject to significant change.
bls-experimental = [
"sp-keystore/bls-experimental",
]
+18
View File
@@ -103,6 +103,9 @@ use sp_core::{
LogLevel, LogLevelFilter, OpaquePeerId, H256,
};
#[cfg(feature = "bls-experimental")]
use sp_core::bls377;
#[cfg(feature = "std")]
use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
@@ -1186,6 +1189,21 @@ pub trait Crypto {
.map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize())
}
#[cfg(feature = "bls-experimental")]
/// Generate an `bls12-377` key for the given key type using an optional `seed` and
/// store it in the keystore.
///
/// The `seed` needs to be a valid utf8.
///
/// Returns the public key.
fn bls377_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> bls377::Public {
let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.bls377_generate_new(id, seed)
.expect("`bls377_generate` failed")
}
}
/// Interface that provides functions for hashing with different algorithms.