mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Staking pallet (#373)
* initial staking pallet * add staking pallet to runtime * support session rotation for serai * optimizations & cleaning * fix deny * add serai network to initial networks * a few tweaks & comments * fix some pr comments * Rewrite validator-sets with logarithmic algorithms Uses the fact the underlying DB is sorted to achieve sorting of potential validators by stake. Removes release of deallocated stake for now. --------- Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
This commit is contained in:
@@ -254,7 +254,10 @@ pub(crate) async fn scan_tributaries<
|
||||
// TODO2: Differentiate connection errors from invariants
|
||||
Err(e) => {
|
||||
// Check if this failed because the keys were already set by someone else
|
||||
if matches!(serai.get_keys(spec.set()).await, Ok(Some(_))) {
|
||||
// TODO: hash_with_keys is latest, yet we'll remove old keys from storage
|
||||
let hash_with_keys = serai.get_latest_block_hash().await.unwrap();
|
||||
if matches!(serai.get_keys(spec.set(), hash_with_keys).await, Ok(Some(_)))
|
||||
{
|
||||
log::info!("another coordinator set key pair for {:?}", set);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -35,12 +35,14 @@ async fn in_set(
|
||||
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||
serai: &Serai,
|
||||
set: ValidatorSet,
|
||||
block_hash: [u8; 32],
|
||||
) -> Result<Option<bool>, SeraiError> {
|
||||
let Some(data) = serai.get_validator_set(set).await? else {
|
||||
let Some(participants) = serai.get_validator_set_participants(set.network, block_hash).await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let key = (Ristretto::generator() * key.deref()).to_bytes();
|
||||
Ok(Some(data.participants.iter().any(|(participant, _)| participant.0 == key)))
|
||||
Ok(Some(participants.iter().any(|participant| participant.0 == key)))
|
||||
}
|
||||
|
||||
async fn handle_new_set<D: Db, CNT: Clone + Fn(&mut D, TributarySpec)>(
|
||||
@@ -51,10 +53,13 @@ async fn handle_new_set<D: Db, CNT: Clone + Fn(&mut D, TributarySpec)>(
|
||||
block: &Block,
|
||||
set: ValidatorSet,
|
||||
) -> Result<(), SeraiError> {
|
||||
if in_set(key, serai, set).await?.expect("NewSet for set which doesn't exist") {
|
||||
if in_set(key, serai, set, block.hash()).await?.expect("NewSet for set which doesn't exist") {
|
||||
log::info!("present in set {:?}", set);
|
||||
|
||||
let set_data = serai.get_validator_set(set).await?.expect("NewSet for set which doesn't exist");
|
||||
let set_participants = serai
|
||||
.get_validator_set_participants(set.network, block.hash())
|
||||
.await?
|
||||
.expect("NewSet for set which doesn't exist");
|
||||
|
||||
let time = if let Ok(time) = block.time() {
|
||||
time
|
||||
@@ -77,7 +82,7 @@ async fn handle_new_set<D: Db, CNT: Clone + Fn(&mut D, TributarySpec)>(
|
||||
const SUBSTRATE_TO_TRIBUTARY_TIME_DELAY: u64 = 120;
|
||||
let time = time + SUBSTRATE_TO_TRIBUTARY_TIME_DELAY;
|
||||
|
||||
let spec = TributarySpec::new(block.hash(), time, set, set_data);
|
||||
let spec = TributarySpec::new(block.hash(), time, set, set_participants);
|
||||
create_new_tributary(db, spec.clone());
|
||||
} else {
|
||||
log::info!("not present in set {:?}", set);
|
||||
|
||||
@@ -15,8 +15,8 @@ use ciphersuite::{
|
||||
use sp_application_crypto::sr25519;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{NETWORKS, NetworkId, Amount},
|
||||
validator_sets::primitives::{Session, ValidatorSet, ValidatorSetData},
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
};
|
||||
|
||||
use tokio::time::sleep;
|
||||
@@ -52,20 +52,12 @@ pub fn new_spec<R: RngCore + CryptoRng>(
|
||||
|
||||
let set = ValidatorSet { session: Session(0), network: NetworkId::Bitcoin };
|
||||
|
||||
let set_data = ValidatorSetData {
|
||||
bond: Amount(100),
|
||||
network: NETWORKS[&NetworkId::Bitcoin].clone(),
|
||||
participants: keys
|
||||
.iter()
|
||||
.map(|key| {
|
||||
(sr25519::Public((<Ristretto as Ciphersuite>::generator() * **key).to_bytes()), Amount(100))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
};
|
||||
let set_participants = keys
|
||||
.iter()
|
||||
.map(|key| sr25519::Public((<Ristretto as Ciphersuite>::generator() * **key).to_bytes()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let res = TributarySpec::new(serai_block, start_time, set, set_data);
|
||||
let res = TributarySpec::new(serai_block, start_time, set, set_participants);
|
||||
assert_eq!(TributarySpec::read::<&[u8]>(&mut res.serialize().as_ref()).unwrap(), res);
|
||||
res
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ use frost::Participant;
|
||||
use scale::{Encode, Decode};
|
||||
|
||||
use serai_client::{
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet, ValidatorSetData},
|
||||
primitives::{NetworkId, PublicKey},
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -51,16 +51,16 @@ impl TributarySpec {
|
||||
serai_block: [u8; 32],
|
||||
start_time: u64,
|
||||
set: ValidatorSet,
|
||||
set_data: ValidatorSetData,
|
||||
set_participants: Vec<PublicKey>,
|
||||
) -> TributarySpec {
|
||||
let mut validators = vec![];
|
||||
for (participant, amount) in set_data.participants {
|
||||
for participant in set_participants {
|
||||
// TODO: Ban invalid keys from being validators on the Serai side
|
||||
// (make coordinator key a session key?)
|
||||
let participant = <Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut participant.0.as_ref())
|
||||
.expect("invalid key registered as participant");
|
||||
// Give one weight on Tributary per bond instance
|
||||
validators.push((participant, amount.0 / set_data.bond.0));
|
||||
// TODO: Give one weight on Tributary per bond instance
|
||||
validators.push((participant, 1));
|
||||
}
|
||||
|
||||
Self { serai_block, start_time, set, validators }
|
||||
|
||||
Reference in New Issue
Block a user