mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Remove usage of serai from intake_cosign
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use core::future::Future;
|
||||
|
||||
use serai_client::{primitives::Amount, Serai};
|
||||
use serai_client::Serai;
|
||||
|
||||
use serai_db::*;
|
||||
use serai_task::ContinuallyRan;
|
||||
@@ -15,12 +15,12 @@ create_db!(
|
||||
// The latest cosigned block number.
|
||||
LatestCosignedBlockNumber: () -> u64,
|
||||
// The latest global session evaluated.
|
||||
// TODO: Also include the weights here
|
||||
LatestGlobalSessionEvaluated: () -> ([u8; 32], Vec<ValidatorSet>),
|
||||
}
|
||||
);
|
||||
|
||||
/// A task to determine if a block has been cosigned and we should handle it.
|
||||
// TODO: Remove `serai` from this
|
||||
pub(crate) struct CosignEvaluatorTask<D: Db, R: RequestNotableCosigns> {
|
||||
pub(crate) db: D,
|
||||
pub(crate) serai: Serai,
|
||||
@@ -38,7 +38,7 @@ async fn get_latest_global_session_evaluated(
|
||||
// This is the initial global session
|
||||
// Fetch the sets participating and declare it the latest value recognized
|
||||
let sets = cosigning_sets_by_parent_hash(serai, parent_hash).await?;
|
||||
let initial_global_session = GlobalSession::new(sets.clone()).id();
|
||||
let initial_global_session = GlobalSession::id(sets.clone());
|
||||
LatestGlobalSessionEvaluated::set(txn, &(initial_global_session, sets.clone()));
|
||||
(initial_global_session, sets)
|
||||
}
|
||||
@@ -73,39 +73,26 @@ impl<D: Db, R: RequestNotableCosigns> ContinuallyRan for CosignEvaluatorTask<D,
|
||||
get_latest_global_session_evaluated(&mut txn, &self.serai, parent_hash).await?;
|
||||
|
||||
let mut weight_cosigned = 0;
|
||||
let mut total_weight = 0;
|
||||
let (_, global_session_start_block) = GlobalSessions::get(&txn, global_session)
|
||||
.ok_or_else(|| {
|
||||
let global_session_info =
|
||||
GlobalSessions::get(&txn, global_session).ok_or_else(|| {
|
||||
"checking if intended cosign was satisfied within an unrecognized global session"
|
||||
.to_string()
|
||||
})?;
|
||||
for set in sets {
|
||||
// Fetch the weight for this set, as of the start of the global session
|
||||
// This simplifies the logic around which set of stakes to use when evaluating
|
||||
// cosigns, even if it's lossy as it isn't accurate to how stake may fluctuate within
|
||||
// a session
|
||||
let stake = self
|
||||
.serai
|
||||
.as_of(global_session_start_block)
|
||||
.validator_sets()
|
||||
.total_allocated_stake(set.network)
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
total_weight += stake;
|
||||
|
||||
// Check if we have the cosign from this set
|
||||
if NetworksLatestCosignedBlock::get(&txn, global_session, set.network)
|
||||
.map(|signed_cosign| signed_cosign.cosign.block_number) ==
|
||||
Some(block_number)
|
||||
{
|
||||
// Since have this cosign, add the set's weight to the weight which has cosigned
|
||||
weight_cosigned += stake;
|
||||
weight_cosigned +=
|
||||
global_session_info.stakes.get(&set.network).ok_or_else(|| {
|
||||
"ValidatorSet in global session yet didn't have its stake".to_string()
|
||||
})?;
|
||||
}
|
||||
}
|
||||
// Check if the sum weight doesn't cross the required threshold
|
||||
if weight_cosigned < (((total_weight * 83) / 100) + 1) {
|
||||
if weight_cosigned < (((global_session_info.total_stake * 83) / 100) + 1) {
|
||||
// Request the necessary cosigns over the network
|
||||
// TODO: Add a timer to ensure this isn't called too often
|
||||
self
|
||||
@@ -122,7 +109,8 @@ impl<D: Db, R: RequestNotableCosigns> ContinuallyRan for CosignEvaluatorTask<D,
|
||||
// Since this block changes the global session, update it
|
||||
{
|
||||
let sets = cosigning_sets(&self.serai.as_of(block_hash)).await?;
|
||||
let global_session = GlobalSession::new(sets.clone()).id();
|
||||
let sets = sets.into_iter().map(|(set, _key)| set).collect::<Vec<_>>();
|
||||
let global_session = GlobalSession::id(sets.clone());
|
||||
LatestGlobalSessionEvaluated::set(&mut txn, &(global_session, sets));
|
||||
}
|
||||
}
|
||||
@@ -149,28 +137,15 @@ impl<D: Db, R: RequestNotableCosigns> ContinuallyRan for CosignEvaluatorTask<D,
|
||||
// Get the global session for this block
|
||||
let (global_session, sets) =
|
||||
get_latest_global_session_evaluated(&mut txn, &self.serai, parent_hash).await?;
|
||||
let (_, global_session_start_block) = GlobalSessions::get(&txn, global_session)
|
||||
.ok_or_else(|| {
|
||||
let global_session_info =
|
||||
GlobalSessions::get(&txn, global_session).ok_or_else(|| {
|
||||
"checking if intended cosign was satisfied within an unrecognized global session"
|
||||
.to_string()
|
||||
})?;
|
||||
|
||||
let mut weight_cosigned = 0;
|
||||
let mut total_weight = 0;
|
||||
let mut lowest_common_block: Option<u64> = None;
|
||||
for set in sets {
|
||||
let stake = self
|
||||
.serai
|
||||
.as_of(global_session_start_block)
|
||||
.validator_sets()
|
||||
.total_allocated_stake(set.network)
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
// Increment total_weight with this set's stake
|
||||
total_weight += stake;
|
||||
|
||||
// Check if this set cosigned this block or not
|
||||
let Some(cosign) =
|
||||
NetworksLatestCosignedBlock::get(&txn, global_session, set.network)
|
||||
@@ -178,7 +153,10 @@ impl<D: Db, R: RequestNotableCosigns> ContinuallyRan for CosignEvaluatorTask<D,
|
||||
continue;
|
||||
};
|
||||
if cosign.cosign.block_number >= block_number {
|
||||
weight_cosigned += total_weight
|
||||
weight_cosigned +=
|
||||
global_session_info.stakes.get(&set.network).ok_or_else(|| {
|
||||
"ValidatorSet in global session yet didn't have its stake".to_string()
|
||||
})?;
|
||||
}
|
||||
|
||||
// Update the lowest block common to all of these cosigns
|
||||
@@ -188,7 +166,7 @@ impl<D: Db, R: RequestNotableCosigns> ContinuallyRan for CosignEvaluatorTask<D,
|
||||
}
|
||||
|
||||
// Check if the sum weight doesn't cross the required threshold
|
||||
if weight_cosigned < (((total_weight * 83) / 100) + 1) {
|
||||
if weight_cosigned < (((global_session_info.total_stake * 83) / 100) + 1) {
|
||||
// Request the superseding notable cosigns over the network
|
||||
// If this session hasn't yet produced notable cosigns, then we presume we'll see
|
||||
// the desired non-notable cosigns as part of normal operations, without needing to
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use core::future::Future;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serai_client::{Serai, validator_sets::primitives::ValidatorSet};
|
||||
use serai_client::{
|
||||
primitives::{SeraiAddress, Amount},
|
||||
validator_sets::primitives::ValidatorSet,
|
||||
Serai,
|
||||
};
|
||||
|
||||
use serai_db::*;
|
||||
use serai_task::ContinuallyRan;
|
||||
@@ -71,29 +76,71 @@ impl<D: Db> ContinuallyRan for CosignIntendTask<D> {
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?;
|
||||
|
||||
// Check we are indexing a linear chain
|
||||
if (block_number > 1) &&
|
||||
(<[u8; 32]>::from(block.header.parent_hash) !=
|
||||
SubstrateBlocks::get(&txn, block_number - 1)
|
||||
.expect("indexing a block but haven't indexed its parent"))
|
||||
{
|
||||
Err(format!(
|
||||
"node's block #{block_number} doesn't build upon the block #{} prior indexed",
|
||||
block_number - 1
|
||||
))?;
|
||||
}
|
||||
|
||||
SubstrateBlocks::set(&mut txn, block_number, &block.hash());
|
||||
|
||||
match has_events {
|
||||
HasEvents::Notable | HasEvents::NonNotable => {
|
||||
// TODO: Replace with LatestGlobalSessionIntended, GlobalSessions
|
||||
let sets = cosigning_sets_for_block(&self.serai, &block).await?;
|
||||
|
||||
// If this block doesn't have any cosigners, meaning it'll never be cosigned, we flag
|
||||
// it as not having any events requiring cosigning so we don't attempt to sign/require
|
||||
// a cosign for it
|
||||
if sets.is_empty() {
|
||||
has_events = HasEvents::No;
|
||||
}
|
||||
|
||||
// If this is notable, it creates a new global session, which we index into the
|
||||
// database now
|
||||
if has_events == HasEvents::Notable {
|
||||
let sets = cosigning_sets(&self.serai.as_of(block.hash())).await?;
|
||||
let global_session = GlobalSession::new(sets).id();
|
||||
GlobalSessions::set(&mut txn, global_session, &(block_number, block.hash()));
|
||||
let serai = self.serai.as_of(block.hash());
|
||||
let sets = cosigning_sets(&serai).await?;
|
||||
let global_session = GlobalSession::id(sets.iter().map(|(set, _key)| *set).collect());
|
||||
|
||||
let mut keys = HashMap::new();
|
||||
let mut stakes = HashMap::new();
|
||||
let mut total_stake = 0;
|
||||
for (set, key) in &sets {
|
||||
keys.insert(set.network, SeraiAddress::from(*key));
|
||||
let stake = serai
|
||||
.validator_sets()
|
||||
.total_allocated_stake(set.network)
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
stakes.insert(set.network, stake);
|
||||
total_stake += stake;
|
||||
}
|
||||
if total_stake == 0 {
|
||||
Err(format!("cosigning sets for block #{block_number} had 0 stake in total"))?;
|
||||
}
|
||||
|
||||
GlobalSessions::set(
|
||||
&mut txn,
|
||||
global_session,
|
||||
&(GlobalSession { start_block_number: block_number, keys, stakes, total_stake }),
|
||||
);
|
||||
if let Some(ending_global_session) = LatestGlobalSessionIntended::get(&txn) {
|
||||
GlobalSessionLastBlock::set(&mut txn, ending_global_session, &block_number);
|
||||
GlobalSessionsLastBlock::set(&mut txn, ending_global_session, &block_number);
|
||||
}
|
||||
LatestGlobalSessionIntended::set(&mut txn, &global_session);
|
||||
}
|
||||
|
||||
// If this block doesn't have any cosigners, meaning it'll never be cosigned, we flag it
|
||||
// as not having any events requiring cosigning so we don't attempt to sign/require a
|
||||
// cosign for it
|
||||
if sets.is_empty() {
|
||||
has_events = HasEvents::No;
|
||||
} else {
|
||||
let global_session = GlobalSession::new(sets.clone()).id();
|
||||
if has_events != HasEvents::No {
|
||||
let global_session = GlobalSession::id(sets.clone());
|
||||
// Tell each set of their expectation to cosign this block
|
||||
for set in sets {
|
||||
log::debug!("{:?} will be cosigning block #{block_number}", set);
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use core::{fmt::Debug, future::Future};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use blake2::{Digest, Blake2s256};
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{Amount, NetworkId, SeraiAddress},
|
||||
primitives::{NetworkId, SeraiAddress},
|
||||
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
|
||||
Block, Serai, TemporalSerai,
|
||||
Public, Block, Serai, TemporalSerai,
|
||||
};
|
||||
|
||||
use serai_db::*;
|
||||
@@ -45,29 +46,36 @@ pub const COSIGN_CONTEXT: &[u8] = b"serai-cosign";
|
||||
cosigning protocol.
|
||||
*/
|
||||
#[derive(Debug, BorshSerialize, BorshDeserialize)]
|
||||
struct GlobalSession {
|
||||
cosigners: Vec<ValidatorSet>,
|
||||
pub(crate) struct GlobalSession {
|
||||
pub(crate) start_block_number: u64,
|
||||
pub(crate) keys: HashMap<NetworkId, SeraiAddress>,
|
||||
pub(crate) stakes: HashMap<NetworkId, u64>,
|
||||
pub(crate) total_stake: u64,
|
||||
}
|
||||
impl GlobalSession {
|
||||
fn new(mut cosigners: Vec<ValidatorSet>) -> Self {
|
||||
fn id(mut cosigners: Vec<ValidatorSet>) -> [u8; 32] {
|
||||
cosigners.sort_by_key(|a| borsh::to_vec(a).unwrap());
|
||||
Self { cosigners }
|
||||
}
|
||||
fn id(&self) -> [u8; 32] {
|
||||
Blake2s256::digest(borsh::to_vec(self).unwrap()).into()
|
||||
Blake2s256::digest(borsh::to_vec(&cosigners).unwrap()).into()
|
||||
}
|
||||
}
|
||||
|
||||
create_db! {
|
||||
Cosign {
|
||||
// A mapping from a global session's ID to its start block (number, hash).
|
||||
GlobalSessions: (global_session: [u8; 32]) -> (u64, [u8; 32]),
|
||||
// The following are populated by the intend task and used throughout the library
|
||||
|
||||
// An index of Substrate blocks
|
||||
SubstrateBlocks: (block_number: u64) -> [u8; 32],
|
||||
// A mapping from a global session's ID to its relevant information.
|
||||
GlobalSessions: (global_session: [u8; 32]) -> GlobalSession,
|
||||
// The last block to be cosigned by a global session.
|
||||
GlobalSessionLastBlock: (global_session: [u8; 32]) -> u64,
|
||||
GlobalSessionsLastBlock: (global_session: [u8; 32]) -> u64,
|
||||
// The latest global session intended.
|
||||
//
|
||||
// This is distinct from the latest global session for which we've evaluated the cosigns for.
|
||||
LatestGlobalSessionIntended: () -> [u8; 32],
|
||||
|
||||
// The following are managed by the `intake_cosign` function present in this file
|
||||
|
||||
// The latest cosigned block for each network.
|
||||
//
|
||||
// This will only be populated with cosigns predating or during the most recent global session
|
||||
@@ -189,21 +197,22 @@ async fn keys_for_network(
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Fetch the `ValidatorSet`s used for cosigning as of this block.
|
||||
async fn cosigning_sets(serai: &TemporalSerai<'_>) -> Result<Vec<ValidatorSet>, String> {
|
||||
/// Fetch the `ValidatorSet`s, and their associated keys, used for cosigning as of this block.
|
||||
async fn cosigning_sets(serai: &TemporalSerai<'_>) -> Result<Vec<(ValidatorSet, Public)>, String> {
|
||||
let mut sets = Vec::with_capacity(serai_client::primitives::NETWORKS.len());
|
||||
for network in serai_client::primitives::NETWORKS {
|
||||
let Some((session, _)) = keys_for_network(serai, network).await? else {
|
||||
let Some((session, keys)) = keys_for_network(serai, network).await? else {
|
||||
// If this network doesn't have usable keys, move on
|
||||
continue;
|
||||
};
|
||||
|
||||
sets.push(ValidatorSet { network, session });
|
||||
sets.push((ValidatorSet { network, session }, keys.0));
|
||||
}
|
||||
Ok(sets)
|
||||
}
|
||||
|
||||
/// Fetch the `ValidatorSet`s used for cosigning a block by the block's parent hash.
|
||||
/// Fetch the `ValidatorSet`s, and their associated keys, used for cosigning a block by the block's
|
||||
/// parent hash.
|
||||
async fn cosigning_sets_by_parent_hash(
|
||||
serai: &Serai,
|
||||
parent_hash: [u8; 32],
|
||||
@@ -213,10 +222,11 @@ async fn cosigning_sets_by_parent_hash(
|
||||
update the sets declared but that update shouldn't take effect until block `n` is cosigned).
|
||||
That's why fetching the cosigning sets for a block by its parent hash is valid.
|
||||
*/
|
||||
cosigning_sets(&serai.as_of(parent_hash)).await
|
||||
let sets = cosigning_sets(&serai.as_of(parent_hash)).await?;
|
||||
Ok(sets.into_iter().map(|(set, _key)| set).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// Fetch the `ValidatorSet`s used for cosigning this block.
|
||||
/// Fetch the `ValidatorSet`s, and their associated keys, used for cosigning this block.
|
||||
async fn cosigning_sets_for_block(
|
||||
serai: &Serai,
|
||||
block: &Block,
|
||||
@@ -242,7 +252,6 @@ pub struct Faulted;
|
||||
/// The interface to manage cosigning with.
|
||||
pub struct Cosigning<D: Db> {
|
||||
db: D,
|
||||
serai: Serai,
|
||||
}
|
||||
impl<D: Db> Cosigning<D> {
|
||||
/// Spawn the tasks to intend and evaluate cosigns.
|
||||
@@ -262,10 +271,10 @@ impl<D: Db> Cosigning<D> {
|
||||
.continually_run(intend_task, vec![evaluator_task_handle]),
|
||||
);
|
||||
tokio::spawn(
|
||||
(evaluator::CosignEvaluatorTask { db: db.clone(), serai: serai.clone(), request })
|
||||
(evaluator::CosignEvaluatorTask { db: db.clone(), serai, request })
|
||||
.continually_run(evaluator_task, tasks_to_run_upon_cosigning),
|
||||
);
|
||||
Self { db, serai }
|
||||
Self { db }
|
||||
}
|
||||
|
||||
/// The latest cosigned block number.
|
||||
@@ -338,7 +347,7 @@ impl<D: Db> Cosigning<D> {
|
||||
//
|
||||
// Takes `&mut self` as this should only be called once at any given moment.
|
||||
// TODO: Don't overload bool here
|
||||
pub async fn intake_cosign(&mut self, signed_cosign: SignedCosign) -> Result<bool, String> {
|
||||
pub fn intake_cosign(&mut self, signed_cosign: &SignedCosign) -> Result<bool, String> {
|
||||
let cosign = &signed_cosign.cosign;
|
||||
|
||||
let Cosigner::ValidatorSet(network) = cosign.cosigner else {
|
||||
@@ -356,41 +365,37 @@ impl<D: Db> Cosigning<D> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check our finalized (and indexed by intend) blockchain exceeds this block number
|
||||
if cosign.block_number >= intend::ScanCosignFrom::get(&self.db).unwrap_or(0) {
|
||||
// Check our indexed blockchain includes a block with this block number
|
||||
let Some(our_block_hash) = SubstrateBlocks::get(&self.db, cosign.block_number) else {
|
||||
return Ok(true);
|
||||
}
|
||||
};
|
||||
|
||||
let Some((global_session_start_block_number, global_session_start_block_hash)) =
|
||||
GlobalSessions::get(&self.db, cosign.global_session)
|
||||
else {
|
||||
let Some(global_session) = GlobalSessions::get(&self.db, cosign.global_session) else {
|
||||
// Unrecognized global session
|
||||
return Ok(true);
|
||||
};
|
||||
if cosign.block_number <= global_session_start_block_number {
|
||||
if cosign.block_number <= global_session.start_block_number {
|
||||
// Cosign is for a block predating the global session
|
||||
return Ok(false);
|
||||
}
|
||||
if Some(cosign.block_number) > GlobalSessionLastBlock::get(&self.db, cosign.global_session) {
|
||||
// Cosign is for a block after the last block this global session should have signed
|
||||
return Ok(false);
|
||||
if let Some(last_block) = GlobalSessionsLastBlock::get(&self.db, cosign.global_session) {
|
||||
if cosign.block_number > last_block {
|
||||
// Cosign is for a block after the last block this global session should have signed
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the cosign's signature
|
||||
{
|
||||
let key = match cosign.cosigner {
|
||||
let key = Public::from(match cosign.cosigner {
|
||||
Cosigner::ValidatorSet(network) => {
|
||||
// TODO: Cache this
|
||||
let Some((_session, keys)) =
|
||||
keys_for_network(&self.serai.as_of(global_session_start_block_hash), network).await?
|
||||
else {
|
||||
let Some(key) = global_session.keys.get(&network) else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
keys.0
|
||||
*key
|
||||
}
|
||||
Cosigner::Validator(signer) => signer.into(),
|
||||
};
|
||||
Cosigner::Validator(signer) => signer,
|
||||
});
|
||||
|
||||
if !signed_cosign.verify_signature(key) {
|
||||
return Ok(false);
|
||||
@@ -402,20 +407,14 @@ impl<D: Db> Cosigning<D> {
|
||||
|
||||
let mut txn = self.db.txn();
|
||||
|
||||
let our_block_hash = self
|
||||
.serai
|
||||
.block_hash(cosign.block_number)
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
.ok_or_else(|| "requested hash of a finalized block yet received None".to_string())?;
|
||||
if our_block_hash == cosign.block_hash {
|
||||
// If this is for a future global session, we don't acknowledge this cosign at this time
|
||||
if global_session_start_block_number > LatestCosignedBlockNumber::get(&txn).unwrap_or(0) {
|
||||
if global_session.start_block_number > LatestCosignedBlockNumber::get(&txn).unwrap_or(0) {
|
||||
drop(txn);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
NetworksLatestCosignedBlock::set(&mut txn, cosign.global_session, network, &signed_cosign);
|
||||
NetworksLatestCosignedBlock::set(&mut txn, cosign.global_session, network, signed_cosign);
|
||||
} else {
|
||||
let mut faults = Faults::get(&txn, cosign.global_session).unwrap_or(vec![]);
|
||||
// Only handle this as a fault if this set wasn't prior faulty
|
||||
@@ -424,31 +423,19 @@ impl<D: Db> Cosigning<D> {
|
||||
Faults::set(&mut txn, cosign.global_session, &faults);
|
||||
|
||||
let mut weight_cosigned = 0;
|
||||
let mut total_weight = 0;
|
||||
for set in cosigning_sets(&self.serai.as_of(global_session_start_block_hash)).await? {
|
||||
let stake = self
|
||||
.serai
|
||||
.as_of(global_session_start_block_hash)
|
||||
.validator_sets()
|
||||
.total_allocated_stake(set.network)
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
// Increment total_weight with this set's stake
|
||||
total_weight += stake;
|
||||
|
||||
// Check if this set cosigned this block or not
|
||||
if faults
|
||||
.iter()
|
||||
.any(|cosign| cosign.cosign.cosigner == Cosigner::ValidatorSet(set.network))
|
||||
{
|
||||
weight_cosigned += total_weight
|
||||
}
|
||||
for fault in &faults {
|
||||
let Cosigner::ValidatorSet(network) = fault.cosign.cosigner else {
|
||||
// TODO when we implement the non-ValidatorSet cosigner protocol
|
||||
Err("non-ValidatorSet cosigner had a fault".to_string())?
|
||||
};
|
||||
let Some(stake) = global_session.stakes.get(&network) else {
|
||||
Err("cosigner with recognized key didn't have a stake entry saved".to_string())?
|
||||
};
|
||||
weight_cosigned += stake;
|
||||
}
|
||||
|
||||
// Check if the sum weight means a fault has occurred
|
||||
if weight_cosigned >= ((total_weight * 17) / 100) {
|
||||
if weight_cosigned >= ((global_session.total_stake * 17) / 100) {
|
||||
FaultedSession::set(&mut txn, &cosign.global_session);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user