diff --git a/processor/frost-attempt-manager/src/lib.rs b/processor/frost-attempt-manager/src/lib.rs index cd8452fa..c4d1708d 100644 --- a/processor/frost-attempt-manager/src/lib.rs +++ b/processor/frost-attempt-manager/src/lib.rs @@ -32,6 +32,8 @@ pub struct AttemptManager { impl AttemptManager { /// Create a new attempt manager. + /// + /// This will not restore any signing sessions from the database. Those must be re-registered. pub fn new(db: D, session: Session, start_i: Participant) -> Self { AttemptManager { db, session, start_i, active: HashMap::new() } } @@ -52,7 +54,7 @@ impl AttemptManager { /// This frees all memory used for it and means no further messages will be handled for it. /// This does not stop the protocol from being re-registered and further worked on (with /// undefined behavior) then. The higher-level context must never call `register` again with this - /// ID. + /// ID accordingly. pub fn retire(&mut self, id: [u8; 32]) { if self.active.remove(&id).is_none() { log::info!("retiring protocol {}, which we didn't register/already retired", hex::encode(id)); diff --git a/processor/primitives/src/eventuality.rs b/processor/primitives/src/eventuality.rs index 6a52194d..80337824 100644 --- a/processor/primitives/src/eventuality.rs +++ b/processor/primitives/src/eventuality.rs @@ -7,6 +7,11 @@ pub trait Eventuality: Sized + Send + Sync { /// The type used to identify a received output. type OutputId: Id; + /// The ID of the transaction this Eventuality is for. + /// + /// This is an internal ID arbitrarily definable so long as it's unique. + fn id(&self) -> [u8; 32]; + /// A unique byte sequence which can be used to identify potentially resolving transactions. /// /// Both a transaction and an Eventuality are expected to be able to yield lookup sequences. diff --git a/processor/scanner/Cargo.toml b/processor/scanner/Cargo.toml index a3e6a9ba..2a3e7e0a 100644 --- a/processor/scanner/Cargo.toml +++ b/processor/scanner/Cargo.toml @@ -39,3 +39,4 @@ serai-in-instructions-primitives = { path = "../../substrate/in-instructions/pri serai-coins-primitives = { path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] } primitives = { package = "serai-processor-primitives", path = "../primitives" } +scheduler-primitives = { package = "serai-processor-scheduler-primitives", path = "../scheduler/primitives" } diff --git a/processor/scanner/src/lib.rs b/processor/scanner/src/lib.rs index 17feefbe..5573e484 100644 --- a/processor/scanner/src/lib.rs +++ b/processor/scanner/src/lib.rs @@ -247,6 +247,9 @@ impl SchedulerUpdate { /// The object responsible for accumulating outputs and planning new transactions. pub trait Scheduler: 'static + Send { + /// The type for a signable transaction. + type SignableTransaction: scheduler_primitives::SignableTransaction; + /// Activate a key. /// /// This SHOULD setup any necessary database structures. This SHOULD NOT cause the new key to diff --git a/processor/scheduler/primitives/src/lib.rs b/processor/scheduler/primitives/src/lib.rs index 97a00c03..b3bf525c 100644 --- a/processor/scheduler/primitives/src/lib.rs +++ b/processor/scheduler/primitives/src/lib.rs @@ -10,11 +10,26 @@ use group::GroupEncoding; use serai_db::DbTxn; /// A signable transaction. -pub trait SignableTransaction: 'static + Sized + Send + Sync { +pub trait SignableTransaction: 'static + Sized + Send + Sync + Clone { + /// The ciphersuite used to sign this transaction. + type Ciphersuite: Cuphersuite; + /// The preprocess machine for the signing protocol for this transaction. + type PreprocessMachine: PreprocessMachine; + /// Read a `SignableTransaction`. fn read(reader: &mut impl io::Read) -> io::Result; /// Write a `SignableTransaction`. fn write(&self, writer: &mut impl io::Write) -> io::Result<()>; + + /// The ID for this transaction. + /// + /// This is an internal ID arbitrarily definable so long as it's unique. + /// + /// This same ID MUST be returned by the Eventuality for this transaction. + fn id(&self) -> [u8; 32]; + + /// Sign this transaction. + fn sign(self, keys: ThresholdKeys) -> Self::PreprocessMachine; } mod db { diff --git a/processor/scheduler/utxo/standard/src/lib.rs b/processor/scheduler/utxo/standard/src/lib.rs index f69ca54b..10e40f15 100644 --- a/processor/scheduler/utxo/standard/src/lib.rs +++ b/processor/scheduler/utxo/standard/src/lib.rs @@ -309,6 +309,8 @@ impl> Scheduler { } impl> SchedulerTrait for Scheduler { + type SignableTransaction = P::SignableTransaction; + fn activate_key(txn: &mut impl DbTxn, key: KeyFor) { for coin in S::NETWORK.coins() { assert!(Db::::outputs(txn, key, *coin).is_none()); diff --git a/processor/scheduler/utxo/transaction-chaining/src/lib.rs b/processor/scheduler/utxo/transaction-chaining/src/lib.rs index 9a4ed2eb..d11e4ac2 100644 --- a/processor/scheduler/utxo/transaction-chaining/src/lib.rs +++ b/processor/scheduler/utxo/transaction-chaining/src/lib.rs @@ -368,6 +368,8 @@ impl>> Sched impl>> SchedulerTrait for Scheduler { + type SignableTransaction = P::SignableTransaction; + fn activate_key(txn: &mut impl DbTxn, key: KeyFor) { for coin in S::NETWORK.coins() { assert!(Db::::outputs(txn, key, *coin).is_none()); diff --git a/processor/signers/Cargo.toml b/processor/signers/Cargo.toml index 70248960..007c814c 100644 --- a/processor/signers/Cargo.toml +++ b/processor/signers/Cargo.toml @@ -20,3 +20,13 @@ ignored = ["borsh", "scale"] workspace = true [dependencies] +group = { version = "0.13", default-features = false } + +log = { version = "0.4", default-features = false, features = ["std"] } +tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "sync", "time", "macros"] } + +primitives = { package = "serai-processor-primitives", path = "../primitives" } +scanner = { package = "serai-processor-scanner", path = "../scanner" } +scheduler = { package = "serai-scheduler-primitives", path = "../scheduler/primitives" } + +frost-attempt-manager = { package = "serai-processor-frost-attempt-manager", path = "../frost-attempt-manager" } diff --git a/processor/signers/src/cosigner.rs b/processor/signers/src/cosigner.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/processor/signers/src/lib.rs b/processor/signers/src/lib.rs index e69de29b..c221ca4c 100644 --- a/processor/signers/src/lib.rs +++ b/processor/signers/src/lib.rs @@ -0,0 +1,56 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] + +mod transaction; + +/* +// The signers used by a Processor, key-scoped. +struct KeySigners { + transaction: AttemptManager, + substrate: AttemptManager>, + cosigner: AttemptManager>, +} + +/// The signers used by a protocol. +pub struct Signers(HashMap, KeySigners>); + +impl Signers { + /// Create a new set of signers. + pub fn new(db: D) -> Self { + // TODO: Load the registered keys + // TODO: Load the transactions being signed + // TODO: Load the batches being signed + todo!("TODO") + } + + /// Register a transaction to sign. + pub fn sign_transaction(&mut self) -> Vec { + todo!("TODO") + } + /// Mark a transaction as signed. + pub fn signed_transaction(&mut self) { todo!("TODO") } + + /// Register a batch to sign. + pub fn sign_batch(&mut self, key: KeyFor, batch: Batch) -> Vec { + todo!("TODO") + } + /// Mark a batch as signed. + pub fn signed_batch(&mut self, batch: u32) { todo!("TODO") } + + /// Register a slash report to sign. + pub fn sign_slash_report(&mut self) -> Vec { + todo!("TODO") + } + /// Mark a slash report as signed. + pub fn signed_slash_report(&mut self) { todo!("TODO") } + + /// Start a cosigning protocol. + pub fn cosign(&mut self) { todo!("TODO") } + + /// Handle a message for a signing protocol. + pub fn handle(&mut self, msg: CoordinatorMessage) -> Vec { + todo!("TODO") + } +} +*/ diff --git a/processor/signers/src/substrate.rs b/processor/signers/src/substrate.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/processor/signers/src/transaction.rs b/processor/signers/src/transaction.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/processor/signers/src/transaction/db.rs b/processor/signers/src/transaction/db.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/processor/signers/src/transaction/db.rs @@ -0,0 +1 @@ + diff --git a/processor/signers/src/transaction/mod.rs b/processor/signers/src/transaction/mod.rs new file mode 100644 index 00000000..ba1487cb --- /dev/null +++ b/processor/signers/src/transaction/mod.rs @@ -0,0 +1,70 @@ +use serai_db::{Get, DbTxn, Db}; + +use primitives::task::ContinuallyRan; +use scanner::ScannerFeed; +use scheduler::TransactionsToSign; + +mod db; +use db::IndexDb; + +// Fetches transactions to sign and signs them. +pub(crate) struct TransactionTask { + db: D, + keys: ThresholdKeys<::Ciphersuite>, + attempt_manager: + AttemptManager::PreprocessMachine>, +} + +impl TransactionTask { + pub(crate) async fn new( + db: D, + keys: ThresholdKeys<::Ciphersuite>, + ) -> Self { + Self { db, keys, attempt_manager: AttemptManager::new() } + } +} + +#[async_trait::async_trait] +impl ContinuallyRan for TransactionTask { + async fn run_iteration(&mut self) -> Result { + let mut iterated = false; + + // Check for new transactions to sign + loop { + let mut txn = self.db.txn(); + let Some(tx) = TransactionsToSign::try_recv(&mut txn, self.key) else { break }; + iterated = true; + + let mut machines = Vec::with_capacity(self.keys.len()); + for keys in &self.keys { + machines.push(tx.clone().sign(keys.clone())); + } + let messages = self.attempt_manager.register(tx.id(), machines); + todo!("TODO"); + txn.commit(); + } + + // Check for completed Eventualities (meaning we should no longer sign for these transactions) + loop { + let mut txn = self.db.txn(); + let Some(tx) = CompletedEventualities::try_recv(&mut txn, self.key) else { break }; + iterated = true; + + self.attempt_manager.retire(tx); + txn.commit(); + } + + loop { + let mut txn = self.db.txn(); + let Some(msg) = TransactionSignMessages::try_recv(&mut txn, self.key) else { break }; + iterated = true; + + match self.attempt_manager.handle(msg) { + Response::Messages(messages) => todo!("TODO"), + Response::Signature(signature) => todo!("TODO"), + } + } + + Ok(iterated) + } +}