diff --git a/Cargo.lock b/Cargo.lock index adc08520..74572d58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7477,6 +7477,7 @@ dependencies = [ "frame-benchmarking-cli", "frame-system", "jsonrpsee", + "pallet-tendermint", "pallet-transaction-payment", "pallet-transaction-payment-rpc", "sc-cli", @@ -7494,6 +7495,7 @@ dependencies = [ "serai-consensus", "serai-runtime", "sp-api", + "sp-application-crypto", "sp-block-builder", "sp-blockchain", "sp-core", @@ -7553,6 +7555,7 @@ dependencies = [ "sp-inherents", "sp-offchain", "sp-runtime", + "sp-session", "sp-std", "sp-transaction-pool", "sp-version", diff --git a/substrate/consensus/src/validators.rs b/substrate/consensus/src/validators.rs index d537ffd5..49b49811 100644 --- a/substrate/consensus/src/validators.rs +++ b/substrate/consensus/src/validators.rs @@ -1,23 +1,72 @@ -// TODO: This should be built around pallet_sessions (and pallet_staking?). +use core::ops::Deref; +use std::sync::{Arc, RwLock}; use sp_application_crypto::{ RuntimePublic as PublicTrait, Pair as PairTrait, sr25519::{Public, Pair, Signature}, }; +use sp_staking::SessionIndex; +use pallet_session::Pallet as Session; + use tendermint_machine::ext::{BlockNumber, Round, Weights, SignatureScheme}; -const VALIDATORS: usize = 1; +struct TendermintValidatorsStruct { + session: SessionIndex, -pub(crate) struct TendermintValidators { - keys: Pair, // sp_keystore - lookup: Vec, // sessions + total_weight: u64, + weights: Vec, + + keys: Pair, // TODO: sp_keystore + lookup: Vec, // TODO: sessions } +impl TendermintValidatorsStruct { + fn from_module() -> TendermintValidatorsStruct { + let validators = Session::::validators(); + assert_eq!(validators.len(), 1); + let keys = Pair::from_string("//Alice", None).unwrap(); + TendermintValidatorsStruct { + session: Session::::current_index(), + + // TODO + total_weight: validators.len().try_into().unwrap(), + weights: vec![1; validators.len()], + + lookup: vec![keys.public()], + keys, + } + } +} + +// Wrap every access of the validators struct in something which forces calling refresh +struct Refresh { + _refresh: Arc>, +} +impl Refresh { + // If the session has changed, re-create the struct with the data on it + fn refresh(&self) { + let session = self._refresh.read().unwrap().session; + if session != Session::::current_index() { + *self._refresh.write().unwrap() = TendermintValidatorsStruct::from_module(); + } + } +} + +impl Deref for Refresh { + type Target = RwLock; + fn deref(&self) -> &RwLock { + self.refresh(); + &self._refresh + } +} + +pub(crate) struct TendermintValidators(Refresh); impl TendermintValidators { pub(crate) fn new() -> TendermintValidators { - let keys = Pair::from_string("//Alice", None).unwrap(); - TendermintValidators { lookup: vec![keys.public()], keys } + TendermintValidators(Refresh { + _refresh: Arc::new(RwLock::new(TendermintValidatorsStruct::from_module())), + }) } } @@ -27,11 +76,11 @@ impl SignatureScheme for TendermintValidators { type AggregateSignature = Vec; fn sign(&self, msg: &[u8]) -> Signature { - self.keys.sign(msg) + self.0.read().unwrap().keys.sign(msg) } fn verify(&self, validator: u16, msg: &[u8], sig: &Signature) -> bool { - self.lookup[usize::try_from(validator).unwrap()].verify(&msg, sig) + self.0.read().unwrap().lookup[usize::try_from(validator).unwrap()].verify(&msg, sig) } fn aggregate(sigs: &[Signature]) -> Vec { @@ -55,13 +104,15 @@ impl Weights for TendermintValidators { type ValidatorId = u16; fn total_weight(&self) -> u64 { - VALIDATORS.try_into().unwrap() - } - fn weight(&self, id: u16) -> u64 { - [1; VALIDATORS][usize::try_from(id).unwrap()] + self.0.read().unwrap().total_weight } + fn weight(&self, id: u16) -> u64 { + self.0.read().unwrap().weights[usize::try_from(id).unwrap()] + } + + // TODO fn proposer(&self, number: BlockNumber, round: Round) -> u16 { - u16::try_from((number.0 + u64::from(round.0)) % u64::try_from(VALIDATORS).unwrap()).unwrap() + u16::try_from(number.0 + u64::from(round.0)).unwrap() } } diff --git a/substrate/node/Cargo.toml b/substrate/node/Cargo.toml index 262e6c20..316c458e 100644 --- a/substrate/node/Cargo.toml +++ b/substrate/node/Cargo.toml @@ -16,6 +16,7 @@ clap = { version = "4", features = ["derive"] } jsonrpsee = { version = "0.15", features = ["server"] } sp-core = { git = "https://github.com/serai-dex/substrate" } +sp-application-crypto = { git = "https://github.com/serai-dex/substrate" } sp-runtime = { git = "https://github.com/serai-dex/substrate" } sp-timestamp = { git = "https://github.com/serai-dex/substrate" } sp-inherents = { git = "https://github.com/serai-dex/substrate" } @@ -46,6 +47,7 @@ sc-rpc-api = { git = "https://github.com/serai-dex/substrate" } substrate-frame-rpc-system = { git = "https://github.com/serai-dex/substrate" } pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate" } +pallet-tendermint = { path = "../pallet-tendermint", default-features = false } serai-runtime = { path = "../runtime" } serai-consensus = { path = "../consensus" } diff --git a/substrate/node/src/chain_spec.rs b/substrate/node/src/chain_spec.rs index fb21bec7..ab0d1f56 100644 --- a/substrate/node/src/chain_spec.rs +++ b/substrate/node/src/chain_spec.rs @@ -1,8 +1,12 @@ use sc_service::ChainType; use sp_core::{Pair as PairTrait, sr25519::Pair}; +use pallet_tendermint::crypto::Public; -use serai_runtime::{WASM_BINARY, AccountId, GenesisConfig, SystemConfig, BalancesConfig}; +use serai_runtime::{ + WASM_BINARY, AccountId, opaque::SessionKeys, GenesisConfig, SystemConfig, BalancesConfig, + SessionConfig, +}; pub type ChainSpec = sc_service::GenericChainSpec; @@ -15,12 +19,16 @@ fn account_id_from_name(name: &'static str) -> AccountId { } fn testnet_genesis(wasm_binary: &[u8], endowed_accounts: Vec) -> GenesisConfig { + let alice = account_id_from_name("Alice"); GenesisConfig { system: SystemConfig { code: wasm_binary.to_vec() }, balances: BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), }, transaction_payment: Default::default(), + session: SessionConfig { + keys: vec![(alice, alice, SessionKeys { tendermint: Public::from(alice) })], + }, } } diff --git a/substrate/runtime/Cargo.toml b/substrate/runtime/Cargo.toml index 62f13bd3..be0f08cd 100644 --- a/substrate/runtime/Cargo.toml +++ b/substrate/runtime/Cargo.toml @@ -23,6 +23,7 @@ sp-std = { git = "https://github.com/serai-dex/substrate", default-features = fa sp-version = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-inherents = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-offchain = { git = "https://github.com/serai-dex/substrate", default-features = false } +sp-session = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-transaction-pool = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-block-builder = { git = "https://github.com/serai-dex/substrate", default-features = false} sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false } @@ -61,6 +62,7 @@ std = [ "sp-version/std", "sp-inherents/std", "sp-offchain/std", + "sp-session/std", "sp-transaction-pool/std", "sp-block-builder/std", "sp-runtime/std", diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index 95c528c5..3f762595 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -7,7 +7,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use sp_core::OpaqueMetadata; pub use sp_core::sr25519::{Public, Signature}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + create_runtime_str, generic, impl_opaque_keys, KeyTypeId, traits::{Convert, OpaqueKeys, IdentityLookup, BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, @@ -349,6 +349,18 @@ sp_api::impl_runtime_apis! { } } + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + opaque::SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + opaque::SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account)