-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Signer groups and rotation #938
Conversation
JesseAbram
commented
Jul 16, 2024
•
edited
Loading
edited
- Set parameters for size of signer group and T of N
- Add Signer groups to chain
- Get randomness to select new signer from validators on session refresh
- Rotate Signers
@@ -714,6 +714,7 @@ parameter_types! { | |||
impl pallet_staking_extension::Config for Runtime { | |||
type Currency = Balances; | |||
type MaxEndpointLength = MaxEndpointLength; | |||
type Randomness = pallet_babe::RandomnessFromOneEpochAgo<Runtime>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think this needs an impl_version
bump since we're already bumping the spec_version
. I also don't think the transaction_version
needs to be bumped since only new stuff is being added, as opposed to existing extrinsics being changed
pallets/staking/src/lib.rs
Outdated
#[pallet::storage] | ||
#[pallet::getter(fn signers)] | ||
pub type Signers<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>; | ||
|
||
#[pallet::storage] | ||
#[pallet::getter(fn next_signers)] | ||
pub type NextSigners<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add docs to these?
'sp-runtime/std', | ||
|
||
"rand_chacha/std", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use chaha20
here instead of something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
saw it used in the society pallet, when I have a choice like this to make I default to auditted production code, open to other ideas
// TODO: Is randomness freshness an issue here | ||
// https://github.com/paritytech/substrate/issues/8312 | ||
let (seed, _) = T::Randomness::random(phrase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So does this mean that we should also be checking how fresh the randomness is here before committing to use it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ummmmmmm no I don't believe so this is like more of a commit reveal thing where this can be useful, as in like idk a lottery, bets are placed, there is a clear end time then you grab randomness, but the way we use randomness is in an ever flowing system where validators are swapped in and out, potentially we can grab the randomness from a previous session, some more investitgation on how validators are ordered may help here, but good randomness is probably enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case this should be hashed out in #944
let mut current_signers = Self::signers(); | ||
// Since not enough validators do not allow rotation | ||
// TODO: https://github.com/entropyxyz/entropy-core/issues/943 | ||
if validators.len() <= current_signers.len() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case that validators.len() == current_signers.len()
why not allow rotation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do have #943 open for discussion about this, but if we do not let signers leave validators while in the signer group (which is also a discussion lol) then we can assume all validators are signers here and we can not let them leave in fear of losing the parent key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If validators.len() == current_signers.len()
then we still want to run the last bit of this function, which is currently just a comment - as in we still do a reshare with the same signer set.
validators.len() < current_signers.len()
i guess should be a state which is impossible to get into. Not sure how to handle that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya probably not a reshare but a refresh instead, can you add this comment to #943
let mut next_signer_up = ¤t_signers[0].clone(); | ||
let mut index; | ||
// loops to find signer in validator that is not already signer | ||
while current_signers.contains(next_signer_up) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty expensive lookup to do on a Vec
, esp with random indexes, can we get away with making Signers
a map?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmmmm ok so we do know signers is relatively small and this would cause other operations (coming in future PRs) to increase complexity (happy to explain them but honestly may be better to see in a few days) that being said Im open to making this an issue and going back over to optimise this, but for now I feel strongly about leaving this as is to push through the flow
Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great 💯 (pending CI)
Just to confirm the scope of this PR is to add a signing committee concept to the staking pallet, but not yet actually use them.
I haven't looked into the issue around 'freshness' relating to randomness, and obviously having a good source of randomness is pretty critical here, but since there is a comment and #944 i think we should move ahead with this.
As discussed elsewhere, as well as randomness there are other factors we could use in this selection process eg: stake, age of validator, and number of blocks since a validator was last in the signing committee.
As for the frequency of rotation (moving one validator in/out per session), i think this is too frequent, and should probably also be configurable as a parameter, or even depend on this size of the validator set (the more validators, the more frequent the rotation). As i mentioned before, my main concern is that the more frequent the rotation, the higher the chance that a malicious actor who controls n
nodes will control the complete signing committee. But for simplicity i think we should leave this as it is for now and merge this.
As for having t and n configurable as a parameter. I totally agree with this, and i can't see a reason why changing them with a running network would cause issues. But of course issues with this could well pop up.
} | ||
|
||
// removes first signer and pushes new signer to back | ||
current_signers.remove(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont think we need to worry too much about optimising for performance here as this only runs once per session and we know that current_signers
is small, but i agree that VecDeque
is better suited to this.
i am totally down with removing the first current signer and push
ing the new one to the end. 👍
let mut current_signers = Self::signers(); | ||
// Since not enough validators do not allow rotation | ||
// TODO: https://github.com/entropyxyz/entropy-core/issues/943 | ||
if validators.len() <= current_signers.len() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If validators.len() == current_signers.len()
then we still want to run the last bit of this function, which is currently just a comment - as in we still do a reshare with the same signer set.
validators.len() < current_signers.len()
i guess should be a state which is impossible to get into. Not sure how to handle that.
pallets/parameters/src/lib.rs
Outdated
) -> DispatchResult { | ||
T::UpdateOrigin::ensure_origin(origin)?; | ||
let signer_info = SignersSize { total_signers, threshold }; | ||
// TODO: add checks to make sure threshold is not bigger then signature size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and that threshold is greater than 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these checks being left as a TODO
? For some quick ones, e.g Peg's suggestion or threshold <= total_signers
we should be able to do now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya imma add them now
pallets/parameters/src/lib.rs
Outdated
) -> DispatchResult { | ||
T::UpdateOrigin::ensure_origin(origin)?; | ||
let signer_info = SignersSize { total_signers, threshold }; | ||
// TODO: add checks to make sure threshold is not bigger then signature size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these checks being left as a TODO
? For some quick ones, e.g Peg's suggestion or threshold <= total_signers
we should be able to do now
// no next signers at start | ||
assert_eq!(Staking::next_signers().len(), 0); | ||
|
||
Staking::new_session_handler(&vec![1u64, 2u64, 3u64]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't need to explicitly specify the u64
type in this whole test
inital_signers: initial_authorities.iter().map(|auth| { | ||
auth.0.clone() | ||
}) | ||
.collect::<Vec<_>>(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the testnet config we may want it to be empty actually, and instead have it be populated through a different jumpstart related mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe, let's open an issue here, too much discussion to be had and let's not slow down the rest of the flow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually covered in #941
Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>