mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-10 13:09:24 +00:00
It makes sense for networks which support arbitrary data to do as part of their address. This reduces the ability to perform DoSs, achieves better performance, and better uses the type system (as now networks we don't support data on don't have a data field). Updates the Ethereum address definition in serai-client accordingly
151 lines
4.5 KiB
Rust
151 lines
4.5 KiB
Rust
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
#![doc = include_str!("../README.md")]
|
|
#![deny(missing_docs)]
|
|
|
|
use core::{marker::PhantomData, future::Future};
|
|
use std::collections::HashMap;
|
|
|
|
use group::GroupEncoding;
|
|
|
|
use serai_db::{Get, DbTxn, create_db};
|
|
|
|
use primitives::{ReceivedOutput, Payment};
|
|
use scanner::{
|
|
LifetimeStage, ScannerFeed, KeyFor, AddressFor, EventualityFor, BlockFor, SchedulerUpdate,
|
|
KeyScopedEventualities, Scheduler as SchedulerTrait,
|
|
};
|
|
use scheduler_primitives::*;
|
|
|
|
create_db! {
|
|
SmartContractScheduler {
|
|
NextNonce: () -> u64,
|
|
}
|
|
}
|
|
|
|
/// A smart contract.
|
|
pub trait SmartContract<S: ScannerFeed>: 'static + Send {
|
|
/// The type representing a signable transaction.
|
|
type SignableTransaction: SignableTransaction;
|
|
|
|
/// Rotate from the retiring key to the new key.
|
|
fn rotate(
|
|
&self,
|
|
nonce: u64,
|
|
retiring_key: KeyFor<S>,
|
|
new_key: KeyFor<S>,
|
|
) -> (Self::SignableTransaction, EventualityFor<S>);
|
|
|
|
/// Fulfill the set of payments, dropping any not worth handling.
|
|
fn fulfill(
|
|
&self,
|
|
starting_nonce: u64,
|
|
key: KeyFor<S>,
|
|
payments: Vec<Payment<AddressFor<S>>>,
|
|
) -> Vec<(Self::SignableTransaction, EventualityFor<S>)>;
|
|
}
|
|
|
|
/// A scheduler for a smart contract representing the Serai processor.
|
|
#[allow(non_snake_case)]
|
|
#[derive(Clone)]
|
|
pub struct Scheduler<S: ScannerFeed, SC: Send + Sync + SmartContract<S>> {
|
|
smart_contract: SC,
|
|
_S: PhantomData<S>,
|
|
}
|
|
|
|
impl<S: ScannerFeed, SC: Send + Sync + SmartContract<S>> Scheduler<S, SC> {
|
|
/// Create a new scheduler.
|
|
pub fn new(smart_contract: SC) -> Self {
|
|
Self { smart_contract, _S: PhantomData }
|
|
}
|
|
|
|
fn fulfill_payments(
|
|
&self,
|
|
txn: &mut impl DbTxn,
|
|
active_keys: &[(KeyFor<S>, LifetimeStage)],
|
|
payments: Vec<Payment<AddressFor<S>>>,
|
|
) -> KeyScopedEventualities<S> {
|
|
let key = match active_keys[0].1 {
|
|
LifetimeStage::ActiveYetNotReporting |
|
|
LifetimeStage::Active |
|
|
LifetimeStage::UsingNewForChange => active_keys[0].0,
|
|
LifetimeStage::Forwarding | LifetimeStage::Finishing => active_keys[1].0,
|
|
};
|
|
|
|
let mut nonce = NextNonce::get(txn).unwrap_or(0);
|
|
let mut eventualities = Vec::with_capacity(1);
|
|
for (signable, eventuality) in self.smart_contract.fulfill(nonce, key, payments) {
|
|
TransactionsToSign::<SC::SignableTransaction>::send(txn, &key, &signable);
|
|
nonce += 1;
|
|
eventualities.push(eventuality);
|
|
}
|
|
NextNonce::set(txn, &nonce);
|
|
HashMap::from([(key.to_bytes().as_ref().to_vec(), eventualities)])
|
|
}
|
|
}
|
|
|
|
impl<S: ScannerFeed, SC: Send + Sync + SmartContract<S>> SchedulerTrait<S> for Scheduler<S, SC> {
|
|
type EphemeralError = ();
|
|
type SignableTransaction = SC::SignableTransaction;
|
|
|
|
fn activate_key(_txn: &mut impl DbTxn, _key: KeyFor<S>) {}
|
|
|
|
fn flush_key(
|
|
&self,
|
|
txn: &mut impl DbTxn,
|
|
_block: &BlockFor<S>,
|
|
retiring_key: KeyFor<S>,
|
|
new_key: KeyFor<S>,
|
|
) -> impl Send + Future<Output = Result<KeyScopedEventualities<S>, Self::EphemeralError>> {
|
|
async move {
|
|
let nonce = NextNonce::get(txn).unwrap_or(0);
|
|
let (signable, eventuality) = self.smart_contract.rotate(nonce, retiring_key, new_key);
|
|
NextNonce::set(txn, &(nonce + 1));
|
|
TransactionsToSign::<SC::SignableTransaction>::send(txn, &retiring_key, &signable);
|
|
Ok(HashMap::from([(retiring_key.to_bytes().as_ref().to_vec(), vec![eventuality])]))
|
|
}
|
|
}
|
|
|
|
fn retire_key(_txn: &mut impl DbTxn, _key: KeyFor<S>) {}
|
|
|
|
fn update(
|
|
&self,
|
|
txn: &mut impl DbTxn,
|
|
_block: &BlockFor<S>,
|
|
active_keys: &[(KeyFor<S>, LifetimeStage)],
|
|
update: SchedulerUpdate<S>,
|
|
) -> impl Send + Future<Output = Result<KeyScopedEventualities<S>, Self::EphemeralError>> {
|
|
async move {
|
|
// We ignore the outputs as we don't need to know our current state as it never suffers
|
|
// partial availability
|
|
|
|
// We shouldn't have any forwards though
|
|
assert!(update.forwards().is_empty());
|
|
|
|
// Create the transactions for the returns
|
|
Ok(
|
|
self.fulfill_payments(
|
|
txn,
|
|
active_keys,
|
|
update
|
|
.returns()
|
|
.iter()
|
|
.map(|to_return| {
|
|
Payment::new(to_return.address().clone(), to_return.output().balance())
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
fn fulfill(
|
|
&self,
|
|
txn: &mut impl DbTxn,
|
|
_block: &BlockFor<S>,
|
|
active_keys: &[(KeyFor<S>, LifetimeStage)],
|
|
payments: Vec<Payment<AddressFor<S>>>,
|
|
) -> impl Send + Future<Output = Result<KeyScopedEventualities<S>, Self::EphemeralError>> {
|
|
async move { Ok(self.fulfill_payments(txn, active_keys, payments)) }
|
|
}
|
|
}
|