From eafd0542968fcb0d5dc2defe8fdb3edf1652bb7a Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 15 Apr 2023 17:38:47 -0400 Subject: [PATCH] Start defining the coordinator --- Cargo.lock | 2 + coordinator/Cargo.toml | 6 +++ coordinator/src/main.rs | 28 ++++++++++++- coordinator/src/substrate.rs | 52 ++++++++++++++++++++++++ substrate/client/src/serai/mod.rs | 44 ++++++++++++++++++-- substrate/client/tests/common/tx.rs | 17 +++----- substrate/client/tests/validator_sets.rs | 5 +-- 7 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 coordinator/src/substrate.rs diff --git a/Cargo.lock b/Cargo.lock index f8de2698..e8c55722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1305,10 +1305,12 @@ name = "coordinator" version = "0.1.0" dependencies = [ "blake2", + "log", "modular-frost", "processor-messages", "rand_core 0.6.4", "serai-client", + "serai-db", "tokio", "tributary-chain", ] diff --git a/coordinator/Cargo.toml b/coordinator/Cargo.toml index 067fb515..63cb3061 100644 --- a/coordinator/Cargo.toml +++ b/coordinator/Cargo.toml @@ -14,13 +14,19 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +log = "0.4" + blake2 = "0.10" frost = { package = "modular-frost", path = "../crypto/frost" } +serai-db = { path = "../common/db" } + processor-messages = { package = "processor-messages", path = "../processor/messages" } tributary = { package = "tributary-chain", path = "./tributary" } +serai-client = { path = "../substrate/client", features = ["serai"] } + tokio = { version = "1", features = ["full"] } [dev-dependencies] diff --git a/coordinator/src/main.rs b/coordinator/src/main.rs index 60e9da44..516c1999 100644 --- a/coordinator/src/main.rs +++ b/coordinator/src/main.rs @@ -1,7 +1,33 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_mut)] + +use serai_db::Db; + +use serai_client::Serai; + mod transaction; +mod substrate; #[cfg(test)] mod tests; +async fn run(db: D, serai: Serai) { + let mut last_substrate_block = 0; // TODO: Load from DB + + loop { + match substrate::handle_new_blocks(&serai, &mut last_substrate_block).await { + Ok(()) => {} + Err(e) => log::error!("couldn't communicate with serai node: {e}"), + } + + // Handle all messages from tributaries + + // Handle all messages from processors + } +} + #[tokio::main] -async fn main() {} +async fn main() { + // Open the database +} diff --git a/coordinator/src/substrate.rs b/coordinator/src/substrate.rs new file mode 100644 index 00000000..ec0b8bac --- /dev/null +++ b/coordinator/src/substrate.rs @@ -0,0 +1,52 @@ +use serai_client::{SeraiError, Block, Serai}; + +async fn handle_block(serai: &Serai, block: Block) -> Result, SeraiError> { + let hash = block.hash(); + let mut actions = vec![]; + + // If a new validator set was activated, create tributary/inform processor to do a DKG + for new_set in serai.get_new_set_events(hash).await? { + todo!() + } + + // If a key pair was confirmed, inform the processor + for key_gen in serai.get_key_gen_events(hash).await? { + todo!() + } + + // If batch, tell processor of block acknowledged/burns + for new_set in serai.get_batch_events(hash).await? { + todo!() + } + + Ok(actions) +} + +pub(crate) async fn handle_new_blocks( + serai: &Serai, + last_substrate_block: &mut u64, +) -> Result<(), SeraiError> { + // Check if there's been a new Substrate block + let latest = serai.get_latest_block().await?; + let latest_number = latest.number(); + if latest_number == *last_substrate_block { + return Ok(()); + } + let mut latest = Some(latest); + + for b in (*last_substrate_block + 1) ..= latest_number { + let actions = handle_block( + serai, + if b == latest_number { + latest.take().unwrap() + } else { + serai.get_block_by_number(b).await?.unwrap() + }, + ) + .await?; + // TODO: Handle actions, update the DB + *last_substrate_block += 1; + } + + Ok(()) +} diff --git a/substrate/client/src/serai/mod.rs b/substrate/client/src/serai/mod.rs index baeff7cc..6814a988 100644 --- a/substrate/client/src/serai/mod.rs +++ b/substrate/client/src/serai/mod.rs @@ -16,7 +16,7 @@ use subxt::{ extrinsic_params::{BaseExtrinsicParams, BaseExtrinsicParamsBuilder}, }, tx::{Signer, Payload, TxClient}, - rpc::types::ChainBlock, + rpc::types::{ChainBlock, ChainBlockExtrinsic}, Config as SubxtConfig, OnlineClient, }; @@ -56,7 +56,32 @@ impl SubxtConfig for SeraiConfig { type ExtrinsicParams = BaseExtrinsicParams; } -pub type Block = ChainBlock; +#[derive(Debug)] +pub struct Block(ChainBlock); +impl Block { + pub fn hash(&self) -> [u8; 32] { + self.0.header.hash().into() + } + pub fn number(&self) -> u64 { + self.0.header.number + } + + pub fn header(&self) -> &Header { + &self.0.header + } + pub fn transactions(&self) -> &[ChainBlockExtrinsic] { + &self.0.extrinsics + } +} + +impl Clone for Block { + fn clone(&self) -> Block { + Block(ChainBlock:: { + header: self.0.header.clone(), + extrinsics: self.0.extrinsics.clone(), + }) + } +} #[derive(Error, Debug)] pub enum SeraiError { @@ -120,6 +145,19 @@ impl Serai { Ok(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?.into()) } + pub async fn get_latest_block(&self) -> Result { + Ok(Block( + self + .0 + .rpc() + .block(Some(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?)) + .await + .map_err(SeraiError::RpcError)? + .ok_or(SeraiError::InvalidNode)? + .block, + )) + } + // There is no provided method for this // TODO: Add one to Serai pub async fn is_finalized(&self, header: &Header) -> Result, SeraiError> { @@ -169,7 +207,7 @@ impl Serai { return Ok(None); } - Ok(Some(res.block)) + Ok(Some(Block(res.block))) } // Ideally, this would be get_block_hash, not get_block_by_number diff --git a/substrate/client/tests/common/tx.rs b/substrate/client/tests/common/tx.rs index 0e00e034..43925096 100644 --- a/substrate/client/tests/common/tx.rs +++ b/substrate/client/tests/common/tx.rs @@ -2,7 +2,7 @@ use core::time::Duration; use tokio::time::sleep; -use serai_client::subxt::{config::Header, utils::Encoded}; +use serai_client::subxt::utils::Encoded; use crate::common::serai; @@ -10,13 +10,8 @@ use crate::common::serai; pub async fn publish_tx(tx: &Encoded) -> [u8; 32] { let serai = serai().await; - let mut latest = serai - .get_block(serai.get_latest_block_hash().await.unwrap()) - .await - .unwrap() - .unwrap() - .header - .number(); + let mut latest = + serai.get_block(serai.get_latest_block_hash().await.unwrap()).await.unwrap().unwrap().number(); serai.publish(tx).await.unwrap(); @@ -42,9 +37,9 @@ pub async fn publish_tx(tx: &Encoded) -> [u8; 32] { block.unwrap() }; - for extrinsic in block.extrinsics { - if extrinsic.0 == tx.0[2 ..] { - return block.header.hash().into(); + for transaction in block.transactions() { + if transaction.0 == tx.0[2 ..] { + return block.hash(); } } } diff --git a/substrate/client/tests/validator_sets.rs b/substrate/client/tests/validator_sets.rs index 19a162e3..48ad0459 100644 --- a/substrate/client/tests/validator_sets.rs +++ b/substrate/client/tests/validator_sets.rs @@ -10,7 +10,6 @@ use serai_client::{ primitives::{Session, ValidatorSet}, ValidatorSetsEvent, }, - subxt::config::Header, Serai, }; @@ -38,9 +37,7 @@ serai_test!( // Make sure the genesis is as expected assert_eq!( serai - .get_new_set_events( - serai.get_block_by_number(0).await.unwrap().unwrap().header.hash().into() - ) + .get_new_set_events(serai.get_block_by_number(0).await.unwrap().unwrap().hash()) .await .unwrap(), [BITCOIN_NET_ID, ETHEREUM_NET_ID, MONERO_NET_ID]