diff --git a/Cargo.lock b/Cargo.lock index e50baf6e6687f..9abfcdb5971ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5127,8 +5127,11 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ + "atty", + "humantime", "log", "regex", + "termcolor", ] [[package]] @@ -6699,6 +6702,19 @@ dependencies = [ "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -7060,6 +7076,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpc" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" +dependencies = [ + "base64 0.13.1", + "serde", + "serde_json", +] + [[package]] name = "jsonrpsee" version = "0.22.5" @@ -8720,6 +8747,23 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "netlink-packet-core" version = "0.4.2" @@ -15903,10 +15947,12 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite 0.2.12", @@ -15916,6 +15962,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls 0.24.1", "tower-service", "url", @@ -16991,11 +17038,14 @@ dependencies = [ "array-bytes", "async-channel", "async-trait", + "env_logger 0.8.4", "fnv", "futures", + "jsonrpc", "log", "parity-scale-codec", "parking_lot 0.12.1", + "reqwest", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -17006,6 +17056,7 @@ dependencies = [ "sc-network-types", "sc-utils", "serde", + "serde_json", "sp-api", "sp-application-crypto", "sp-arithmetic", @@ -19643,6 +19694,7 @@ dependencies = [ name = "sp-consensus-beefy" version = "13.0.0" dependencies = [ + "ark-serialize 0.4.2", "array-bytes", "lazy_static", "parity-scale-codec", @@ -21797,6 +21849,16 @@ dependencies = [ "syn 2.0.61", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-retry" version = "0.3.0" @@ -22539,9 +22601,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "w3f-bls" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" +checksum = "9c5da5fa2c6afa2c9158eaa7cd9aee249765eb32b5fb0c63ad8b9e79336a47ec" dependencies = [ "ark-bls12-377", "ark-bls12-381", diff --git a/substrate/client/consensus/beefy/Cargo.toml b/substrate/client/consensus/beefy/Cargo.toml index cd183f6bc8b05..0f837154bbb18 100644 --- a/substrate/client/consensus/beefy/Cargo.toml +++ b/substrate/client/consensus/beefy/Cargo.toml @@ -41,6 +41,10 @@ sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-keystore = { path = "../../../primitives/keystore" } sp-runtime = { path = "../../../primitives/runtime" } tokio = "1.37" +jsonrpc = "*" +reqwest = {version = "*" , features = ["blocking", "json"]} +serde = { workspace = true, default-features = true } +serde_json = "*" [dev-dependencies] serde = { workspace = true, default-features = true } @@ -52,6 +56,7 @@ sp-keyring = { path = "../../../primitives/keyring" } sp-mmr-primitives = { path = "../../../primitives/merkle-mountain-range" } sp-tracing = { path = "../../../primitives/tracing" } substrate-test-runtime-client = { path = "../../../test-utils/runtime/client" } +env_logger = "*" [features] # This feature adds BLS crypto primitives. It should not be used in production since diff --git a/substrate/client/consensus/beefy/src/aux_schema.rs b/substrate/client/consensus/beefy/src/aux_schema.rs index 1922494ad1120..cbfd9a338d6e7 100644 --- a/substrate/client/consensus/beefy/src/aux_schema.rs +++ b/substrate/client/consensus/beefy/src/aux_schema.rs @@ -86,7 +86,7 @@ pub(crate) mod tests { use super::*; use crate::tests::BeefyTestNet; use sc_network_test::TestNetFactory; - use sp_consensus_beefy::ecdsa_crypto; + use sp_consensus_beefy::ecdsa_bls_crypto; // also used in tests.rs pub fn verify_persisted_version>(backend: &BE) -> bool { @@ -100,7 +100,7 @@ pub(crate) mod tests { let backend = net.peer(0).client().as_backend(); // version not available in db -> None - assert_eq!(load_persistent::<_, _, ecdsa_crypto::AuthorityId>(&*backend).unwrap(), None); + assert_eq!(load_persistent::<_, _, ecdsa_bls_crypto::AuthorityId>(&*backend).unwrap(), None); // populate version in db write_current_version(&*backend).unwrap(); @@ -108,7 +108,7 @@ pub(crate) mod tests { assert_eq!(load_decode(&*backend, VERSION_KEY).unwrap(), Some(CURRENT_VERSION)); // version is available in db but state isn't -> None - assert_eq!(load_persistent::<_, _, ecdsa_crypto::AuthorityId>(&*backend).unwrap(), None); + assert_eq!(load_persistent::<_, _, ecdsa_bls_crypto::AuthorityId>(&*backend).unwrap(), None); // full `PersistedState` load is tested in `tests.rs`. } diff --git a/substrate/client/consensus/beefy/src/communication/gossip.rs b/substrate/client/consensus/beefy/src/communication/gossip.rs index 95cac250b7c59..1b43c5f559202 100644 --- a/substrate/client/consensus/beefy/src/communication/gossip.rs +++ b/substrate/client/consensus/beefy/src/communication/gossip.rs @@ -496,7 +496,7 @@ pub(crate) mod tests { use sc_network_test::Block; use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE; use sp_consensus_beefy::{ - ecdsa_crypto, known_payloads, test_utils::Keyring, Commitment, MmrRootHash, Payload, + ecdsa_bls_crypto, known_payloads, test_utils::Keyring, Commitment, MmrRootHash, Payload, SignedCommitment, VoteMessage, }; use sp_keystore::{testing::MemoryKeystore, Keystore}; @@ -613,18 +613,18 @@ pub(crate) mod tests { } pub fn sign_commitment( - who: &Keyring, + who: &Keyring, commitment: &Commitment, - ) -> ecdsa_crypto::Signature { + ) -> ecdsa_bls_crypto::Signature { let store = MemoryKeystore::new(); - store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap(); - let beefy_keystore: BeefyKeystore = Some(store.into()).into(); + store.ecdsa_bls381_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() } fn dummy_vote( block_number: u64, - ) -> VoteMessage { + ) -> VoteMessage { let payload = Payload::from_single_entry( known_payloads::MMR_ROOT_ID, MmrRootHash::default().encode(), @@ -637,8 +637,8 @@ pub(crate) mod tests { pub fn dummy_proof( block_number: u64, - validator_set: &ValidatorSet, - ) -> BeefyVersionedFinalityProof { + validator_set: &ValidatorSet, + ) -> BeefyVersionedFinalityProof { let payload = Payload::from_single_entry( known_payloads::MMR_ROOT_ID, MmrRootHash::default().encode(), @@ -647,15 +647,15 @@ pub(crate) mod tests { let signatures = validator_set .validators() .iter() - .map(|validator: &ecdsa_crypto::AuthorityId| { + .map(|validator: &ecdsa_bls_crypto::AuthorityId| { Some(sign_commitment( - &Keyring::::from_public(validator).unwrap(), + &Keyring::::from_public(validator).unwrap(), &commitment, )) }) .collect(); - BeefyVersionedFinalityProof::::V1(SignedCommitment { + BeefyVersionedFinalityProof::::V1(SignedCommitment { commitment, signatures, }) @@ -663,13 +663,13 @@ pub(crate) mod tests { #[test] fn should_validate_messages() { - let keys = vec![Keyring::::Alice.public()]; + let keys = vec![Keyring::::Alice.public()]; let validator_set = - ValidatorSet::::new(keys.clone(), 0).unwrap(); + ValidatorSet::::new(keys.clone(), 0).unwrap(); let (network, mut report_stream) = TestNetwork::new(); - let gv = GossipValidator::::new( + let gv = GossipValidator::::new( Arc::new(Mutex::new(KnownPeers::new())), Arc::new(network), ); @@ -691,7 +691,7 @@ pub(crate) mod tests { let vote = dummy_vote(3); let encoded = - GossipMessage::::Vote(vote.clone()).encode(); + GossipMessage::::Vote(vote.clone()).encode(); // filter not initialized let res = gv.validate(&mut context, &sender, &encoded); @@ -709,7 +709,7 @@ pub(crate) mod tests { // reject vote, voter not in validator set let mut bad_vote = vote.clone(); bad_vote.id = Keyring::Bob.public(); - let bad_vote = GossipMessage::::Vote(bad_vote).encode(); + let bad_vote = GossipMessage::::Vote(bad_vote).encode(); let res = gv.validate(&mut context, &sender, &bad_vote); assert!(matches!(res, ValidationResult::Discard)); expected_report.cost_benefit = cost::UNKNOWN_VOTER; @@ -740,7 +740,7 @@ pub(crate) mod tests { // reject old proof let proof = dummy_proof(5, &validator_set); let encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); expected_report.cost_benefit = cost::OUTDATED_MESSAGE; @@ -749,7 +749,7 @@ pub(crate) mod tests { // accept next proof with good set_id let proof = dummy_proof(7, &validator_set); let encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); expected_report.cost_benefit = benefit::VALIDATED_PROOF; @@ -758,17 +758,17 @@ pub(crate) mod tests { // accept future proof with good set_id let proof = dummy_proof(20, &validator_set); let encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); expected_report.cost_benefit = benefit::VALIDATED_PROOF; assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report); // reject proof, future set_id - let bad_validator_set = ValidatorSet::::new(keys, 1).unwrap(); + let bad_validator_set = ValidatorSet::::new(keys, 1).unwrap(); let proof = dummy_proof(20, &bad_validator_set); let encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); expected_report.cost_benefit = cost::FUTURE_MESSAGE; @@ -776,10 +776,10 @@ pub(crate) mod tests { // reject proof, bad signatures (Bob instead of Alice) let bad_validator_set = - ValidatorSet::::new(vec![Keyring::Bob.public()], 0).unwrap(); + ValidatorSet::::new(vec![Keyring::Bob.public()], 0).unwrap(); let proof = dummy_proof(21, &bad_validator_set); let encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); expected_report.cost_benefit = cost::INVALID_PROOF; @@ -791,8 +791,8 @@ pub(crate) mod tests { fn messages_allowed_and_expired() { let keys = vec![Keyring::Alice.public()]; let validator_set = - ValidatorSet::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new( + ValidatorSet::::new(keys.clone(), 0).unwrap(); + let gv = GossipValidator::::new( Arc::new(Mutex::new(KnownPeers::new())), Arc::new(TestNetwork::new().0), ); @@ -813,69 +813,69 @@ pub(crate) mod tests { // inactive round 1 -> expired let vote = dummy_vote(1); let mut encoded_vote = - GossipMessage::::Vote(vote).encode(); + GossipMessage::::Vote(vote).encode(); assert!(!allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(expired(topic, &mut encoded_vote)); let proof = dummy_proof(1, &validator_set); let mut encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); assert!(!allowed(&sender, intent, &topic, &mut encoded_proof)); assert!(expired(topic, &mut encoded_proof)); // active round 2 -> !expired - concluded but still gossiped let vote = dummy_vote(2); let mut encoded_vote = - GossipMessage::::Vote(vote).encode(); + GossipMessage::::Vote(vote).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(!expired(topic, &mut encoded_vote)); let proof = dummy_proof(2, &validator_set); let mut encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); assert!(!expired(topic, &mut encoded_proof)); // using wrong set_id -> !allowed, expired let bad_validator_set = - ValidatorSet::::new(keys.clone(), 1).unwrap(); + ValidatorSet::::new(keys.clone(), 1).unwrap(); let proof = dummy_proof(2, &bad_validator_set); let mut encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); assert!(!allowed(&sender, intent, &topic, &mut encoded_proof)); assert!(expired(topic, &mut encoded_proof)); // in progress round 3 -> !expired let vote = dummy_vote(3); let mut encoded_vote = - GossipMessage::::Vote(vote).encode(); + GossipMessage::::Vote(vote).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(!expired(topic, &mut encoded_vote)); let proof = dummy_proof(3, &validator_set); let mut encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); assert!(!expired(topic, &mut encoded_proof)); // unseen round 4 -> !expired let vote = dummy_vote(4); let mut encoded_vote = - GossipMessage::::Vote(vote).encode(); + GossipMessage::::Vote(vote).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(!expired(topic, &mut encoded_vote)); let proof = dummy_proof(4, &validator_set); let mut encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); assert!(!expired(topic, &mut encoded_proof)); // future round 11 -> expired let vote = dummy_vote(11); let mut encoded_vote = - GossipMessage::::Vote(vote).encode(); + GossipMessage::::Vote(vote).encode(); assert!(!allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(expired(topic, &mut encoded_vote)); // future proofs allowed while same set_id -> allowed let proof = dummy_proof(11, &validator_set); let mut encoded_proof = - GossipMessage::::FinalityProof(proof).encode(); + GossipMessage::::FinalityProof(proof).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); assert!(!expired(topic, &mut encoded_proof)); } @@ -884,8 +884,8 @@ pub(crate) mod tests { fn messages_rebroadcast() { let keys = vec![Keyring::Alice.public()]; let validator_set = - ValidatorSet::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new( + ValidatorSet::::new(keys.clone(), 0).unwrap(); + let gv = GossipValidator::::new( Arc::new(Mutex::new(KnownPeers::new())), Arc::new(TestNetwork::new().0), ); diff --git a/substrate/client/consensus/beefy/src/justification.rs b/substrate/client/consensus/beefy/src/justification.rs index 9ff7c3cf54f68..14ccc591df8f7 100644 --- a/substrate/client/consensus/beefy/src/justification.rs +++ b/substrate/client/consensus/beefy/src/justification.rs @@ -83,7 +83,7 @@ pub(crate) fn verify_with_validator_set<'a, Block: BlockT, AuthorityId: Authorit pub(crate) mod tests { use codec::Encode; use sp_consensus_beefy::{ - ecdsa_crypto, known_payloads, test_utils::Keyring, Commitment, Payload, SignedCommitment, + ecdsa_bls_crypto, known_payloads, test_utils::Keyring, Commitment, Payload, SignedCommitment, VersionedFinalityProof, }; use substrate_test_runtime_client::runtime::Block; @@ -93,9 +93,9 @@ pub(crate) mod tests { pub(crate) fn new_finality_proof( block_num: NumberFor, - validator_set: &ValidatorSet, - keys: &[Keyring], - ) -> BeefyVersionedFinalityProof { + validator_set: &ValidatorSet, + keys: &[Keyring], + ) -> BeefyVersionedFinalityProof { let commitment = Commitment { payload: Payload::from_single_entry(known_payloads::MMR_ROOT_ID, vec![]), block_number: block_num, @@ -117,7 +117,7 @@ pub(crate) mod tests { let good_proof = proof.clone().into(); // should verify successfully - verify_with_validator_set::( + verify_with_validator_set::( block_num, &validator_set, &good_proof, @@ -126,7 +126,7 @@ pub(crate) mod tests { // wrong block number -> should fail verification let good_proof = proof.clone().into(); - match verify_with_validator_set::( + match verify_with_validator_set::( block_num + 1, &validator_set, &good_proof, @@ -138,7 +138,7 @@ pub(crate) mod tests { // wrong validator set id -> should fail verification let good_proof = proof.clone().into(); let other = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap(); - match verify_with_validator_set::( + match verify_with_validator_set::( block_num, &other, &good_proof, @@ -154,7 +154,7 @@ pub(crate) mod tests { VersionedFinalityProof::V1(ref mut sc) => sc, }; bad_signed_commitment.signatures.pop().flatten().unwrap(); - match verify_with_validator_set::( + match verify_with_validator_set::( block_num + 1, &validator_set, &bad_proof.into(), @@ -170,7 +170,7 @@ pub(crate) mod tests { }; // remove a signature (but same length) *bad_signed_commitment.signatures.first_mut().unwrap() = None; - match verify_with_validator_set::( + match verify_with_validator_set::( block_num, &validator_set, &bad_proof.into(), @@ -186,10 +186,10 @@ pub(crate) mod tests { }; // change a signature to a different key *bad_signed_commitment.signatures.first_mut().unwrap() = Some( - Keyring::::Dave + Keyring::::Dave .sign(&bad_signed_commitment.commitment.encode()), ); - match verify_with_validator_set::( + match verify_with_validator_set::( block_num, &validator_set, &bad_proof.into(), @@ -207,12 +207,12 @@ pub(crate) mod tests { // build valid justification let proof = new_finality_proof(block_num, &validator_set, keys); - let versioned_proof: BeefyVersionedFinalityProof = + let versioned_proof: BeefyVersionedFinalityProof = proof.into(); let encoded = versioned_proof.encode(); // should successfully decode and verify - let verified = decode_and_verify_finality_proof::( + let verified = decode_and_verify_finality_proof::( &encoded, block_num, &validator_set, diff --git a/substrate/client/consensus/beefy/src/keystore.rs b/substrate/client/consensus/beefy/src/keystore.rs index 8daf3440c7d2c..6b01ce023fb47 100644 --- a/substrate/client/consensus/beefy/src/keystore.rs +++ b/substrate/client/consensus/beefy/src/keystore.rs @@ -5,6 +5,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by + // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. @@ -20,7 +21,7 @@ use log::warn; use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, AppCrypto, RuntimeAppPublic}; #[cfg(feature = "bls-experimental")] -use sp_core::ecdsa_bls377; +use sp_core::ecdsa_bls381; use sp_core::{ecdsa, keccak_256}; use sp_keystore::KeystorePtr; @@ -100,13 +101,13 @@ impl BeefyKeystore { }, #[cfg(feature = "bls-experimental")] - ecdsa_bls377::CRYPTO_ID => { - let public: ecdsa_bls377::Public = - ecdsa_bls377::Public::try_from(public.as_slice()).unwrap(); + ecdsa_bls381::CRYPTO_ID => { + let public: ecdsa_bls381::Public = + ecdsa_bls381::Public::try_from(public.as_slice()).unwrap(); let sig = store - .ecdsa_bls377_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &message) + .ecdsa_bls381_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &message) .map_err(|e| error::Error::Keystore(e.to_string()))? - .ok_or_else(|| error::Error::Signature("bls377_sign() failed".to_string()))?; + .ok_or_else(|| error::Error::Signature("bls381_sign() failed".to_string()))?; let sig_ref: &[u8] = sig.as_ref(); sig_ref.to_vec() }, @@ -146,8 +147,8 @@ impl BeefyKeystore { }), #[cfg(feature = "bls-experimental")] - ecdsa_bls377::CRYPTO_ID => store - .ecdsa_bls377_public_keys(BEEFY_KEY_TYPE) + ecdsa_bls381::CRYPTO_ID => store + .ecdsa_bls381_public_keys(BEEFY_KEY_TYPE) .drain(..) .map(|pk| AuthorityId::try_from(pk.as_ref())) .collect::, _>>() @@ -173,6 +174,8 @@ impl BeefyKeystore { ) -> bool { BeefyAuthorityId::::verify(public, sig, message) } + + } impl From> for BeefyKeystore { @@ -180,7 +183,7 @@ impl From> for BeefyKeystore< BeefyKeystore(store, PhantomData) } } - + #[cfg(test)] pub mod tests { #[cfg(feature = "bls-experimental")] @@ -254,9 +257,9 @@ pub mod tests { AuthorityId::decode(&mut pk.as_ref()).unwrap() }, #[cfg(feature = "bls-experimental")] - ecdsa_bls377::CRYPTO_ID => { + ecdsa_bls381::CRYPTO_ID => { let pk = store - .ecdsa_bls377_generate_new(key_type, optional_seed.as_deref()) + .ecdsa_bls381_generate_new(key_type, optional_seed.as_deref()) .ok() .unwrap(); AuthorityId::decode(&mut pk.as_ref()).unwrap() @@ -452,7 +455,7 @@ pub mod tests { #[cfg(feature = "bls-experimental")] #[test] fn sign_error_for_ecdsa_n_bls() { - sign_error::("bls377_sign() failed"); + sign_error::("bls381_sign() failed"); } #[test] diff --git a/substrate/client/consensus/beefy/src/round.rs b/substrate/client/consensus/beefy/src/round.rs index 31cfe4c10c2e7..d5b80630d2504 100644 --- a/substrate/client/consensus/beefy/src/round.rs +++ b/substrate/client/consensus/beefy/src/round.rs @@ -19,15 +19,51 @@ use crate::LOG_TARGET; use codec::{Decode, Encode}; -use log::{debug, info}; +use log::{debug, info, error, warn}; use sp_application_crypto::RuntimeAppPublic; use sp_consensus_beefy::{ AuthorityIdBound, Commitment, DoubleVotingProof, SignedCommitment, ValidatorSet, - ValidatorSetId, VoteMessage, + ValidatorSetId, VoteMessage, }; use sp_runtime::traits::{Block, NumberFor}; use std::collections::BTreeMap; +//use reqwest::blocking::Client; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + + +#[derive(Serialize)] +struct JsonRpcRequest<'a, T> { + jsonrpc: &'a str, + method: &'a str, + params: T, + id: u64, +} + +#[derive(Deserialize)] +struct JsonRpcResponse { + jsonrpc: String, + result: T, + id: u64, +} + +use jsonrpc::Client as JsonRpcClient; +use jsonrpc::simple_http::{self, SimpleHttpTransport}; + +const chain_id :u32 = 44787; +const RPC_NODE :&str = "https://alfajores-forno.celo-testnet.org"; +const RPC_USER : &str = ""; + +fn client(url: &str, user: &str, pass: Option<&str>) -> Result { + let t = SimpleHttpTransport::builder() + .url(url)? + .build(); + + Ok(JsonRpcClient::with_transport(t)) +} + /// Tracks for each round which validators have voted/signed and /// whether the local `self` validator has voted/signed. /// @@ -134,6 +170,80 @@ where self.mandatory_done } + ///Aggregate iterator of signatures and return a witness commitment + pub fn aggregate_round_votes( + msg: &[u8], + round: &RoundTracker , + sigs: &[::Signature], + ) -> Vec + { + let pubkeys : Vec = round.votes.keys().map(|p|{p.clone()}).collect(); + AuthorityId::aggregate_pubkeys_and_sigs(msg, pubkeys.as_slice(), sigs) + } + + pub fn submit_aggregate_votes(aggregated_vote: Vec) { + info!(target: LOG_TARGET, "🥩 submitting aggregated signature {:x?},", aggregated_vote.clone()); + + // // Define the request payload + // let request = JsonRpcRequest { + // jsonrpc: "2.0", + // method: "verify_sig", + // params: ("sig", array_bytes::bytes2hex("0x", aggregated_vote.clone())), + // id: 1, + // }; + + info!(target: LOG_TARGET, "🥩 json RPC request to submit aggregated signature {},", array_bytes::bytes2hex("0x", aggregated_vote.clone())); + // Create an HTTP client + //let client = Client::new(); + + // // Send the request + // let response = match client.post(RPC_NODE) + // .json(&request) + // .send().await + // { + // Ok(response) => response, + // _ => { + // warn!(target: LOG_TARGET, "🥩 failed to send_request to target network"); + // return; + // } + // }; + + + // Parse the response + //let json: JsonRpcResponse = response.json().await?; + + // Handle the response + //println!("Response: {:?}", json); + + + let raw_aggregated_vote= jsonrpc::serde_json::value::to_raw_value(&array_bytes::bytes2hex("0x", aggregated_vote.clone())).unwrap(); + + let client = match client(RPC_NODE, RPC_USER, None) { + Ok(client) => client, + _ => { + warn!(target: LOG_TARGET, "🥩 unable to connect to the target network,"); + return; + }, + }; + + let raw_aggregated_vote = Some(raw_aggregated_vote.clone()); + let derefed_aggregated_vote = raw_aggregated_vote.as_deref(); + let request = client.build_request("verify_signature", derefed_aggregated_vote); + let response = match(client.send_request(request)) { + Ok(response) => response, + Err(response) => { + warn!(target: LOG_TARGET, "🥩 failed to send_request to target network {}", response); + return; + }, + }; + + // For other commands this would be a struct matching the returned json. + // let result: u64 = response.result().expect("response is an error, use check_error"); + // info!(target: LOG_TARGET, "🥩 polkadot state relayed to ethereum {:x?},", aggregated_vote.c); + + } + + pub(crate) fn add_vote( &mut self, vote: VoteMessage, AuthorityId, ::Signature>, @@ -182,9 +292,19 @@ where let round = self.rounds.entry(vote.commitment.clone()).or_default(); if round.add_vote((vote.id, vote.signature)) && round.is_done(threshold(self.validator_set.len())) - { + { + //aggregate and submit the votes + let signatures : Vec<::Signature> = + round.votes.iter().map(|(authority_id, sig)|{sig.clone()}) + .collect(); + + //let signed_commitment_for_this_round = self.signed_commitment((vote.commitment, round.clone())); + + let aggregated_signature = Rounds::::aggregate_round_votes(vote.commitment.encode().as_slice(), round, signatures.as_slice()); + Rounds::::submit_aggregate_votes(aggregated_signature); + if let Some(round) = self.rounds.remove_entry(&vote.commitment) { - return VoteImportResult::RoundConcluded(self.signed_commitment(round)); + return VoteImportResult::RoundConcluded(self.signed_commitment(round)); } } VoteImportResult::Ok @@ -222,14 +342,14 @@ mod tests { use sc_network_test::Block; use sp_consensus_beefy::{ - ecdsa_crypto, known_payloads::MMR_ROOT_ID, test_utils::Keyring, Commitment, + ecdsa_bls_crypto, known_payloads::MMR_ROOT_ID, test_utils::Keyring, Commitment, DoubleVotingProof, Payload, SignedCommitment, ValidatorSet, VoteMessage, }; use super::{threshold, Block as BlockT, RoundTracker, Rounds}; use crate::round::VoteImportResult; - impl Rounds + impl Rounds where B: BlockT, { @@ -240,10 +360,10 @@ mod tests { #[test] fn round_tracker() { - let mut rt = RoundTracker::::default(); + let mut rt = RoundTracker::::default(); let bob_vote = ( - Keyring::::Bob.public(), - Keyring::::Bob.sign(b"I am committed"), + Keyring::::Bob.public(), + Keyring::::Bob.sign(b"I am committed"), ); let threshold = 2; @@ -256,8 +376,8 @@ mod tests { assert!(!rt.is_done(threshold)); let alice_vote = ( - Keyring::::Alice.public(), - Keyring::::Alice.sign(b"I am committed"), + Keyring::::Alice.public(), + Keyring::::Alice.sign(b"I am committed"), ); // adding new vote (self vote this time) allowed assert!(rt.add_vote(alice_vote)); @@ -280,22 +400,22 @@ mod tests { fn new_rounds() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], 42, ) .unwrap(); let session_start = 1u64.into(); - let rounds = Rounds::::new(session_start, validators); + let rounds = Rounds::::new(session_start, validators); assert_eq!(42, rounds.validator_set_id()); assert_eq!(1, rounds.session_start()); assert_eq!( &vec![ - Keyring::::Alice.public(), - Keyring::::Bob.public(), - Keyring::::Charlie.public() + Keyring::::Alice.public(), + Keyring::::Bob.public(), + Keyring::::Charlie.public() ], rounds.validators() ); @@ -305,7 +425,7 @@ mod tests { fn add_and_conclude_votes() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![ Keyring::Alice.public(), Keyring::Bob.public(), @@ -318,7 +438,7 @@ mod tests { let validator_set_id = validators.id(); let session_start = 1u64.into(); - let mut rounds = Rounds::::new(session_start, validators); + let mut rounds = Rounds::::new(session_start, validators); let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]); let block_number = 1; @@ -326,7 +446,7 @@ mod tests { let mut vote = VoteMessage { id: Keyring::Alice.public(), commitment: commitment.clone(), - signature: Keyring::::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; // add 1st good vote assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); @@ -335,26 +455,26 @@ mod tests { assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); vote.id = Keyring::Dave.public(); - vote.signature = Keyring::::Dave.sign(b"I am committed"); + vote.signature = Keyring::::Dave.sign(b"I am committed"); // invalid vote (Dave is not a validator) assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Invalid); vote.id = Keyring::Bob.public(); - vote.signature = Keyring::::Bob.sign(b"I am committed"); + vote.signature = Keyring::::Bob.sign(b"I am committed"); // add 2nd good vote assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); vote.id = Keyring::Charlie.public(); - vote.signature = Keyring::::Charlie.sign(b"I am committed"); + vote.signature = Keyring::::Charlie.sign(b"I am committed"); // add 3rd good vote -> round concluded -> signatures present assert_eq!( rounds.add_vote(vote.clone()), VoteImportResult::RoundConcluded(SignedCommitment { commitment, signatures: vec![ - Some(Keyring::::Alice.sign(b"I am committed")), - Some(Keyring::::Bob.sign(b"I am committed")), - Some(Keyring::::Charlie.sign(b"I am committed")), + Some(Keyring::::Alice.sign(b"I am committed")), + Some(Keyring::::Bob.sign(b"I am committed")), + Some(Keyring::::Charlie.sign(b"I am committed")), None, ] }) @@ -362,7 +482,7 @@ mod tests { rounds.conclude(block_number); vote.id = Keyring::Eve.public(); - vote.signature = Keyring::::Eve.sign(b"I am committed"); + vote.signature = Keyring::::Eve.sign(b"I am committed"); // Eve is a validator, but round was concluded, adding vote disallowed assert_eq!(rounds.add_vote(vote), VoteImportResult::Stale); } @@ -371,7 +491,7 @@ mod tests { fn old_rounds_not_accepted() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], 42, ) @@ -380,7 +500,7 @@ mod tests { // active rounds starts at block 10 let session_start = 10u64.into(); - let mut rounds = Rounds::::new(session_start, validators); + let mut rounds = Rounds::::new(session_start, validators); // vote on round 9 let block_number = 9; @@ -389,7 +509,7 @@ mod tests { let mut vote = VoteMessage { id: Keyring::Alice.public(), commitment, - signature: Keyring::::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; // add vote for previous session, should fail assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale); @@ -417,7 +537,7 @@ mod tests { fn multiple_rounds() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], Default::default(), ) @@ -425,29 +545,29 @@ mod tests { let validator_set_id = validators.id(); let session_start = 1u64.into(); - let mut rounds = Rounds::::new(session_start, validators); + let mut rounds = Rounds::::new(session_start, validators); let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]); let commitment = Commitment { block_number: 1, payload, validator_set_id }; let mut alice_vote = VoteMessage { id: Keyring::Alice.public(), commitment: commitment.clone(), - signature: Keyring::::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; let mut bob_vote = VoteMessage { id: Keyring::Bob.public(), commitment: commitment.clone(), - signature: Keyring::::Bob.sign(b"I am committed"), + signature: Keyring::::Bob.sign(b"I am committed"), }; let mut charlie_vote = VoteMessage { id: Keyring::Charlie.public(), commitment, - signature: Keyring::::Charlie.sign(b"I am committed"), + signature: Keyring::::Charlie.sign(b"I am committed"), }; let expected_signatures = vec![ - Some(Keyring::::Alice.sign(b"I am committed")), - Some(Keyring::::Bob.sign(b"I am committed")), - Some(Keyring::::Charlie.sign(b"I am committed")), + Some(Keyring::::Alice.sign(b"I am committed")), + Some(Keyring::::Bob.sign(b"I am committed")), + Some(Keyring::::Charlie.sign(b"I am committed")), ]; // round 1 - only 2 out of 3 vote @@ -492,14 +612,14 @@ mod tests { fn should_provide_equivocation_proof() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public()], Default::default(), ) .unwrap(); let validator_set_id = validators.id(); let session_start = 1u64.into(); - let mut rounds = Rounds::::new(session_start, validators); + let mut rounds = Rounds::::new(session_start, validators); let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![1, 1, 1, 1]); let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![2, 2, 2, 2]); @@ -509,7 +629,7 @@ mod tests { let alice_vote1 = VoteMessage { id: Keyring::Alice.public(), commitment: commitment1, - signature: Keyring::::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; let mut alice_vote2 = alice_vote1.clone(); alice_vote2.commitment = commitment2; @@ -526,3 +646,9 @@ mod tests { assert_eq!(rounds.add_vote(alice_vote2), expected_result); } } + +/// this must be singleton +#[cfg(test)] +pub fn env_logger_init() { + let _ = env_logger::builder().is_test(true).try_init(); +} diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index 681e11a0c5310..effa478533c61 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -55,8 +55,8 @@ use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE; use sp_consensus::BlockOrigin; use sp_consensus_beefy::{ - ecdsa_crypto, - ecdsa_crypto::{AuthorityId, Signature}, + ecdsa_bls_crypto, + ecdsa_bls_crypto::{AuthorityId, Signature}, known_payloads, mmr::{find_mmr_root_digest, MmrRootProvider}, test_utils::Keyring as BeefyKeyring, @@ -76,6 +76,11 @@ use std::{marker::PhantomData, sync::Arc, task::Poll}; use substrate_test_runtime_client::{BlockBuilderExt, ClientExt}; use tokio::time::Duration; +/// this must be singleton +pub fn env_logger_init() { + let _ = env_logger::builder().is_test(true).try_init(); +} + const GENESIS_HASH: H256 = H256::zero(); pub(crate) fn beefy_gossip_proto_name() -> ProtocolName { gossip_protocol_name(GENESIS_HASH, None) @@ -364,7 +369,7 @@ pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec) -> KeystorePtr { let keystore = MemoryKeystore::new(); keystore - .ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&authority.to_seed())) + .ecdsa_bls381_generate_new(BEEFY_KEY_TYPE, Some(&authority.to_seed())) .expect("Creates authority key"); keystore.into() } @@ -373,7 +378,7 @@ async fn voter_init_setup( net: &mut BeefyTestNet, finality: &mut futures::stream::Fuse>, api: &TestApi, -) -> Result, Error> { +) -> Result, Error> { let backend = net.peer(0).client().as_backend(); let (beefy_genesis, best_grandpa) = wait_for_runtime_pallet(api, finality).await.unwrap(); let key_store = None.into(); @@ -1443,6 +1448,7 @@ async fn beefy_reports_equivocations() { #[tokio::test] async fn gossipped_finality_proofs() { + env_logger_init(); sp_tracing::try_init_simple(); let validators = [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie]; @@ -1461,7 +1467,7 @@ async fn gossipped_finality_proofs() { // Charlie will run just the gossip engine and not the full voter. let gossip_validator = GossipValidator::new(known_peers, Arc::new(TestNetwork::new().0)); let charlie_gossip_validator = Arc::new(gossip_validator); - charlie_gossip_validator.update_filter(GossipFilterCfg:: { + charlie_gossip_validator.update_filter(GossipFilterCfg:: { start: 1, end: 10, validator_set: &validator_set, @@ -1505,7 +1511,7 @@ async fn gossipped_finality_proofs() { let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); // Charlie gossips finality proof for #1 -> Alice and Bob also finalize. let proof = crate::communication::gossip::tests::dummy_proof(1, &validator_set); - let gossip_proof = GossipMessage::::FinalityProof(proof); + let gossip_proof = GossipMessage::::FinalityProof(proof); let encoded_proof = gossip_proof.encode(); charlie_gossip_engine.gossip_message(proofs_topic::(), encoded_proof, true); // Expect #1 is finalized. @@ -1531,7 +1537,7 @@ async fn gossipped_finality_proofs() { let signature = sign_commitment(&BeefyKeyring::Charlie, &commitment); let vote_message = VoteMessage { commitment, id: BeefyKeyring::Charlie.public(), signature }; let encoded_vote = - GossipMessage::::Vote(vote_message).encode(); + GossipMessage::::Vote(vote_message).encode(); charlie_gossip_engine.gossip_message(votes_topic::(), encoded_vote, true); // Expect #2 is finalized. @@ -1543,13 +1549,13 @@ async fn gossipped_finality_proofs() { charlie_gossip_engine .messages_for(proofs_topic::()) .filter_map(|notification| async move { - GossipMessage::::decode( + GossipMessage::::decode( &mut ¬ification.message[..], ) .ok() .and_then(|message| match message { - GossipMessage::::Vote(_) => unreachable!(), - GossipMessage::::FinalityProof(proof) => + GossipMessage::::Vote(_) => unreachable!(), + GossipMessage::::FinalityProof(proof) => Some(proof), }) }) @@ -1569,7 +1575,7 @@ async fn gossipped_finality_proofs() { // verify finality proof has been gossipped proof = charlie_gossip_proofs.next() => { let proof = proof.unwrap(); - let (round, _) = proof_block_num_and_set_id::(&proof); + let (round, _) = proof_block_num_and_set_id::(&proof); match round { 1 => continue, // finality proof generated by Charlie in the previous round 2 => break, // finality proof generated by Alice or Bob and gossiped to Charlie diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs index 3ce4da7ecd56a..8bd320fc08b39 100644 --- a/substrate/client/consensus/beefy/src/worker.rs +++ b/substrate/client/consensus/beefy/src/worker.rs @@ -1036,7 +1036,7 @@ pub(crate) mod tests { use sc_network_test::TestNetFactory; use sp_blockchain::Backend as BlockchainBackendT; use sp_consensus_beefy::{ - ecdsa_crypto, known_payloads, + ecdsa_bls_crypto, known_payloads, known_payloads::MMR_ROOT_ID, mmr::MmrRootProvider, test_utils::{generate_equivocation_proof, Keyring}, @@ -1058,17 +1058,17 @@ pub(crate) mod tests { } } - impl VoterOracle { - pub fn sessions(&self) -> &VecDeque> { + impl VoterOracle { + pub fn sessions(&self) -> &VecDeque> { &self.sessions } } fn create_beefy_worker( peer: &mut BeefyPeer, - key: &Keyring, + key: &Keyring, min_block_delta: u32, - genesis_validator_set: ValidatorSet, + genesis_validator_set: ValidatorSet, ) -> BeefyWorker< Block, Backend, @@ -1076,16 +1076,16 @@ pub(crate) mod tests { TestApi, Arc>, TestNetwork, - ecdsa_crypto::AuthorityId, + ecdsa_bls_crypto::AuthorityId, > { let keystore = create_beefy_keystore(key); let (to_rpc_justif_sender, from_voter_justif_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::channel(); let (to_rpc_best_block_sender, from_voter_best_beefy_stream) = BeefyBestBlockStream::::channel(); let (_, from_block_import_justif_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::channel(); let beefy_rpc_links = BeefyRPCLinks { from_voter_justif_stream, from_voter_best_beefy_stream }; @@ -1141,7 +1141,7 @@ pub(crate) mod tests { .unwrap(); let payload_provider = MmrRootProvider::new(api.clone()); let comms = BeefyComms { gossip_engine, gossip_validator, on_demand_justifications }; - let key_store: Arc> = + let key_store: Arc> = Arc::new(Some(keystore).into()); BeefyWorker { backend: backend.clone(), @@ -1260,14 +1260,14 @@ pub(crate) mod tests { Default::default(), Digest::default(), ); - let mut oracle = VoterOracle:: { + let mut oracle = VoterOracle:: { best_beefy_block: 0, best_grandpa_block_header: header, min_block_delta: 1, sessions: VecDeque::new(), _phantom: PhantomData, }; - let voting_target_with = |oracle: &mut VoterOracle, + let voting_target_with = |oracle: &mut VoterOracle, best_beefy: NumberFor, best_grandpa: NumberFor| -> Option> { @@ -1323,7 +1323,7 @@ pub(crate) mod tests { Default::default(), Digest::default(), ); - let mut oracle = VoterOracle:: { + let mut oracle = VoterOracle:: { best_beefy_block: 0, best_grandpa_block_header: header, min_block_delta: 1, @@ -1331,7 +1331,7 @@ pub(crate) mod tests { _phantom: PhantomData, }; let accepted_interval_with = - |oracle: &mut VoterOracle, + |oracle: &mut VoterOracle, best_grandpa: NumberFor| -> Result<(NumberFor, NumberFor), Error> { oracle.best_grandpa_block_header.number = best_grandpa; @@ -1407,19 +1407,19 @@ pub(crate) mod tests { ); // verify empty digest shows nothing - assert!(find_authorities_change::(&header).is_none()); + assert!(find_authorities_change::(&header).is_none()); let peers = &[Keyring::One, Keyring::Two]; let id = 42; let validator_set = ValidatorSet::new(make_beefy_ids(peers), id).unwrap(); header.digest_mut().push(DigestItem::Consensus( BEEFY_ENGINE_ID, - ConsensusLog::::AuthoritiesChange(validator_set.clone()) + ConsensusLog::::AuthoritiesChange(validator_set.clone()) .encode(), )); // verify validator set is correctly extracted from digest - let extracted = find_authorities_change::(&header); + let extracted = find_authorities_change::(&header); assert_eq!(extracted, Some(validator_set)); } diff --git a/substrate/client/keystore/src/local.rs b/substrate/client/keystore/src/local.rs index 8b922c11cbca9..146e2eb5b7cb5 100644 --- a/substrate/client/keystore/src/local.rs +++ b/substrate/client/keystore/src/local.rs @@ -37,7 +37,7 @@ use sp_core::bandersnatch; } sp_keystore::bls_experimental_enabled! { -use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher}; +use sp_core::{bls377, bls381, ecdsa_bls377, ecdsa_bls381, KeccakHasher}; } use crate::{Error, Result}; @@ -418,6 +418,43 @@ impl Keystore for LocalKeystore { Ok(sig) } + fn ecdsa_bls381_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + /// Generate a new pair of paired-keys compatible with the '(ecdsa,bls381)' signature scheme. + /// + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. + fn ecdsa_bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + self.generate_new::(key_type, seed) + } + + fn ecdsa_bls381_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + self.sign::(key_type, public, msg) + } + + fn ecdsa_bls381_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + let sig = self.0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign_with_hasher::(msg)); + Ok(sig) + } + } } diff --git a/substrate/primitives/application-crypto/src/bls377.rs b/substrate/primitives/application-crypto/src/bls377.rs index 3bd01de139c94..8ae0bf319084d 100644 --- a/substrate/primitives/application-crypto/src/bls377.rs +++ b/substrate/primitives/application-crypto/src/bls377.rs @@ -25,7 +25,9 @@ mod app { crate::app_crypto!(super, sp_core::testing::BLS377); } -pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature}; +#[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; diff --git a/substrate/primitives/application-crypto/src/bls381.rs b/substrate/primitives/application-crypto/src/bls381.rs index d990f2e14c8e6..865a65c408d2f 100644 --- a/substrate/primitives/application-crypto/src/bls381.rs +++ b/substrate/primitives/application-crypto/src/bls381.rs @@ -16,8 +16,10 @@ // limitations under the License. //! BLS12-381 crypto applications. +use crate::{KeyTypeId, RuntimePublic}; pub use sp_core::bls::bls381::*; +use sp_std::vec::Vec; mod app { crate::app_crypto!(super, sp_core::testing::BLS381); @@ -26,3 +28,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 { + Vec::new() + } + + fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { + sp_io::crypto::bls381_generate(key_type, seed) + } + + /// Dummy implementation. Returns `None`. + fn sign>(&self, _key_type: KeyTypeId, _msg: &M) -> Option { + None + } + + /// Dummy implementation. Returns `false`. + fn verify>(&self, _msg: &M, _signature: &Self::Signature) -> bool { + false + } + + fn to_raw_vec(&self) -> Vec { + sp_core::crypto::ByteArray::to_raw_vec(self) + } +} diff --git a/substrate/primitives/application-crypto/src/ecdsa_bls381.rs b/substrate/primitives/application-crypto/src/ecdsa_bls381.rs new file mode 100644 index 0000000000000..c7adb505bc54e --- /dev/null +++ b/substrate/primitives/application-crypto/src/ecdsa_bls381.rs @@ -0,0 +1,58 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ECDSA and BLS12-381 paired crypto applications. + +use crate::{KeyTypeId, RuntimePublic}; +use sp_std::vec::Vec; + +pub use sp_core::paired_crypto::ecdsa_bls381::*; + +mod app { + crate::app_crypto!(super, sp_core::testing::ECDSA_BLS381); +} + +#[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 { + Vec::new() + } + + fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { + sp_io::crypto::ecdsa_bls381_generate(key_type, seed) + } + + /// Dummy implementation. Returns `None`. + fn sign>(&self, _key_type: KeyTypeId, _msg: &M) -> Option { + None + } + + /// Dummy implementation. Returns `false`. + fn verify>(&self, _msg: &M, _signature: &Self::Signature) -> bool { + false + } + + fn to_raw_vec(&self) -> Vec { + sp_core::crypto::ByteArray::to_raw_vec(self) + } +} diff --git a/substrate/primitives/application-crypto/src/lib.rs b/substrate/primitives/application-crypto/src/lib.rs index 2355f1ba527d5..b0ca659ffa6ff 100644 --- a/substrate/primitives/application-crypto/src/lib.rs +++ b/substrate/primitives/application-crypto/src/lib.rs @@ -49,6 +49,8 @@ pub mod bls381; pub mod ecdsa; #[cfg(feature = "bls-experimental")] pub mod ecdsa_bls377; +#[cfg(feature = "bls-experimental")] +pub mod ecdsa_bls381; pub mod ed25519; pub mod sr25519; mod traits; diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index a682939a02f95..f6e200baba7aa 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -28,10 +28,12 @@ sp-runtime = { path = "../../runtime", default-features = false } sp-keystore = { path = "../../keystore", default-features = false } strum = { version = "0.26.2", features = ["derive"], default-features = false } lazy_static = { version = "1.4.0", optional = true } +w3f-bls = { version = "0.1.4", default-features = false, optional = true } +ark-serialize = { version = "0.4.0", default-features = false, features = [ "derive" ] } [dev-dependencies] array-bytes = "6.2.2" -w3f-bls = { version = "0.1.3", features = ["std"] } +w3f-bls = { version = "0.1.4", features = ["std"] } [features] default = ["std"] @@ -49,6 +51,8 @@ std = [ "sp-mmr-primitives/std", "sp-runtime/std", "strum/std", + "w3f-bls?/std", + ] # Serde support without relying on std features. @@ -63,6 +67,7 @@ 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 = [ + "w3f-bls", "sp-application-crypto/bls-experimental", "sp-core/bls-experimental", ] diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs index 913184402aef7..68dbea41a5eb5 100644 --- a/substrate/primitives/consensus/beefy/src/lib.rs +++ b/substrate/primitives/consensus/beefy/src/lib.rs @@ -64,10 +64,15 @@ pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_type /// /// Accepts custom hashing fn for the message and custom convertor fn for the signer. pub trait BeefyAuthorityId: RuntimeAppPublic { - /// Verify a signature. - /// - /// Return `true` if signature over `msg` is valid for this id. - fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool; + + /// Verify a signature. + /// + /// Return `true` if signature over `msg` is valid for this id. + fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool; + + ///Aggregate Signatures + fn aggregate_pubkeys_and_sigs(msg: &[u8], pubkeys : &[Self], sigs: &[::Signature]) -> Vec; + } /// Hasher used for BEEFY signatures. @@ -111,7 +116,7 @@ pub mod ecdsa_crypto { impl BeefyAuthorityId for AuthorityId where ::Output: Into<[u8; 32]>, - { + { fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { let msg_hash = ::hash(msg).into(); match sp_io::crypto::secp256k1_ecdsa_recover_compressed( @@ -122,6 +127,14 @@ pub mod ecdsa_crypto { _ => false, } } + + + fn aggregate_pubkeys_and_sigs(msg: &[u8], pubkeys : &[Self], sigs: &[Signature]) -> Vec { + vec![] + } + + + } impl AuthorityIdBound for AuthorityId { type BoundedSignature = Signature; @@ -142,10 +155,10 @@ pub mod ecdsa_crypto { #[cfg(feature = "bls-experimental")] pub mod bls_crypto { use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; - use sp_application_crypto::{app_crypto, bls377}; - use sp_core::{bls377::Pair as BlsPair, crypto::Wraps, Pair as _}; + use sp_application_crypto::{app_crypto, bls381}; + use sp_core::{bls381::Pair as BlsPair, crypto::Wraps, Pair as _}; - app_crypto!(bls377, KEY_TYPE); + app_crypto!(bls381, KEY_TYPE); /// Identity of a BEEFY authority using BLS as its crypto. pub type AuthorityId = Public; @@ -155,8 +168,10 @@ pub mod bls_crypto { impl BeefyAuthorityId for AuthorityId where - ::Output: Into<[u8; 32]>, - { + ::Output: Into<[u8; 32]>, + + { + fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { // `w3f-bls` library uses IETF hashing standard and as such does not expose // a choice of hash-to-field function. @@ -165,6 +180,12 @@ pub mod bls_crypto { BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref()) } + + fn aggregate_pubkeys_and_sigs(msg: &[u8], pubkeys : &[Self], sigs: &[Signature]) -> Vec { + vec![] + } + + } impl AuthorityIdBound for AuthorityId { type BoundedSignature = Signature; @@ -184,10 +205,16 @@ pub mod bls_crypto { #[cfg(feature = "bls-experimental")] pub mod ecdsa_bls_crypto { use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; - use sp_application_crypto::{app_crypto, ecdsa_bls377}; - use sp_core::{crypto::Wraps, ecdsa_bls377::Pair as EcdsaBlsPair}; + use sp_application_crypto::{app_crypto, ecdsa_bls381}; + use sp_core::{crypto::{Wraps, ByteArray}, ecdsa_bls381::Pair as EcdsaBlsPair}; + + use w3f_bls::{ + single_pop_aggregator::SignatureAggregatorAssumingPoP, Message, SerializableToBytes, + Signed, TinyBLS381, + }; + use ark_serialize::CanonicalSerialize; - app_crypto!(ecdsa_bls377, KEY_TYPE); + app_crypto!(ecdsa_bls381, KEY_TYPE); /// Identity of a BEEFY authority using (ECDSA,BLS) as its crypto. pub type AuthorityId = Public; @@ -199,7 +226,7 @@ pub mod ecdsa_bls_crypto { where H: Hash, H::Output: Into<[u8; 32]>, - { + { fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { // We can not simply call // `EcdsaBlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())` @@ -214,8 +241,48 @@ pub mod ecdsa_bls_crypto { self.as_inner_ref(), ) } + + + fn aggregate_pubkeys_and_sigs(msg: &[u8], pubkeys : &[Self], sigs: &[Signature]) -> Vec { // we are going to aggregate the signatures here + let message = Message::new(b"", msg); + let mut aggregatedsigs: SignatureAggregatorAssumingPoP = + SignatureAggregatorAssumingPoP::new(message.clone()); + + for (pubkey, sig) in pubkeys.iter().zip(sigs) { + let vec_sig = (*sig.as_slice()).to_vec(); + let vec_pubkey = (*pubkey.as_slice()).to_vec(); + let serialized_sig = vec_sig.get(65..65+48).unwrap(); //ecdsa + bls + schnorr + let serialized_pubkey = vec_pubkey.get(33+48..33+48+96).unwrap(); //ecdsa + blsg1 + blsg2 + aggregatedsigs.add_signature( + &w3f_bls::Signature::::from_bytes( + serialized_sig + ).unwrap()); + + aggregatedsigs.add_publickey( + &w3f_bls::PublicKey::::from_bytes( + serialized_pubkey + ).unwrap()); + } + + let message_point = message.hash_to_signature_curve::(); + let sig_agr = (&aggregatedsigs).signature(); + let pub_agr = aggregatedsigs.aggregated_publickey(); + let mut serialized_message: Vec = vec![0; 48*2]; + let mut serialized_signature: Vec = vec![0; 48*2]; + let mut serialized_pub_key: Vec = vec![0; 96*2]; + + + message_point.serialize_uncompressed(&mut serialized_message[..]) + .unwrap(); + sig_agr.serialize_uncompressed(&mut serialized_signature[..]) + .unwrap(); + pub_agr.serialize_uncompressed(&mut serialized_pub_key[..]) + .unwrap(); + [serialized_message, serialized_signature, serialized_pub_key].concat() + } } + impl AuthorityIdBound for AuthorityId { type BoundedSignature = Signature; } diff --git a/substrate/primitives/consensus/beefy/src/test_utils.rs b/substrate/primitives/consensus/beefy/src/test_utils.rs index d7fd49214f12f..e005b62395405 100644 --- a/substrate/primitives/consensus/beefy/src/test_utils.rs +++ b/substrate/primitives/consensus/beefy/src/test_utils.rs @@ -18,18 +18,19 @@ #[cfg(feature = "bls-experimental")] use crate::ecdsa_bls_crypto; use crate::{ - ecdsa_crypto, AuthorityIdBound, BeefySignatureHasher, Commitment, DoubleVotingProof, Payload, + ecdsa_crypto, + AuthorityIdBound, BeefySignatureHasher, Commitment, DoubleVotingProof, Payload, ValidatorSetId, VoteMessage, }; use sp_application_crypto::{AppCrypto, AppPair, RuntimeAppPublic, Wraps}; -use sp_core::{ecdsa, Pair}; +use sp_core::{ecdsa, ecdsa_bls381, Pair}; use sp_runtime::traits::Hash; use codec::Encode; use std::{collections::HashMap, marker::PhantomData}; use strum::IntoEnumIterator; -/// Set of test accounts using [`crate::ecdsa_crypto`] types. +/// Set of test accounts using [`crate::ecdsa_bls_crypto`] types. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] pub enum Keyring { @@ -112,39 +113,39 @@ where } lazy_static::lazy_static! { - static ref PRIVATE_KEYS: HashMap, ecdsa_crypto::Pair> = + static ref PRIVATE_KEYS: HashMap, ecdsa_bls_crypto::Pair> = Keyring::iter().map(|i| (i.clone(), i.pair())).collect(); - static ref PUBLIC_KEYS: HashMap, ecdsa_crypto::Public> = + static ref PUBLIC_KEYS: HashMap, ecdsa_bls_crypto::Public> = PRIVATE_KEYS.iter().map(|(name, pair)| (name.clone(), sp_application_crypto::Pair::public(pair))).collect(); } -impl From> for ecdsa_crypto::Pair { - fn from(k: Keyring) -> Self { +impl From> for ecdsa_bls_crypto::Pair { + fn from(k: Keyring) -> Self { k.pair() } } -impl From> for ecdsa::Pair { - fn from(k: Keyring) -> Self { +impl From> for ecdsa_bls381::Pair { + fn from(k: Keyring) -> Self { k.pair().into() } } -impl From> for ecdsa_crypto::Public { - fn from(k: Keyring) -> Self { +impl From> for ecdsa_bls_crypto::Public { + fn from(k: Keyring) -> Self { (*PUBLIC_KEYS).get(&k).cloned().unwrap() } } /// Create a new `EquivocationProof` based on given arguments. pub fn generate_equivocation_proof( - vote1: (u64, Payload, ValidatorSetId, &Keyring), - vote2: (u64, Payload, ValidatorSetId, &Keyring), -) -> DoubleVotingProof { + vote1: (u64, Payload, ValidatorSetId, &Keyring), + vote2: (u64, Payload, ValidatorSetId, &Keyring), +) -> DoubleVotingProof { let signed_vote = |block_number: u64, payload: Payload, validator_set_id: ValidatorSetId, - keyring: &Keyring| { + keyring: &Keyring| { let commitment = Commitment { validator_set_id, block_number, payload }; let signature = keyring.sign(&commitment.encode()); VoteMessage { commitment, id: keyring.public(), signature } diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index bb04babb3f180..ed448ff753c64 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -68,6 +68,9 @@ pub mod bls381 { /// An identifier used to match public keys against BLS12-381 keys pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bls8"); + #[doc(hidden)] + pub type Bls381Tag = TinyBLS381; + /// BLS12-381 key pair. pub type Pair = super::Pair; /// BLS12-381 public key. diff --git a/substrate/primitives/core/src/lib.rs b/substrate/primitives/core/src/lib.rs index 098bd135bfebb..e4962255cadd6 100644 --- a/substrate/primitives/core/src/lib.rs +++ b/substrate/primitives/core/src/lib.rs @@ -80,7 +80,7 @@ pub mod sr25519; #[cfg(feature = "bls-experimental")] pub use bls::{bls377, bls381}; #[cfg(feature = "bls-experimental")] -pub use paired_crypto::ecdsa_bls377; +pub use paired_crypto::{ecdsa_bls377, ecdsa_bls381}; pub use self::{ hash::{convert_hash, H160, H256, H512}, diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs index 260e86b6ff9c4..f600f01e2c48d 100644 --- a/substrate/primitives/core/src/paired_crypto.rs +++ b/substrate/primitives/core/src/paired_crypto.rs @@ -126,6 +126,106 @@ pub mod ecdsa_bls377 { } } +/// ECDSA and BLS12-381 paired crypto scheme +#[cfg(feature = "bls-experimental")] +pub mod ecdsa_bls381 { + use crate::{bls381, crypto::CryptoTypeId, ecdsa}; + #[cfg(feature = "full_crypto")] + use crate::{ + crypto::{Pair as PairT, UncheckedFrom}, + Hasher, + }; + + /// An identifier used to match public keys against BLS12-381 keys + pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecb8"); + + const PUBLIC_KEY_LEN: usize = + ecdsa::PUBLIC_KEY_SERIALIZED_SIZE + bls381::PUBLIC_KEY_SERIALIZED_SIZE; + const SIGNATURE_LEN: usize = + ecdsa::SIGNATURE_SERIALIZED_SIZE + bls381::SIGNATURE_SERIALIZED_SIZE; + + #[doc(hidden)] + pub struct EcdsaBls381Tag(ecdsa::EcdsaTag, bls381::Bls381Tag); + + impl super::PairedCryptoSubTagBound for EcdsaBls381Tag {} + + /// (ECDSA,BLS12-381) key-pair pair. + pub type Pair = + super::Pair; + + /// (ECDSA,BLS12-381) public key pair. + pub type Public = super::Public; + + /// (ECDSA,BLS12-381) signature pair. + pub type Signature = super::Signature; + + impl super::CryptoType for Public { + type Pair = Pair; + } + + impl super::CryptoType for Signature { + type Pair = Pair; + } + + impl super::CryptoType for Pair { + type Pair = Pair; + } + + #[cfg(feature = "full_crypto")] + impl Pair { + /// Hashes the `message` with the specified [`Hasher`] before signing with the ECDSA secret + /// component. + /// + /// The hasher does not affect the BLS12-381 component. This generates BLS12-381 Signature + /// according to IETF standard. + pub fn sign_with_hasher(&self, message: &[u8]) -> Signature + where + H: Hasher, + H::Out: Into<[u8; 32]>, + { + let msg_hash = H::hash(message).into(); + + let mut raw: [u8; SIGNATURE_LEN] = [0u8; SIGNATURE_LEN]; + raw[..ecdsa::SIGNATURE_SERIALIZED_SIZE] + .copy_from_slice(self.left.sign_prehashed(&msg_hash).as_ref()); + raw[ecdsa::SIGNATURE_SERIALIZED_SIZE..] + .copy_from_slice(self.right.sign(message).as_ref()); + ::Signature::unchecked_from(raw) + } + + /// Hashes the `message` with the specified [`Hasher`] before verifying with the ECDSA + /// public component. + /// + /// The hasher does not affect the the BLS12-381 component. This verifies whether the + /// BLS12-381 signature was hashed and signed according to IETF standard + pub fn verify_with_hasher(sig: &Signature, message: &[u8], public: &Public) -> bool + where + H: Hasher, + H::Out: Into<[u8; 32]>, + { + let msg_hash = H::hash(message).into(); + + let Ok(left_pub) = public.0[..ecdsa::PUBLIC_KEY_SERIALIZED_SIZE].try_into() else { + return false + }; + let Ok(left_sig) = sig.0[..ecdsa::SIGNATURE_SERIALIZED_SIZE].try_into() else { + return false + }; + if !ecdsa::Pair::verify_prehashed(&left_sig, &msg_hash, &left_pub) { + return false + } + + let Ok(right_pub) = public.0[ecdsa::PUBLIC_KEY_SERIALIZED_SIZE..].try_into() else { + return false + }; + let Ok(right_sig) = sig.0[ecdsa::SIGNATURE_SERIALIZED_SIZE..].try_into() else { + return false + }; + bls381::Pair::verify(&right_sig, message, &right_pub) + } + } +} + /// Secure seed length. /// /// Currently only supporting sub-schemes whose seed is a 32-bytes array. diff --git a/substrate/primitives/core/src/testing.rs b/substrate/primitives/core/src/testing.rs index c26e23d442f1f..378b3416db7c5 100644 --- a/substrate/primitives/core/src/testing.rs +++ b/substrate/primitives/core/src/testing.rs @@ -33,6 +33,8 @@ pub const BLS377: KeyTypeId = KeyTypeId(*b"bls7"); pub const BLS381: KeyTypeId = KeyTypeId(*b"bls8"); /// Key type for (ECDSA,BLS12-377) key pair pub const ECDSA_BLS377: KeyTypeId = KeyTypeId(*b"ecb7"); +/// Key type for (ECDSA,BLS12-381) key pair +pub const ECDSA_BLS381: KeyTypeId = KeyTypeId(*b"ecb8"); /// Macro for exporting functions from wasm in with the expected signature for using it with the /// wasm executor. This is useful for tests where you need to call a function in wasm. diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index c8675a9a90bd2..bedaa3d846c6f 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -106,7 +106,7 @@ use sp_core::{ }; #[cfg(feature = "bls-experimental")] -use sp_core::{bls377, ecdsa_bls377}; +use sp_core::{bls377, bls381, ecdsa_bls377, ecdsa_bls381}; #[cfg(feature = "std")] use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration}; @@ -1236,6 +1236,40 @@ pub trait Crypto { .expect("`ecdsa_bls377_generate` failed") } + /// Generate an `bls12-381` 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. + #[cfg(feature = "bls-experimental")] + fn bls381_generate(&mut self, id: KeyTypeId, seed: Option>) -> bls381::Public { + let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); + self.extension::() + .expect("No `keystore` associated for the current context!") + .bls381_generate_new(id, seed) + .expect("`bls381_generate` failed") + } + + /// Generate an `(ecdsa,bls12-381)` 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. + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_generate( + &mut self, + id: KeyTypeId, + seed: Option>, + ) -> ecdsa_bls381::Public { + let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); + self.extension::() + .expect("No `keystore` associated for the current context!") + .ecdsa_bls381_generate_new(id, seed) + .expect("`ecdsa_bls381_generate` failed") + } + /// Generate a `bandersnatch` key pair for the given key type using an optional /// `seed` and store it in the keystore. /// diff --git a/substrate/primitives/keystore/src/lib.rs b/substrate/primitives/keystore/src/lib.rs index 64f0e3ea49e8b..1e6cbd8cc4e2c 100644 --- a/substrate/primitives/keystore/src/lib.rs +++ b/substrate/primitives/keystore/src/lib.rs @@ -27,7 +27,7 @@ pub mod testing; #[cfg(feature = "bandersnatch-experimental")] use sp_core::bandersnatch; #[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381, ecdsa_bls377}; +use sp_core::{bls377, bls381, ecdsa_bls377, ecdsa_bls381}; use sp_core::{ crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, @@ -38,7 +38,7 @@ use alloc::{string::String, sync::Arc, vec::Vec}; /// Keystore error #[derive(Debug)] pub enum Error { - /// Public key type is not supported + /// Pub1lic key type is not supported KeyNotSupported(KeyTypeId), /// Validation error ValidationError(String), @@ -288,6 +288,10 @@ pub trait Keystore: Send + Sync { #[cfg(feature = "bls-experimental")] fn ecdsa_bls377_public_keys(&self, id: KeyTypeId) -> Vec; + /// Returns all (ecdsa,bls12-381) paired public keys for the given key type. + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_public_keys(&self, id: KeyTypeId) -> Vec; + /// Generate a new bls381 key pair for the given key type and an optional seed. /// /// Returns an `bls381::Public` key of the generated key pair or an `Err` if @@ -321,6 +325,17 @@ pub trait Keystore: Send + Sync { seed: Option<&str>, ) -> Result; + /// Generate a new (ecdsa,bls381) key pair for the given key type and an optional seed. + /// + /// Returns an `ecdsa_bls381::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result; + /// Generate a bls381 signature for a given message. /// /// Receives [`KeyTypeId`] and a [`bls381::Public`] key to be able to map @@ -387,6 +402,40 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; + /// Generate a (ecdsa,bls381) signature pair for a given message. + /// + /// Receives [`KeyTypeId`] and a [`ecdsa_bls381::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`ecdsa_bls381::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> Result, Error>; + + /// Hashes the `message` using keccak256 and then signs it using ECDSA + /// algorithm. It does not affect the behavior of BLS12-381 component. It generates + /// BLS12-381 Signature according to IETF standard. + /// + /// Receives [`KeyTypeId`] and a [`ecdsa_bls381::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`ecdsa_bls381::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> Result, Error>; + /// Insert a new secret key. fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; @@ -413,6 +462,7 @@ pub trait Keystore: Send + Sync { /// - bls381 /// - bls377 /// - (ecdsa,bls377) paired keys + /// - (ecdsa,bls381) paired keys /// /// To support more schemes you can overwrite this method. /// @@ -468,7 +518,12 @@ pub trait Keystore: Send + Sync { .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; self.ecdsa_bls377_sign(id, &public, msg)?.map(|s| s.encode()) }, - + #[cfg(feature = "bls-experimental")] + ecdsa_bls381::CRYPTO_ID => { + let public = ecdsa_bls381::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.ecdsa_bls381_sign(id, &public, msg)?.map(|s| s.encode()) + }, _ => return Err(Error::KeyNotSupported(id)), }; Ok(signature) @@ -636,6 +691,11 @@ impl Keystore for Arc { (**self).ecdsa_bls377_public_keys(id) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_public_keys(&self, id: KeyTypeId) -> Vec { + (**self).ecdsa_bls381_public_keys(id) + } + #[cfg(feature = "bls-experimental")] fn bls381_generate_new( &self, @@ -663,6 +723,15 @@ impl Keystore for Arc { (**self).ecdsa_bls377_generate_new(key_type, seed) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result { + (**self).ecdsa_bls381_generate_new(key_type, seed) + } + #[cfg(feature = "bls-experimental")] fn bls381_sign( &self, @@ -693,6 +762,16 @@ impl Keystore for Arc { (**self).ecdsa_bls377_sign(key_type, public, msg) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> Result, Error> { + (**self).ecdsa_bls381_sign(key_type, public, msg) + } + #[cfg(feature = "bls-experimental")] fn ecdsa_bls377_sign_with_keccak256( &self, @@ -703,6 +782,16 @@ impl Keystore for Arc { (**self).ecdsa_bls377_sign_with_keccak256(key_type, public, msg) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> Result, Error> { + (**self).ecdsa_bls381_sign_with_keccak256(key_type, public, msg) + } + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { (**self).insert(key_type, suri, public) } diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs index 1403e4745ff11..a6182a0b163f9 100644 --- a/substrate/primitives/keystore/src/testing.rs +++ b/substrate/primitives/keystore/src/testing.rs @@ -22,7 +22,7 @@ use crate::{Error, Keystore, KeystorePtr}; #[cfg(feature = "bandersnatch-experimental")] use sp_core::bandersnatch; #[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher}; +use sp_core::{bls377, bls381, ecdsa_bls377, ecdsa_bls381, KeccakHasher}; use sp_core::{ crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, @@ -359,6 +359,43 @@ impl Keystore for MemoryKeystore { Ok(sig) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result { + self.generate_new::(key_type, seed) + } + + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> Result, Error> { + self.sign::(key_type, public, msg) + } + + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls381_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls381::Public, + msg: &[u8], + ) -> Result, Error> { + let sig = self + .pair::(key_type, public) + .map(|pair| pair.sign_with_hasher::(msg)); + Ok(sig) + } + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { self.keys .write()