diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index f255268def7b..a0bcc029b4d3 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -107,7 +107,7 @@ pallet-im-online = { path = "../../../frame/im-online", default-features = false pallet-skip-feeless-payment = { path = "../../../frame/transaction-payment/skip-feeless-payment", default-features = false } # node-specific dependencies -kitchensink-runtime = { path = "../runtime", features = ["etf"]} +kitchensink-runtime = { path = "../runtime" } node-rpc = { path = "../rpc" } node-primitives = { path = "../primitives" } @@ -119,12 +119,13 @@ try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = tr serde_json = { workspace = true, default-features = true } # etf dependencies -etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk.git", branch = "dpss-noscale" } +etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk.git", branch = "w3fbls-migration" } +ark-ec = "0.4.0" ark-std = "0.4.0" ark-serialize = "0.4.0" ark-bls12-377 = { version = "0.4.0", features = ["curve"] } sp-application-crypto = { path = "../../../primitives/application-crypto" } -w3f-bls = { path = "../../../../../bls" } +w3f-bls = "0.1.3" [dev-dependencies] sc-keystore = { path = "../../../client/keystore" } diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index 670a0f22a083..ac983be83eb5 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -18,7 +18,7 @@ //! Substrate chain configurations. -use beefy_primitives::bls_crypto::{AuthorityId as BeefyId, Public as BeefyPublic}; +use beefy_primitives::bls_crypto::AuthorityId as BeefyId; use grandpa_primitives::AuthorityId as GrandpaId; use kitchensink_runtime::{ constants::currency::*, wasm_binary_unwrap, Block, MaxNominations, SessionKeys, StakerStatus, @@ -33,21 +33,20 @@ use sp_consensus_babe::AuthorityId as BabeId; use sp_core::{crypto::UncheckedInto, sr25519, Pair, Public}; use sp_mixnet::types::AuthorityId as MixnetId; use sp_runtime::{ - traits::{IdentifyAccount, Verify, One}, + traits::{IdentifyAccount, Verify}, Perbill, RuntimeAppPublic, }; -use w3f_bls::{ - single_pop_aggregator::SignatureAggregatorAssumingPoP, DoublePublicKeyScheme, EngineBLS, Keypair, Message, PublicKey, PublicKeyInSignatureGroup, Signed, TinyBLS, TinyBLS377, -}; +use w3f_bls::{EngineBLS, TinyBLS377, SerializableToBytes}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::UniformRand; +use ark_ec::Group; use rand::rngs::OsRng; -use etf_crypto_primitives::dpss::acss::HighThresholdACSS; +use etf_crypto_primitives::dpss::acss::DoubleSecret; pub use kitchensink_runtime::RuntimeGenesisConfig; pub use node_primitives::{AccountId, Balance, Signature}; @@ -333,6 +332,7 @@ fn configure_accounts( Vec, usize, Vec<(AccountId, AccountId, Balance, StakerStatus)>, + Vec, Vec<(BeefyId, BeefyId, Vec)> ) { let mut endowed_accounts: Vec = endowed_accounts.unwrap_or_else(|| { @@ -383,54 +383,55 @@ fn configure_accounts( let num_endowed_accounts = endowed_accounts.len(); - let genesis_shares = etf_genesis( + // Aggregate BLS signature scheme with Signature in G1 for BLS12-377 curve. + let (ibe_pp_bytes, genesis_shares) = etf_genesis::( initial_authorities.iter().map(|x| x.7.clone()).collect::>(), - vec!["Alice", "Bob"], + vec!["Alice", "Bob", "Charlie"], ); - (initial_authorities, endowed_accounts, num_endowed_accounts, stakers, genesis_shares) + (initial_authorities, endowed_accounts, num_endowed_accounts, stakers, ibe_pp_bytes, genesis_shares) } /// Helper function to prepare initial secrets and resharing for ETF conensus -/// return a vec of (authority id, resharing, pubkey commitment) -pub fn etf_genesis( - initial_authorities: Vec, - seeds: Vec<&str> -) -> Vec<(BeefyId, BeefyId, Vec)> { - let msk = ark_bls12_377::Fr::rand(&mut OsRng); - let msk_prime = ark_bls12_377::Fr::rand(&mut OsRng); - - let genesis_resharing = HighThresholdACSS::reshare( - msk, - msk_prime, +/// return a vec of (authority id, resharing, pubkey commitment) along with ibe public key against the master secret +pub fn etf_genesis( + initial_authorities: Vec, + seeds: Vec<&str> + ) -> (Vec, Vec<(BeefyId, BeefyId, Vec)>) { + let msk = EB::Scalar::rand(&mut OsRng); + let msk_prime = EB::Scalar::rand(&mut OsRng); + + let double_secret = DoubleSecret::(msk, msk_prime); + + let ibe_pub_param = EB::PublicKeyGroup::generator() * msk; + let mut ibe_pp_bytes = Vec::new(); + ibe_pub_param.serialize_compressed(&mut ibe_pp_bytes).unwrap(); + + let genesis_resharing = double_secret.reshare( &initial_authorities.iter().map(|authority| { - // NO: that's 144 bytes, we only want the first 48 of them (48 + 96 bytes for both keypairs) - ark_bls12_377::G1Projective::deserialize_compressed( - &authority.to_raw_vec()[..48] - ).unwrap() + w3f_bls::single::PublicKey::::from_bytes(&authority.to_raw_vec()[..48]).unwrap() + // EB::SignatureGroup::deserialize_compressed( + // // [48 bytes for SigGroup][96 bytes for PubKeyGroup] + // &authority.to_raw_vec()[..48] + // ).unwrap() }).collect::>(), - initial_authorities.len() as u8, + initial_authorities.len() as u8, // threshold = full set of authorities for now &mut OsRng, - ); + ).unwrap(); - initial_authorities.iter().enumerate().map(|(idx, auth)| { + let resharings = initial_authorities.iter().enumerate().map(|(idx, auth)| { let pok = &genesis_resharing[idx].1; let mut bytes = Vec::new(); pok.serialize_compressed(&mut bytes).unwrap(); let seed = seeds[idx]; - // let alice_secret = w3f_bls::SecretKey::::from_seed(format!("//{}", seed.clomne)); - // let alice_public = alice_secret.into_public(); - - // let mut alice_kp = w3f_bls::Keypair { - // secret: alice_secret, - // public: alice_public - // }; let test = get_pair_from_seed::(seed); let t = sp_core::bls::Pair::::from(test); - let o = t.acss_recover(&bytes).expect("genesis shares should be well formatted"); + let o = t.acss_recover(&bytes, initial_authorities.len() as u8) + .expect("genesis shares should be well formatted"); let etf_id = BeefyId::from(o.public()); (auth.clone(), etf_id, bytes) - }).collect::>() + }).collect::>(); + (ibe_pp_bytes, resharings) } /// Helper function to create RuntimeGenesisConfig json patch for testing. @@ -449,7 +450,7 @@ pub fn testnet_genesis( root_key: AccountId, endowed_accounts: Option>, ) -> serde_json::Value { - let (initial_authorities, endowed_accounts, num_endowed_accounts, stakers, genesis_shares) = + let (initial_authorities, endowed_accounts, num_endowed_accounts, stakers, round_key, genesis_shares) = configure_accounts(initial_authorities, initial_nominators, endowed_accounts, STASH); serde_json::json!({ @@ -505,6 +506,7 @@ pub fn testnet_genesis( "authorities": Vec::::new(), "genesisBlock": Some(1), "genesisResharing": genesis_shares, + "roundPubkey": round_key, }, "society": { "pot": 0 }, "assets": { diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index 06128eafb2fc..e3e69826f6aa 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -24,7 +24,7 @@ sc-chain-spec = { path = "../../../client/chain-spec" } sc-client-api = { path = "../../../client/api" } sc-consensus-babe = { path = "../../../client/consensus/babe" } sc-consensus-babe-rpc = { path = "../../../client/consensus/babe/rpc" } -sc-consensus-beefy = { path = "../../../client/consensus/beefy", features = ["bls-experimental", "etf"]} +sc-consensus-beefy = { path = "../../../client/consensus/beefy", features = ["bls-experimental"]} sc-consensus-beefy-rpc = { path = "../../../client/consensus/beefy/rpc" } sc-consensus-grandpa = { path = "../../../client/consensus/grandpa" } sc-consensus-grandpa-rpc = { path = "../../../client/consensus/grandpa/rpc" } diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index d6ea2fdb6dd0..0d5d5919a890 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -34,7 +34,7 @@ primitive-types = { version = "0.12.0", default-features = false, features = ["c # primitives sp-authority-discovery = { path = "../../../primitives/authority-discovery", default-features = false, features = ["serde"] } sp-consensus-babe = { path = "../../../primitives/consensus/babe", default-features = false, features = ["serde"] } -sp-consensus-beefy = { path = "../../../primitives/consensus/beefy", default-features = false, features = ["bls-experimental", "etf"]} +sp-consensus-beefy = { path = "../../../primitives/consensus/beefy", default-features = false, features = ["bls-experimental"]} sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features = ["serde"] } sp-block-builder = { path = "../../../primitives/block-builder", default-features = false } sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" } diff --git a/substrate/bin/node/testing/src/keyring.rs b/substrate/bin/node/testing/src/keyring.rs index f712191bed69..ed78e489dca5 100644 --- a/substrate/bin/node/testing/src/keyring.rs +++ b/substrate/bin/node/testing/src/keyring.rs @@ -22,7 +22,7 @@ use codec::Encode; use kitchensink_runtime::{CheckedExtrinsic, SessionKeys, SignedExtra, UncheckedExtrinsic}; use node_cli::chain_spec::get_from_seed; use node_primitives::{AccountId, Balance, Nonce}; -use sp_core::{ecdsa, ed25519, sr25519}; +use sp_core::{bls377, ed25519, sr25519}; use sp_crypto_hashing::blake2_256; use sp_keyring::AccountKeyring; use sp_runtime::generic::Era; @@ -65,7 +65,7 @@ pub fn session_keys_from_seed(seed: &str) -> SessionKeys { im_online: get_from_seed::(seed).into(), authority_discovery: get_from_seed::(seed).into(), mixnet: get_from_seed::(seed).into(), - beefy: get_from_seed::(seed).into(), + beefy: get_from_seed::(seed).into(), } } diff --git a/substrate/client/consensus/beefy/Cargo.toml b/substrate/client/consensus/beefy/Cargo.toml index 484fd25fb4c1..8e7eb88850a0 100644 --- a/substrate/client/consensus/beefy/Cargo.toml +++ b/substrate/client/consensus/beefy/Cargo.toml @@ -37,15 +37,14 @@ sp-consensus = { path = "../../../primitives/consensus/common" } sp-consensus-beefy = { path = "../../../primitives/consensus/beefy" } sp-core = { path = "../../../primitives/core" } sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } -sp-keystore = { path = "../../../primitives/keystore", features = ["bls-experimental", "etf"]} +sp-keystore = { path = "../../../primitives/keystore", features = ["bls-experimental"]} sp-mmr-primitives = { path = "../../../primitives/merkle-mountain-range" } sp-runtime = { path = "../../../primitives/runtime" } tokio = "1.22.0" -# etf-crypto-primitives = { path = "../../../../../etf-sdk/etf-crypto-primitives" } -etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk.git", branch = "dpss-noscale" } +# etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk.git", branch = "w3fbls-migration" } ark-serialize = { version = "0.4.0" } ark-bls12-377 = { version = "0.4.0", features = ["curve"], optional = true} -w3f-bls = { path = "../../../../../bls" } +w3f-bls = { version = "0.1.3", optional = true } [dev-dependencies] @@ -66,6 +65,5 @@ bls-experimental = [ "sp-consensus-beefy/bls-experimental", "sp-core/bls-experimental", "ark-bls12-377", + "w3f-bls" ] - -etf = [] \ No newline at end of file diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index 2e17ad760c87..8a1718251a63 100644 --- a/substrate/client/consensus/beefy/rpc/Cargo.toml +++ b/substrate/client/consensus/beefy/rpc/Cargo.toml @@ -19,7 +19,7 @@ log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde = { features = ["derive"], workspace = true, default-features = true } thiserror = { workspace = true } -sc-consensus-beefy = { path = "..", features = ["bls-experimental", "etf"] } +sc-consensus-beefy = { path = "..", features = ["bls-experimental" ] } sp-consensus-beefy = { path = "../../../../primitives/consensus/beefy" } sc-rpc = { path = "../../../rpc" } sp-core = { path = "../../../../primitives/core" } diff --git a/substrate/client/consensus/beefy/src/communication/gossip.rs b/substrate/client/consensus/beefy/src/communication/gossip.rs index 05e5ab719663..9424874aea99 100644 --- a/substrate/client/consensus/beefy/src/communication/gossip.rs +++ b/substrate/client/consensus/beefy/src/communication/gossip.rs @@ -306,14 +306,24 @@ where } // recover the signature bytes from the payload - let raw_etf_payload = vote.commitment.payload.get_raw( - &sp_consensus_beefy::known_payloads::ETF_SIGNATURE - ).expect("its ok for now"); + let raw_etf_payload = vote.commitment.payload.get_raw(&sp_consensus_beefy::known_payloads::ETF_SIGNATURE) + .unwrap_or({ + debug!( + target: LOG_TARGET, + "🎲 Corrupted (irrecoverable) signature on message: {:?}, from: {:?}", vote, sender + ); + return Action::Discard(cost::BAD_SIGNATURE); + }); let etf_sig: Signature = Signature::decode( &mut sp_runtime::traits::TrailingZeroInput::new(&raw_etf_payload)) - .ok() - .unwrap(); + .unwrap_or({ + debug!( + target: LOG_TARGET, + "🎲 Corrupted (irrecoverable) signature on message: {:?}, from: {:?}", vote, sender + ); + return Action::Discard(cost::BAD_SIGNATURE); + }); if BeefyKeystore::verify(&vote.id, &vote.signature, &vote.commitment.encode()) // && BeefyKeystore::verify(&vote.id, &etf_sig, &round.to_string().as_bytes()) @@ -324,10 +334,6 @@ where ); Action::Keep(self.votes_topic, benefit::VOTE_MESSAGE) } else { - info!( - target: LOG_TARGET, - "🎲 The etf signature WAS NOT verified! AH FUCK!~!!!!!!!!!!!!!!!!!!", - ); debug!( target: LOG_TARGET, "🥩 Bad signature on message: {:?}, from: {:?}", vote, sender diff --git a/substrate/client/consensus/beefy/src/keystore.rs b/substrate/client/consensus/beefy/src/keystore.rs index 8bd626f28959..6fc599acf98a 100644 --- a/substrate/client/consensus/beefy/src/keystore.rs +++ b/substrate/client/consensus/beefy/src/keystore.rs @@ -35,11 +35,6 @@ use codec::Decode; use log::{info, warn}; use std::marker::PhantomData; -use etf_crypto_primitives::{ - proofs::hashed_el_gamal_sigma::BatchPoK, - dpss::acss::HighThresholdACSS -}; - use crate::{error, LOG_TARGET}; /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a @@ -217,6 +212,7 @@ impl BeefyKeystore { public: &AuthorityId, pok_bytes: &[u8], message: &[u8], + threshold: u8, ) -> Result<::Signature, error::Error> { let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into())) .map_err(|_| ()) @@ -230,6 +226,7 @@ impl BeefyKeystore { &public, pok_bytes, message, + threshold, ).map_err(|_| { error::Error::Signature(format!( "Failed to recover a key from the provided proof of knowledge" @@ -237,14 +234,9 @@ impl BeefyKeystore { })?; let mut signature_byte_array: &[u8] = sig.as_ref(); - // should this be runtimeapppublic instead? let signature = ::Signature::decode( &mut signature_byte_array, - ) - // let signature = bls377::Signature::decode( - // &mut signature_byte_array, - // ) - .map_err(|_| { + ).map_err(|_| { error::Error::Signature(format!( "invalid signature {:?} for key {:?}", signature_byte_array, public @@ -485,7 +477,7 @@ pub mod tests { let store: BeefyKeystore = Some(store).into(); - let msg = b"are you involved or commited?"; + let msg = b"are you involved or committed?"; let sig1 = store.sign(&alice, msg).unwrap(); let sig2 = Keyring::::Alice.sign(msg); @@ -521,7 +513,7 @@ pub mod tests { let alice = Keyring::Alice.public(); - let msg = b"are you involved or commited?"; + let msg = b"are you involved or committed?"; let sig = store.sign(&alice, msg).err().unwrap(); let err = Error::Signature(expected_error_message.to_string()); @@ -544,7 +536,7 @@ pub mod tests { let store: BeefyKeystore = None.into(); let alice = Keyring::Alice.public(); - let msg = b"are you involved or commited"; + let msg = b"are you involved or committed"; let sig = store.sign(&alice, msg).err().unwrap(); let err = Error::Keystore("no Keystore".to_string()); @@ -568,7 +560,7 @@ pub mod tests { let alice = Keyring::Alice.public(); // `msg` and `sig` match - let msg = b"are you involved or commited?"; + let msg = b"are you involved or committed?"; let sig = store.sign(&alice, msg).unwrap(); assert!(BeefyKeystore::verify(&alice, &sig, msg)); diff --git a/substrate/client/consensus/beefy/src/round.rs b/substrate/client/consensus/beefy/src/round.rs index 88d7e8762ad8..56123b768f01 100644 --- a/substrate/client/consensus/beefy/src/round.rs +++ b/substrate/client/consensus/beefy/src/round.rs @@ -123,9 +123,6 @@ where &mut self, vote: VoteMessage, AuthorityId, Signature>, ) -> VoteImportResult { - - info!("TONY CALLING ADD VOTE"); - let num = vote.commitment.block_number; let vote_key = (vote.id.clone(), num); @@ -173,8 +170,6 @@ where { if let Some(round) = self.rounds.remove_entry(&vote.commitment) { return VoteImportResult::RoundConcluded(self.signed_commitment(round)) - } else { - info!("TONY ROUND NOT CONCLUDED BUT WE DID TRY!"); } } VoteImportResult::Ok diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs index 13ef74d14d91..9f2518d7831a 100644 --- a/substrate/client/consensus/beefy/src/worker.rs +++ b/substrate/client/consensus/beefy/src/worker.rs @@ -566,15 +566,11 @@ where &mut self, vote: VoteMessage, AuthorityId, Signature>, ) -> Result>, Error> { - - info!("TONY IN HANDLE_VOTE"); - let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?; let block_number = vote.commitment.block_number; match rounds.add_vote(vote) { VoteImportResult::RoundConcluded(signed_commitment) => { - info!("TONY IN HANDLE_VOTE: ROUND CONCLUDED"); let finality_proof = VersionedFinalityProof::V1(signed_commitment); debug!( target: LOG_TARGET, @@ -587,7 +583,6 @@ where return Ok(Some(finality_proof)) }, VoteImportResult::Ok => { - info!("TONY IN HANDLE_VOTE: VOTE IMPORT OK"); // Persist state after handling mandatory block vote. if self .voting_oracle() @@ -745,13 +740,6 @@ where }; let target_hash = target_header.hash(); - // let payload = if let Some(hash) = self.payload_provider.payload(&target_header) { - // hash - // } else { - // warn!(target: LOG_TARGET, "🥩 No MMR root digest found for: {:?}", target_hash); - // return Ok(()) - // }; - let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?; let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); @@ -799,22 +787,11 @@ where ) ); - // if it was valid then we instantly schedule the next round - // let next_round_number: NumberFor = 1u64.into(); - // self.persisted_state.voting_oracle.add_session( - // Rounds::new( - // next_round_number, - // - // )); - - // self.init_session_at(rounds.validator_set().clone(), NumberFor::TryFrom); - - let vote = VoteMessage { commitment, id: etf_authority_id, signature }; + let vote = VoteMessage { commitment, id: authority_id, signature }; if let Some(finality_proof) = self.handle_vote(vote.clone()).map_err(|err| { error!(target: LOG_TARGET, "🥩 Error handling self vote: {}", err); err })? { - info!("TONY ***************** CREATED THE FINALITY PROOF WOO"); let encoded_proof = GossipMessage::::FinalityProof(finality_proof).encode(); self.comms .gossip_engine @@ -824,7 +801,6 @@ where debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", vote); let encoded_vote = GossipMessage::::Vote(vote).encode(); self.comms.gossip_engine.gossip_message(votes_topic::(), encoded_vote, false); - info!("TONY ***************** GOSSIPED VOTE WOO"); } // Persist state after vote to avoid double voting in case of voter restarts. @@ -848,7 +824,6 @@ where // If the current target is a mandatory block, // make sure there's also an on-demand justification request out for it. if let Some((block, active)) = self.voting_oracle().mandatory_pending() { - // TONY TODO: could this be why I'm not getting finalized heads? // This only starts new request if there isn't already an active one. self.comms.on_demand_justifications.request(block, active); } @@ -862,11 +837,8 @@ where hash: B::Hash, id: AuthorityId, message: &[u8] - ) - -> Option - { + ) -> Option { let runtime_api = self.runtime.runtime_api(); - info!( target: LOG_TARGET, "🎲 run ACSS recovery at best grandpa: #{:?}.", @@ -874,7 +846,11 @@ where ); if let Some(Some(validator_set)) = runtime_api.validator_set(hash).ok() { if let Some(Some(pok_bytes)) = runtime_api.read_share(hash, id.clone()).ok() { - if let Ok(sig) = self.key_store.etf_sign(&id, &pok_bytes, &message) { + if let Ok(sig) = self.key_store.etf_sign( + &id, + &pok_bytes, + &message, validator_set.len() as u8 + ) { return Some(sig); } } diff --git a/substrate/client/keystore/src/local.rs b/substrate/client/keystore/src/local.rs index 2fc9bebe6876..3f54560290dd 100644 --- a/substrate/client/keystore/src/local.rs +++ b/substrate/client/keystore/src/local.rs @@ -426,10 +426,11 @@ impl Keystore for LocalKeystore { public: &bls377::Public, pok_bytes: &[u8], message: &[u8], + threshold: u8, ) -> std::result::Result { if let Some(Some(etf_pair)) = self.0.read() .key_pair_by_type::(public, key_type)? - .map(|pair| pair.acss_recover(pok_bytes.clone())) { + .map(|pair| pair.acss_recover(pok_bytes.clone(), threshold)) { // "IBE.Extract" Q = s*H(message) + DLEQ Proof let extract = etf_pair.sign(&message); // let pk = etf_pair.public(); diff --git a/substrate/frame/beefy/Cargo.toml b/substrate/frame/beefy/Cargo.toml index 273f8a77befd..bb292f0873ab 100644 --- a/substrate/frame/beefy/Cargo.toml +++ b/substrate/frame/beefy/Cargo.toml @@ -29,7 +29,6 @@ sp-std = { path = "../../primitives/std", default-features = false } ark-serialize = { version = "0.4.0", default-features = false } ark-std = { version = "0.4.0", default-features = false } ark-bls12-377 = { version = "0.4.0", features = ["curve"], default-features = false } -# etf-crypto-primitives = { path = "../../../../etf-sdk/etf-crypto-primitives", default-features = false } getrandom = { version = "0.2", features = ["js"] } [dev-dependencies] @@ -50,7 +49,6 @@ std = [ "ark-std/std", "ark-serialize/std", "ark-bls12-377/std", - # "etf-crypto-primitives/std", "codec/std", "frame-election-provider-support/std", "frame-support/std", diff --git a/substrate/frame/beefy/src/lib.rs b/substrate/frame/beefy/src/lib.rs index 20c5e00a6e5e..a9363d8f4766 100644 --- a/substrate/frame/beefy/src/lib.rs +++ b/substrate/frame/beefy/src/lib.rs @@ -62,6 +62,7 @@ use crate::equivocation::{EquivocationEvidenceFor}; const LOG_TARGET: &str = "runtime::beefy"; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -137,8 +138,6 @@ pub mod pallet { StorageValue<_, BoundedVec, ValueQuery>; /// publicly verifiable shares for the current round (a resharing) - /// here we assume that they follow the same order as the Authorities storage value vec - /// later on we need to modify this to use merkle roots so we can change the ETF authority set #[pallet::storage] pub type Shares = StorageValue<_, BoundedVec>, T::MaxAuthorities>, ValueQuery>; @@ -149,6 +148,11 @@ pub mod pallet { pub type Commitments = StorageValue<_, BoundedVec, ValueQuery>; + /// the public key for the round (or rounds) + #[pallet::storage] + pub type RoundPublic = + StorageValue<_, BoundedVec>, ValueQuery>; + /// A mapping from BEEFY set ID to the index of the *most recent* session for which its /// members were responsible. /// @@ -179,7 +183,9 @@ pub mod pallet { /// to guarantee the client gets a finality notification for exactly this block. pub genesis_block: Option>, /// (beefy id, commitment, BatchPoK (which technically contains the commitment...)) - pub genesis_resharing: Vec<(T::BeefyId, T::BeefyId, Vec)> + pub genesis_resharing: Vec<(T::BeefyId, T::BeefyId, Vec)>, + /// the round pubkey is the IBE master secret multiplied by a given group generator (e.g r = sP) + pub round_pubkey: Vec, } impl Default for GenesisConfig { @@ -187,10 +193,12 @@ pub mod pallet { // BEEFY genesis will be first BEEFY-MANDATORY block, // use block number one instead of chain-genesis. let genesis_block = Some(One::one()); + // by default, etf consensus will fail, must be intentionally seeded Self { authorities: Vec::new(), genesis_block, genesis_resharing: Vec::new(), + round_pubkey: Vec::new(), } } } @@ -202,8 +210,10 @@ pub mod pallet { // we panic here as runtime maintainers can simply reconfigure genesis and restart // the chain easily .expect("Authorities vec too big"); - Pallet::::initialize_genesis_shares(&self.genesis_resharing) - .expect("The genesis resharing should be correctly derived"); + Pallet::::initialize_genesis_shares( + &self.genesis_resharing, + self.round_pubkey.clone(), + ).expect("The genesis resharing should be correctly derived"); GenesisBlock::::put(&self.genesis_block); } } @@ -323,15 +333,6 @@ use frame_system::offchain::SubmitTransaction; impl Pallet { - // /// try to read shares at a given index - // pub fn read_share(at: u8) -> Option> { - // // let shares = Shares::::get(); - // // if at as usize >= shares.len() { - // // return None; - // // } - // // Some(shares[at as usize].clone().into_inner()) - // } - /// Return the current active BEEFY validator set. pub fn validator_set() -> Option> { let validators: BoundedVec = Authorities::::get(); @@ -421,15 +422,18 @@ impl Pallet { } fn initialize_genesis_shares( - genesis_resharing: &Vec<(T::BeefyId, T::BeefyId, Vec)> + genesis_resharing: &Vec<(T::BeefyId, T::BeefyId, Vec)>, + round_key: Vec, ) -> Result<(), ()> { - // TODO: we need to convert back to BatchPoks and get the pubkey commitments - // then we need to aggregate them and encode it onchain + // TODO: define 96 as a const + let bounded_rk = + BoundedVec::>::try_from(round_key) + .expect("The round key should be 96 bytes."); + >::put(bounded_rk); - // let mut round_pubkey = ark_bls12_377::G1Projective::zero(); let mut unbounded_shares: Vec>> = Vec::new(); let mut unbounded_commitments: Vec = Vec::new(); - // let mut shares: = Vec::new(); + genesis_resharing.iter().for_each(|(public, commitment, pok_bytes)| { let bounded_pok = @@ -437,22 +441,12 @@ impl Pallet { .expect("genesis poks should be well formatted"); unbounded_shares.push(bounded_pok); - - // let bounded_commitment = - // BoundedVec::>::try_from(commitment_bytes.clone()) - // .expect("genesis commitments should be well formatted"); - - // unbounded_commitments.push(commitment.clone()); - - // let pok: BatchPoK = - // BatchPoK::deserialize_compressed(&pok_bytes[..]) - // .expect("Genesis shares should be well formatted."); - // we could also check the proofs here, but we can trust them on genesis for nows }); let bounded_shares = - BoundedVec::>, T::MaxAuthorities>::try_from(unbounded_shares) - .expect("There should be the correct number of genesis resharings"); + BoundedVec::>, T::MaxAuthorities>::try_from( + unbounded_shares + ).expect("There should be the correct number of genesis resharings"); >::put(bounded_shares); let bounded_commitments = diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index f49a73e86529..4b6718cbffb7 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -212,6 +212,8 @@ impl pallet_offences::Config for Test { #[derive(Default)] pub struct ExtBuilder { authorities: Vec, + genesis_resharing: Vec<(T::BeefyId, T::BeefyId, Vec)>, + round_pubkey: Vec, } impl ExtBuilder { @@ -222,6 +224,18 @@ impl ExtBuilder { self } + #[cfg(test)] + pub(crate) fn add_resharing(mut self, genesis_resharing: Vec<(T::BeefyId, T::BeefyId, Vec)>) -> Self { + self.genesis_resharing = genesis_resharing; + self + } + + #[cfg(test)] + pub(crate) fn add_round_key(mut self, round_pubkey: Vec) -> Self { + self.round_pubkey = round_pubkey; + self + } + pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); @@ -292,6 +306,11 @@ pub fn mock_authorities(vec: Vec) -> Vec { vec.into_iter().map(|id| mock_beefy_id(id)).collect() } + +pub fn mock_resharing(vec: Vec<(u8, u8, Vec)>) -> Vec { + vec.into_iter().map(|id| (mock_beefy_id(id.0), mock_beefy_id(id.1), id.2)).collect() +} + pub fn start_session(session_index: SessionIndex) { for i in Session::current_index()..session_index { System::on_finalize(System::block_number()); diff --git a/substrate/frame/beefy/src/tests.rs b/substrate/frame/beefy/src/tests.rs index 6a6aa245ce1f..ecda810b494d 100644 --- a/substrate/frame/beefy/src/tests.rs +++ b/substrate/frame/beefy/src/tests.rs @@ -64,6 +64,35 @@ fn genesis_session_initializes_authorities() { }); } +#[test] +fn genesis_session_initializes_resharing_and_commitments_with_valid_values() { + let genesis_resharing = mock_resharing( + vec![ + (1, 2, vec![1, 2]), + (2, 3, vec![2, 3]) + ]); + let want_resharing = genesis_resharing.clone(); + + let genesis_roundkey = [1;96].to_vec(); + + ExtBuilder::default() + .add_resharing(genesis_resharing) + .add_round_key(genesis_roundkey) + .build_and_execute(|| + { + // resharings are populated + let resharings = beefy::Shares::::get(); + assert_eq!(resharings.len(), 2); + assert_eq!(resharings[0], want_resharing[0].2); + assert_eq!(resharings[1], want_resharing[1].2); + + let commitments = beefy::Commitments::::get(); + assert_eq!(commitments.len(), 2); + assert_eq!(commitments[0], want_resharing[0].1); + assert_eq!(commitments[1], want_resharing[1].1); + }); +} + #[test] fn session_change_updates_authorities() { let authorities = mock_authorities(vec![1, 2, 3, 4]); diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index c9921e3569da..bf0113cb5b8b 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -31,9 +31,9 @@ lazy_static = { version = "1.4.0", optional = true } [dev-dependencies] array-bytes = "6.1" -# w3f-bls = { version = "0.1.3", features = ["std"] } -w3f-bls = { path = "../../../../../bls", features = ["std"] } -# w3f-bls = { git = "https://github.com/driemworks/bls.git", branch = "etf", features = ["std"]} +w3f-bls = { version = "0.1.3", features = ["std"] } +# w3f-bls = { path = "../../../../../bls", features = ["std"] } +# w3f-bls = { git = "https://github.com/driemworks/bls.git", branch = "etf", features = ["std"] } [features] default = ["std"] @@ -68,5 +68,3 @@ bls-experimental = [ "sp-application-crypto/bls-experimental", "sp-core/bls-experimental", ] - -etf = [] \ No newline at end of file diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index e831373b6234..a7e17e6d8350 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -61,12 +61,12 @@ k256 = { version = "0.13.3", features = ["alloc", "ecdsa"], default-features = f secp256k1 = { version = "0.28.0", default-features = false, features = ["alloc", "recovery"], optional = true } # bls crypto -# w3f-bls = { version = "0.1.3", default-features = false, optional = true } -# w3f-bls = { git = "https://github.com/driemworks/bls.git", branch = "etf", default-features = false, optional = true } -w3f-bls = { path = "../../../../bls", default-features = false, optional = true } +w3f-bls = { version = "0.1.3", default-features = false, optional = true } # bandersnatch crypto bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "e9782f9", default-features = false, features = ["substrate-curves"], optional = true } -etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk.git", branch = "dpss-noscale", default-features = false, optional = true} +# etf algs +# etf-crypto-primitives = { path = "../../../../etf-sdk/etf-crypto-primitives", default-features = false, optional = true } +etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk.git", branch = "w3fbls-migration", default-features = false, optional = true} ark-serialize = { version = "0.4.0", default-features = false } @@ -158,7 +158,10 @@ full_crypto = [ # This feature adds BLS crypto primitives. # It should not be used in production since the implementation and interface may still # be subject to significant changes. -bls-experimental = ["w3f-bls", "etf-crypto-primitives"] +bls-experimental = [ + "w3f-bls", + "etf-crypto-primitives" +] # This feature adds Bandersnatch crypto primitives. # It should not be used in production since the implementation and interface may still diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index 1f8102b11b5c..2ceb962ea5ca 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -35,6 +35,11 @@ use w3f_bls::{ SecretKey, SerializableToBytes, TinyBLS381, }; +use etf_crypto_primitives::{ + dpss::acss::SKWrapper, + proofs::hashed_el_gamal_sigma::BatchPoK +}; + use ark_serialize::CanonicalDeserialize; /// BLS-377 specialized types @@ -144,15 +149,13 @@ fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> } impl Pair { - pub fn acss_recover(&self, pok_bytes: &[u8]) -> Option { + pub fn acss_recover(&self, pok_bytes: &[u8], threshold: u8) -> Option { let mut mutable_self = self.clone(); - if let Ok(pok) = etf_crypto_primitives::proofs::hashed_el_gamal_sigma::BatchPoK:::: + if let Ok(pok) = BatchPoK:::: deserialize_compressed(&pok_bytes[..]) { - if let Some(recovered) = DoublePublicKeyScheme::acss_recover( - &mut mutable_self.0, - pok - ) { - let secret = w3f_bls::SecretKeyVT(recovered).into_split_dirty(); + let sk = SKWrapper(mutable_self.0.into_vartime()); + if let Ok(recovered) = sk.recover(pok, threshold) { + let secret = w3f_bls::SecretKeyVT(recovered.0).into_split_dirty(); let public = secret.into_public(); return Some(Pair(w3f_bls::Keypair { secret, public, @@ -179,7 +182,7 @@ impl TraitPair for Pair { fn derive>( &self, - path: Iter, + path: Iter, seed: Option, ) -> Result<(Self, Option), DeriveError> { let mut acc: [u8; SECRET_KEY_SERIALIZED_SIZE] = @@ -436,4 +439,9 @@ mod tests { // Poorly-sized assert!(deserialize_signature("\"abc123\"").is_err()); } + + #[test] + fn acss_recover_works() { + + } } diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index 9ea1dd80b545..4f1ea47847ad 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -962,7 +962,7 @@ pub trait Pair: CryptoType + Sized { fn to_raw_vec(&self) -> Vec; #[cfg(feature = "etf")] - fn acss_recover(&self, pok: &[u8]); + fn acss_recover(&self, pok: &[u8], threshold: u8); } /// One type is wrapped by another. diff --git a/substrate/primitives/core/src/crypto_bytes.rs b/substrate/primitives/core/src/crypto_bytes.rs index 75af5edcd286..8fa231db9497 100644 --- a/substrate/primitives/core/src/crypto_bytes.rs +++ b/substrate/primitives/core/src/crypto_bytes.rs @@ -3,7 +3,7 @@ // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 -// Licensed under the Apache License, 1sion 2.0 (the "License"); +// 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 // diff --git a/substrate/primitives/keystore/src/lib.rs b/substrate/primitives/keystore/src/lib.rs index aa94ee169016..8ab9641d8578 100644 --- a/substrate/primitives/keystore/src/lib.rs +++ b/substrate/primitives/keystore/src/lib.rs @@ -393,7 +393,8 @@ pub trait Keystore: Send + Sync { key_type: KeyTypeId, public: &bls377::Public, pok_bytes: &[u8], - message: &[u8] + message: &[u8], + threshold: u8, ) -> Result; /// Insert a new secret key. @@ -719,8 +720,9 @@ impl Keystore for Arc { public: &bls377::Public, pok_bytes: &[u8], message: &[u8], + threshold: u8, ) -> Result { - (**self).acss_recover(key_type, public, pok_bytes, message) + (**self).acss_recover(key_type, public, pok_bytes, message, threshold) } diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs index 8e29681e9e7b..0054fee5ddd1 100644 --- a/substrate/primitives/keystore/src/testing.rs +++ b/substrate/primitives/keystore/src/testing.rs @@ -365,13 +365,9 @@ impl Keystore for MemoryKeystore { key_type: KeyTypeId, public: &bls377::Public, pok: &[u8], - msg: &[u8] + msg: &[u8], + threshold: u8 ) -> Result { - // let sig = self - // .pair::(key_type, public) - // .map(|pair| pair.sign(msg)); - // Ok(sig) - // Ok(None) Err(Error::Unavailable) }