Add sorts for safety even upon non-determinism

This commit is contained in:
Luke Parker 2024-08-27 02:21:22 -04:00
parent 75251f04b4
commit 9cebdf7c68
3 changed files with 23 additions and 20 deletions

View file

@ -8,7 +8,7 @@ use primitives::{task::ContinuallyRan, OutputType, ReceivedOutput, Eventuality,
use crate::{ use crate::{
lifetime::LifetimeStage, lifetime::LifetimeStage,
db::{OutputWithInInstruction, ReceiverScanData, ScannerDb, ScanToEventualityDb}, db::{OutputWithInInstruction, ReceiverScanData, ScannerDb, ScanToEventualityDb},
BlockExt, ScannerFeed, KeyFor, SchedulerUpdate, Scheduler, BlockExt, ScannerFeed, KeyFor, SchedulerUpdate, Scheduler, sort_outputs,
}; };
mod db; mod db;
@ -214,8 +214,11 @@ impl<D: Db, S: ScannerFeed, Sch: Scheduler<S>> ContinuallyRan for EventualityTas
} }
// TODO: This also has to intake Burns // TODO: This also has to intake Burns
let new_eventualities = let mut scheduler_update = SchedulerUpdate { outputs, forwards, returns };
self.scheduler.update(&mut txn, SchedulerUpdate { outputs, forwards, returns }); scheduler_update.outputs.sort_by(sort_outputs);
scheduler_update.forwards.sort_by(sort_outputs);
scheduler_update.returns.sort_by(|a, b| sort_outputs(&a.output, &b.output));
let new_eventualities = self.scheduler.update(&mut txn, scheduler_update);
for (key, new_eventualities) in new_eventualities { for (key, new_eventualities) in new_eventualities {
let key = { let key = {
let mut key_repr = <KeyFor<S> as GroupEncoding>::Repr::default(); let mut key_repr = <KeyFor<S> as GroupEncoding>::Repr::default();

View file

@ -1,11 +1,13 @@
use core::{marker::PhantomData, fmt::Debug}; use core::{marker::PhantomData, fmt::Debug};
use std::collections::HashMap; use std::collections::HashMap;
use group::GroupEncoding;
use serai_db::{Get, DbTxn}; use serai_db::{Get, DbTxn};
use serai_primitives::{NetworkId, Coin, Amount}; use serai_primitives::{NetworkId, Coin, Amount};
use primitives::{task::*, ReceivedOutput, Block}; use primitives::{task::*, Address, ReceivedOutput, Block};
// Logic for deciding where in its lifetime a multisig is. // Logic for deciding where in its lifetime a multisig is.
mod lifetime; mod lifetime;
@ -21,6 +23,16 @@ mod eventuality;
/// Task which reports `Batch`s to Substrate. /// Task which reports `Batch`s to Substrate.
mod report; mod report;
pub(crate) fn sort_outputs<K: GroupEncoding, A: Address, O: ReceivedOutput<K, A>>(
a: &O,
b: &O,
) -> core::cmp::Ordering {
use core::cmp::{Ordering, Ord};
let res = a.id().as_ref().cmp(b.id().as_ref());
assert!(res != Ordering::Equal, "two outputs within a collection had the same ID");
res
}
/// Extension traits around Block. /// Extension traits around Block.
pub(crate) trait BlockExt: Block { pub(crate) trait BlockExt: Block {
fn scan_for_outputs(&self, key: Self::Key) -> Vec<Self::Output>; fn scan_for_outputs(&self, key: Self::Key) -> Vec<Self::Output>;
@ -28,12 +40,7 @@ pub(crate) trait BlockExt: Block {
impl<B: Block> BlockExt for B { impl<B: Block> BlockExt for B {
fn scan_for_outputs(&self, key: Self::Key) -> Vec<Self::Output> { fn scan_for_outputs(&self, key: Self::Key) -> Vec<Self::Output> {
let mut outputs = self.scan_for_outputs_unordered(key); let mut outputs = self.scan_for_outputs_unordered(key);
outputs.sort_by(|a, b| { outputs.sort_by(sort_outputs);
use core::cmp::{Ordering, Ord};
let res = a.id().as_ref().cmp(b.id().as_ref());
assert!(res != Ordering::Equal, "scanned two outputs within a block with the same ID");
res
});
outputs outputs
} }
} }

View file

@ -6,13 +6,13 @@ use serai_in_instructions_primitives::{
Shorthand, RefundableInInstruction, InInstruction, InInstructionWithBalance, Shorthand, RefundableInInstruction, InInstruction, InInstructionWithBalance,
}; };
use primitives::{OutputType, ReceivedOutput, Block}; use primitives::{task::ContinuallyRan, OutputType, ReceivedOutput, Block};
// TODO: Localize to ScanDb? // TODO: Localize to ScanDb?
use crate::{ use crate::{
lifetime::LifetimeStage, lifetime::LifetimeStage,
db::{OutputWithInInstruction, SenderScanData, ScannerDb, ScanToReportDb, ScanToEventualityDb}, db::{OutputWithInInstruction, SenderScanData, ScannerDb, ScanToReportDb, ScanToEventualityDb},
BlockExt, ScannerFeed, AddressFor, OutputFor, Return, ContinuallyRan, BlockExt, ScannerFeed, AddressFor, OutputFor, Return, sort_outputs,
}; };
// Construct an InInstruction from an external output. // Construct an InInstruction from an external output.
@ -96,15 +96,8 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ScanForOutputsTask<D, S> {
let queued_outputs = { let queued_outputs = {
let mut queued_outputs = ScannerDb::<S>::take_queued_outputs(&mut txn, b); let mut queued_outputs = ScannerDb::<S>::take_queued_outputs(&mut txn, b);
// Sort the queued outputs in case they weren't queued in a deterministic fashion // Sort the queued outputs in case they weren't queued in a deterministic fashion
queued_outputs.sort_by(|a, b| { queued_outputs.sort_by(|a, b| sort_outputs(&a.output, &b.output));
use core::cmp::{Ordering, Ord};
let res = a.output.id().as_ref().cmp(b.output.id().as_ref());
assert!(res != Ordering::Equal);
res
});
queued_outputs queued_outputs
}; };
for queued_output in queued_outputs { for queued_output in queued_outputs {