mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add key management to the scheduler
This commit is contained in:
@@ -1,12 +1,17 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use scale::Encode;
|
use scale::Encode;
|
||||||
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
use serai_db::{Get, DbTxn, create_db};
|
use serai_db::{Get, DbTxn, create_db};
|
||||||
|
|
||||||
use primitives::{EncodableG, Eventuality, EventualityTracker};
|
use primitives::{EncodableG, Eventuality, EventualityTracker};
|
||||||
|
|
||||||
use crate::{ScannerFeed, KeyFor, EventualityFor};
|
use crate::{ScannerFeed, KeyFor, EventualityFor};
|
||||||
|
|
||||||
|
// The DB macro doesn't support `BorshSerialize + BorshDeserialize` as a bound, hence this.
|
||||||
|
trait Borshy: BorshSerialize + BorshDeserialize {}
|
||||||
|
impl<T: BorshSerialize + BorshDeserialize> Borshy for T {}
|
||||||
|
|
||||||
create_db!(
|
create_db!(
|
||||||
ScannerEventuality {
|
ScannerEventuality {
|
||||||
// The next block to check for resolving eventualities
|
// The next block to check for resolving eventualities
|
||||||
@@ -15,6 +20,8 @@ create_db!(
|
|||||||
LatestHandledNotableBlock: () -> u64,
|
LatestHandledNotableBlock: () -> u64,
|
||||||
|
|
||||||
SerializedEventualities: <K: Encode>(key: K) -> Vec<u8>,
|
SerializedEventualities: <K: Encode>(key: K) -> Vec<u8>,
|
||||||
|
|
||||||
|
RetiredKey: <K: Borshy>(block_number: u64) -> K,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -51,7 +58,6 @@ impl<S: ScannerFeed> EventualityDb<S> {
|
|||||||
}
|
}
|
||||||
SerializedEventualities::set(txn, EncodableG(key), &serialized);
|
SerializedEventualities::set(txn, EncodableG(key), &serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eventualities(
|
pub(crate) fn eventualities(
|
||||||
getter: &impl Get,
|
getter: &impl Get,
|
||||||
key: KeyFor<S>,
|
key: KeyFor<S>,
|
||||||
@@ -66,4 +72,19 @@ impl<S: ScannerFeed> EventualityDb<S> {
|
|||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn retire_key(txn: &mut impl DbTxn, block_number: u64, key: KeyFor<S>) {
|
||||||
|
assert!(
|
||||||
|
RetiredKey::get::<EncodableG<KeyFor<S>>>(txn, block_number).is_none(),
|
||||||
|
"retiring multiple keys within the same block"
|
||||||
|
);
|
||||||
|
RetiredKey::set(txn, block_number, &EncodableG(key));
|
||||||
|
}
|
||||||
|
pub(crate) fn take_retired_key(txn: &mut impl DbTxn, block_number: u64) -> Option<KeyFor<S>> {
|
||||||
|
let res = RetiredKey::get::<EncodableG<KeyFor<S>>>(txn, block_number).map(|res| res.0);
|
||||||
|
if res.is_some() {
|
||||||
|
RetiredKey::del::<EncodableG<KeyFor<S>>>(txn, block_number);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,6 +248,11 @@ impl<D: Db, S: ScannerFeed, Sch: Scheduler<S>> ContinuallyRan for EventualityTas
|
|||||||
let mut outputs = received_external_outputs;
|
let mut outputs = received_external_outputs;
|
||||||
|
|
||||||
for key in &keys {
|
for key in &keys {
|
||||||
|
// If this is the key's activation block, activate it
|
||||||
|
if key.activation_block_number == b {
|
||||||
|
self.scheduler.activate_key(&mut txn, key.key);
|
||||||
|
}
|
||||||
|
|
||||||
let completed_eventualities = {
|
let completed_eventualities = {
|
||||||
let mut eventualities = EventualityDb::<S>::eventualities(&txn, key.key);
|
let mut eventualities = EventualityDb::<S>::eventualities(&txn, key.key);
|
||||||
let completed_eventualities = block.check_for_eventuality_resolutions(&mut eventualities);
|
let completed_eventualities = block.check_for_eventuality_resolutions(&mut eventualities);
|
||||||
@@ -349,11 +354,18 @@ impl<D: Db, S: ScannerFeed, Sch: Scheduler<S>> ContinuallyRan for EventualityTas
|
|||||||
|
|
||||||
// Retire this key `WINDOW_LENGTH` blocks in the future to ensure the scan task never
|
// Retire this key `WINDOW_LENGTH` blocks in the future to ensure the scan task never
|
||||||
// has a malleable view of the keys.
|
// has a malleable view of the keys.
|
||||||
ScannerGlobalDb::<S>::retire_key(&mut txn, b + S::WINDOW_LENGTH, key.key);
|
let retire_at = b + S::WINDOW_LENGTH;
|
||||||
|
ScannerGlobalDb::<S>::retire_key(&mut txn, retire_at, key.key);
|
||||||
|
EventualityDb::<S>::retire_key(&mut txn, retire_at, key.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we retired any key at this block, retire it within the scheduler
|
||||||
|
if let Some(key) = EventualityDb::<S>::take_retired_key(&mut txn, b) {
|
||||||
|
self.scheduler.retire_key(&mut txn, key);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the next-to-check block
|
// Update the next-to-check block
|
||||||
EventualityDb::<S>::set_next_to_check_for_eventualities_block(&mut txn, next_to_check);
|
EventualityDb::<S>::set_next_to_check_for_eventualities_block(&mut txn, next_to_check);
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,12 @@ pub trait ScannerFeed: 'static + Send + Sync + Clone {
|
|||||||
Ok(block)
|
Ok(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The dust threshold for the specified coin.
|
||||||
|
///
|
||||||
|
/// This MUST be constant. Serai MUST NOT create internal outputs worth less than this. This
|
||||||
|
/// SHOULD be a value worth handling at a human level.
|
||||||
|
fn dust(&self, coin: Coin) -> Amount;
|
||||||
|
|
||||||
/// The cost to aggregate an input as of the specified block.
|
/// The cost to aggregate an input as of the specified block.
|
||||||
///
|
///
|
||||||
/// This is defined as the transaction fee for a 2-input, 1-output transaction.
|
/// This is defined as the transaction fee for a 2-input, 1-output transaction.
|
||||||
@@ -145,12 +151,6 @@ pub trait ScannerFeed: 'static + Send + Sync + Clone {
|
|||||||
coin: Coin,
|
coin: Coin,
|
||||||
reference_block: &Self::Block,
|
reference_block: &Self::Block,
|
||||||
) -> Result<Amount, Self::EphemeralError>;
|
) -> Result<Amount, Self::EphemeralError>;
|
||||||
|
|
||||||
/// The dust threshold for the specified coin.
|
|
||||||
///
|
|
||||||
/// This MUST be constant. Serai MUST NOT create internal outputs worth less than this. This
|
|
||||||
/// SHOULD be a value worth handling at a human level.
|
|
||||||
fn dust(&self, coin: Coin) -> Amount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type KeyFor<S> = <<S as ScannerFeed>::Block as Block>::Key;
|
type KeyFor<S> = <<S as ScannerFeed>::Block as Block>::Key;
|
||||||
@@ -187,6 +187,27 @@ pub struct SchedulerUpdate<S: ScannerFeed> {
|
|||||||
|
|
||||||
/// The object responsible for accumulating outputs and planning new transactions.
|
/// The object responsible for accumulating outputs and planning new transactions.
|
||||||
pub trait Scheduler<S: ScannerFeed>: 'static + Send {
|
pub trait Scheduler<S: ScannerFeed>: 'static + Send {
|
||||||
|
/// Activate a key.
|
||||||
|
///
|
||||||
|
/// This SHOULD setup any necessary database structures. This SHOULD NOT cause the new key to
|
||||||
|
/// be used as the primary key. The multisig rotation time clearly establishes its steps.
|
||||||
|
fn activate_key(&mut self, txn: &mut impl DbTxn, key: KeyFor<S>);
|
||||||
|
|
||||||
|
/// Flush all outputs within a retiring key to the new key.
|
||||||
|
///
|
||||||
|
/// When a key is activated, the existing multisig should retain its outputs and utility for a
|
||||||
|
/// certain time period. With `flush_key`, all outputs should be directed towards fulfilling some
|
||||||
|
/// obligation or the `new_key`. Every output MUST be connected to an Eventuality. If a key no
|
||||||
|
/// longer has active Eventualities, it MUST be able to be retired.
|
||||||
|
// TODO: Call this
|
||||||
|
fn flush_key(&mut self, txn: &mut impl DbTxn, retiring_key: KeyFor<S>, new_key: KeyFor<S>);
|
||||||
|
|
||||||
|
/// Retire a key as it'll no longer be used.
|
||||||
|
///
|
||||||
|
/// Any key retired MUST NOT still have outputs associated with it. This SHOULD be a NOP other
|
||||||
|
/// than any assertions and database cleanup.
|
||||||
|
fn retire_key(&mut self, txn: &mut impl DbTxn, key: KeyFor<S>);
|
||||||
|
|
||||||
/// Accumulate outputs into the scheduler, yielding the Eventualities now to be scanned for.
|
/// Accumulate outputs into the scheduler, yielding the Eventualities now to be scanned for.
|
||||||
///
|
///
|
||||||
/// The `Vec<u8>` used as the key in the returned HashMap should be the encoded key the
|
/// The `Vec<u8>` used as the key in the returned HashMap should be the encoded key the
|
||||||
|
|||||||
Reference in New Issue
Block a user