A Tetcore frame to add/remove authorities/validators using extrinsics, in Tetcore-based PoA networks.
Note: Current master is compatible with Tetcore monthly-2021-12 tag. For older versions, please see releases/tags.
To see this pallet in action in a Substrate runtime, watch this video - https://www.youtube.com/watch?v=lIYxE-tOAdw
Add the module's dependency in the Cargo.toml
of your runtime directory. Make sure to enter the correct path or git url of the pallet as per your setup.
Make sure that you also have the Substrate session pallet as part of your runtime. This is because the validator-set pallet is dependent on the session pallet.
```toml [dependencies.validator-set] default-features = false package = 'substrate-validator-set' git = 'https://github.com/gautamdhameja/substrate-validator-set.git' version = '4.0.0-dev'
[dependencies.pallet-session] default-features = false git = 'https://github.com/paritytech/substrate.git' tag = 'monthly-2021-12' version = '4.0.0-dev' ```
toml
std = [
...
'validator-set/std',
'pallet-session/std',
]
OpaqueKeys
in your runtime/src/lib.rs
.rust
use sp_runtime::traits::{
AccountIdLookup, BlakeTwo256, Block as BlockT, Verify, IdentifyAccount, NumberFor, OpaqueKeys,
};
runtime/src/lib.rs
import the EnsureRoot
trait. This would change if you want to configure a custom origin (see below).rust
use frame_system::EnsureRoot;
runtime/src/lib.rs
. The pallet supports configurable origin and you can either set it to use one of the governance pallets (Collective, Democracy, etc.), or just use root as shown below. But do not use a normal origin here because the addition and removal of validators should be done using elevated privileges.```rust parameter_types! { pub const MinAuthorities: u32 = 2; }
impl validator_set::Config for Runtime {
type Event = Event;
type AddRemoveOrigin = EnsureRoot
runtime/src/lib.rs
. Some of the type configuration of session pallet would depend on the ValidatorSet pallet as shown below.```rust parameter_types! { pub const Period: u32 = 2 * MINUTES; pub const Offset: u32 = 0; }
impl palletsession::Config for Runtime {
type ValidatorId =
validator_set
, and session
pallets in construct_runtime
macro. Make sure to add them before Aura
and Grandpa
pallets and after Balances
. Also make sure that the validator_set
pallet is added before the session
pallet, because it provides the initial validators at genesis, and must initialize first.rust
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
...
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
ValidatorSet: validator_set::{Pallet, Call, Storage, Event<T>, Config<T>},
Session: pallet_session::{Pallet, Call, Storage, Event, Config<T>},
Aura: pallet_aura::{Pallet, Config<T>},
Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event},
...
...
}
);
opaque::SessionKeys, ValidatorSetConfig, SessionConfig
from the runtime in node/src/chain_spec.rs
.rust
use node_template_runtime::{
AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig,
SudoConfig, SystemConfig, WASM_BINARY, Signature,
opaque::SessionKeys, ValidatorSetConfig, SessionConfig
};
node/src/chain_spec.rs
update the key generation functions.```rust fn session_keys(aura: AuraId, grandpa: GrandpaId) -> SessionKeys { SessionKeys { aura, grandpa } }
pub fn authoritykeysfromseed(s: &str) -> (AccountId, AuraId, GrandpaId) {
(
getaccountidfromseed::
chain_spec.rs
file for session
and validatorset
pallets, and update it for Aura
and Grandpa
pallets. Because the validators are provided by the session
pallet, we do not initialize them explicitly for Aura
and Grandpa
pallets. Order is important, notice that pallet_session
is declared after pallet_balances
since it depends on it (session accounts should have some balance).rust
fn testnet_genesis(initial_authorities: Vec<(AccountId, AuraId, GrandpaId)>,
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
_enable_println: bool) -> GenesisConfig {
GenesisConfig {
...,
balances: BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(),
},
validator_set: ValidatorSetConfig {
initial_validators: initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
},
session: SessionConfig {
keys: initial_authorities.iter().map(|x| {
(x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone()))
}).collect::<Vec<_>>(),
},
aura: AuraConfig {
authorities: vec![],
},
grandpa: GrandpaConfig {
authorities: vec![],
},
}
}
json
{
"Keys": "SessionKeys2"
}
Once you have set up the pallet in your node/node-template and everything compiles, follow the steps in docs/local-network-setup.md to run a local network and add validators. Also, watch this video to see this in action - https://www.youtube.com/watch?v=lIYxE-tOAdw.
Instead of using sudo
, for a council-based governance, use the pallet with the Collective
pallet. Follow the steps in docs/council-integration.md.
When a validator goes offline, it skips its block production slot in Aura and that causes increased block times. Sometimes, we want to remove these offline validators so that the block time can recover to normal. The ImOnline
pallet, when added to a runtime, can report offline validators and the ValidatorSet
pallet implements the required types to integrate with ImOnline
pallet for automatic removal of offline validators. To use the ValidatorSet
pallet with the ImOnline
pallet, follow the steps in docs/im-online-integration.md.
This code not audited and reviewed for production use cases. You can expect bugs and security vulnerabilities. Do not use it as-is in real applications.