mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add a proper error for intake_cosign
This commit is contained in:
@@ -128,7 +128,6 @@ create_db! {
|
|||||||
|
|
||||||
// An index of Substrate blocks
|
// An index of Substrate blocks
|
||||||
SubstrateBlockHash: (block_number: u64) -> [u8; 32],
|
SubstrateBlockHash: (block_number: u64) -> [u8; 32],
|
||||||
SubstrateBlockNumber: (block_hash: [u8; 32]) -> u64,
|
|
||||||
// A mapping from a global session's ID to its relevant information.
|
// A mapping from a global session's ID to its relevant information.
|
||||||
GlobalSessions: (global_session: [u8; 32]) -> GlobalSession,
|
GlobalSessions: (global_session: [u8; 32]) -> GlobalSession,
|
||||||
// The last block to be cosigned by a global session.
|
// The last block to be cosigned by a global session.
|
||||||
@@ -229,6 +228,43 @@ pub trait RequestNotableCosigns: 'static + Send {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Faulted;
|
pub struct Faulted;
|
||||||
|
|
||||||
|
/// An error incurred while intaking a cosign.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum IntakeCosignError {
|
||||||
|
/// Cosign is for a not-yet-indexed block
|
||||||
|
NotYetIndexedBlock,
|
||||||
|
/// A later cosign for this cosigner has already been handled
|
||||||
|
StaleCosign,
|
||||||
|
/// The cosign's global session isn't recognized
|
||||||
|
UnrecognizedGlobalSession,
|
||||||
|
/// The cosign is for a block before its global session starts
|
||||||
|
BeforeGlobalSessionStart,
|
||||||
|
/// The cosign is for a block after its global session ends
|
||||||
|
AfterGlobalSessionEnd,
|
||||||
|
/// The cosign's signing network wasn't a participant in this global session
|
||||||
|
NonParticipatingNetwork,
|
||||||
|
/// The cosign had an invalid signature
|
||||||
|
InvalidSignature,
|
||||||
|
/// The cosign is for a global session which has yet to have its declaration block cosigned
|
||||||
|
FutureGlobalSession,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntakeCosignError {
|
||||||
|
/// If this error is temporal to the local view
|
||||||
|
pub fn temporal(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
IntakeCosignError::NotYetIndexedBlock |
|
||||||
|
IntakeCosignError::StaleCosign |
|
||||||
|
IntakeCosignError::UnrecognizedGlobalSession |
|
||||||
|
IntakeCosignError::FutureGlobalSession => true,
|
||||||
|
IntakeCosignError::BeforeGlobalSessionStart |
|
||||||
|
IntakeCosignError::AfterGlobalSessionEnd |
|
||||||
|
IntakeCosignError::NonParticipatingNetwork |
|
||||||
|
IntakeCosignError::InvalidSignature => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The interface to manage cosigning with.
|
/// The interface to manage cosigning with.
|
||||||
pub struct Cosigning<D: Db> {
|
pub struct Cosigning<D: Db> {
|
||||||
db: D,
|
db: D,
|
||||||
@@ -282,13 +318,6 @@ impl<D: Db> Cosigning<D> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a finalized block's number by its hash.
|
|
||||||
///
|
|
||||||
/// This block is not guaranteed to be cosigned.
|
|
||||||
pub fn finalized_block_number(getter: &impl Get, block_hash: [u8; 32]) -> Option<u64> {
|
|
||||||
SubstrateBlockNumber::get(getter, block_hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch the notable cosigns for a global session in order to respond to requests.
|
/// Fetch the notable cosigns for a global session in order to respond to requests.
|
||||||
///
|
///
|
||||||
/// If this global session hasn't produced any notable cosigns, this will return the latest
|
/// If this global session hasn't produced any notable cosigns, this will return the latest
|
||||||
@@ -335,25 +364,15 @@ impl<D: Db> Cosigning<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Intake a cosign.
|
/// Intake a cosign.
|
||||||
///
|
|
||||||
/// - Returns Err(_) if there was an error trying to validate the cosign.
|
|
||||||
/// - Returns Ok(true) if the cosign was successfully handled or could not be handled at this
|
|
||||||
/// time.
|
|
||||||
/// - Returns Ok(false) if the cosign was invalid.
|
|
||||||
//
|
|
||||||
// We collapse a cosign which shouldn't be handled yet into a valid cosign (`Ok(true)`) as we
|
|
||||||
// assume we'll either explicitly request it if we need it or we'll naturally see it (or a later,
|
|
||||||
// more relevant, cosign) again.
|
|
||||||
//
|
//
|
||||||
// Takes `&mut self` as this should only be called once at any given moment.
|
// Takes `&mut self` as this should only be called once at any given moment.
|
||||||
// TODO: Don't overload bool here
|
pub fn intake_cosign(&mut self, signed_cosign: &SignedCosign) -> Result<(), IntakeCosignError> {
|
||||||
pub fn intake_cosign(&mut self, signed_cosign: &SignedCosign) -> Result<bool, String> {
|
|
||||||
let cosign = &signed_cosign.cosign;
|
let cosign = &signed_cosign.cosign;
|
||||||
let network = cosign.cosigner;
|
let network = cosign.cosigner;
|
||||||
|
|
||||||
// Check our indexed blockchain includes a block with this block number
|
// Check our indexed blockchain includes a block with this block number
|
||||||
let Some(our_block_hash) = SubstrateBlockHash::get(&self.db, cosign.block_number) else {
|
let Some(our_block_hash) = SubstrateBlockHash::get(&self.db, cosign.block_number) else {
|
||||||
return Ok(true);
|
Err(IntakeCosignError::NotYetIndexedBlock)?
|
||||||
};
|
};
|
||||||
let faulty = cosign.block_hash != our_block_hash;
|
let faulty = cosign.block_hash != our_block_hash;
|
||||||
|
|
||||||
@@ -363,20 +382,19 @@ impl<D: Db> Cosigning<D> {
|
|||||||
NetworksLatestCosignedBlock::get(&self.db, cosign.global_session, network)
|
NetworksLatestCosignedBlock::get(&self.db, cosign.global_session, network)
|
||||||
{
|
{
|
||||||
if existing.cosign.block_number >= cosign.block_number {
|
if existing.cosign.block_number >= cosign.block_number {
|
||||||
return Ok(true);
|
Err(IntakeCosignError::StaleCosign)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(global_session) = GlobalSessions::get(&self.db, cosign.global_session) else {
|
let Some(global_session) = GlobalSessions::get(&self.db, cosign.global_session) else {
|
||||||
// Unrecognized global session
|
Err(IntakeCosignError::UnrecognizedGlobalSession)?
|
||||||
return Ok(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check the cosigned block number is in range to the global session
|
// Check the cosigned block number is in range to the global session
|
||||||
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
|
// Cosign is for a block predating the global session
|
||||||
return Ok(false);
|
Err(IntakeCosignError::BeforeGlobalSessionStart)?;
|
||||||
}
|
}
|
||||||
if !faulty {
|
if !faulty {
|
||||||
// This prevents a malicious validator set, on the same chain, from producing a cosign after
|
// This prevents a malicious validator set, on the same chain, from producing a cosign after
|
||||||
@@ -384,7 +402,7 @@ impl<D: Db> Cosigning<D> {
|
|||||||
if let Some(last_block) = GlobalSessionsLastBlock::get(&self.db, cosign.global_session) {
|
if let Some(last_block) = GlobalSessionsLastBlock::get(&self.db, cosign.global_session) {
|
||||||
if cosign.block_number > last_block {
|
if cosign.block_number > last_block {
|
||||||
// Cosign is for a block after the last block this global session should have signed
|
// Cosign is for a block after the last block this global session should have signed
|
||||||
return Ok(false);
|
Err(IntakeCosignError::AfterGlobalSessionEnd)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,13 +411,13 @@ impl<D: Db> Cosigning<D> {
|
|||||||
{
|
{
|
||||||
let key = Public::from({
|
let key = Public::from({
|
||||||
let Some(key) = global_session.keys.get(&network) else {
|
let Some(key) = global_session.keys.get(&network) else {
|
||||||
return Ok(false);
|
Err(IntakeCosignError::NonParticipatingNetwork)?
|
||||||
};
|
};
|
||||||
*key
|
*key
|
||||||
});
|
});
|
||||||
|
|
||||||
if !signed_cosign.verify_signature(key) {
|
if !signed_cosign.verify_signature(key) {
|
||||||
return Ok(false);
|
Err(IntakeCosignError::InvalidSignature)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +433,7 @@ impl<D: Db> Cosigning<D> {
|
|||||||
// block declaring it was cosigned
|
// block declaring it was cosigned
|
||||||
if (global_session.start_block_number - 1) > latest_cosigned_block_number {
|
if (global_session.start_block_number - 1) > latest_cosigned_block_number {
|
||||||
drop(txn);
|
drop(txn);
|
||||||
return Ok(true);
|
return Err(IntakeCosignError::FutureGlobalSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is safe as it's in-range and newer, as prior checked since it isn't faulty
|
// This is safe as it's in-range and newer, as prior checked since it isn't faulty
|
||||||
@@ -429,9 +447,10 @@ impl<D: Db> Cosigning<D> {
|
|||||||
|
|
||||||
let mut weight_cosigned = 0;
|
let mut weight_cosigned = 0;
|
||||||
for fault in &faults {
|
for fault in &faults {
|
||||||
let Some(stake) = global_session.stakes.get(&fault.cosign.cosigner) else {
|
let stake = global_session
|
||||||
Err("cosigner with recognized key didn't have a stake entry saved".to_string())?
|
.stakes
|
||||||
};
|
.get(&fault.cosign.cosigner)
|
||||||
|
.expect("cosigner with recognized key didn't have a stake entry saved");
|
||||||
weight_cosigned += stake;
|
weight_cosigned += stake;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +462,7 @@ impl<D: Db> Cosigning<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
Ok(true)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive intended cosigns to produce for this ValidatorSet.
|
/// Receive intended cosigns to produce for this ValidatorSet.
|
||||||
|
|||||||
Reference in New Issue
Block a user