diff --git a/Cargo.toml b/Cargo.toml index 4305df32..19cb80f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ members = [ "crypto/frost", "crypto/dalek-ff-group", "coins/monero", + "processor", ] diff --git a/processor/Cargo.toml b/processor/Cargo.toml new file mode 100644 index 00000000..d9bab263 --- /dev/null +++ b/processor/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "serai-processor" +version = "0.1.0" +description = "Multichain processor premised on canonicity to reach distributed consensus automatically" +license = "MIT" +authors = ["Luke Parker "] +edition = "2021" + +[dependencies] +async-trait = "0.1" +thiserror = "1" + +monero = { version = "0.16", features = ["experimental"] } +monero-serai = { path = "../coins/monero", features = ["multisig"] } + +[dev-dependencies] +tokio = { version = "1", features = ["full"] } diff --git a/processor/src/coins/mod.rs b/processor/src/coins/mod.rs new file mode 100644 index 00000000..3c43a86a --- /dev/null +++ b/processor/src/coins/mod.rs @@ -0,0 +1 @@ +pub mod monero; diff --git a/processor/src/coins/monero.rs b/processor/src/coins/monero.rs new file mode 100644 index 00000000..88390795 --- /dev/null +++ b/processor/src/coins/monero.rs @@ -0,0 +1,62 @@ +use async_trait::async_trait; + +use monero::util::address::Address; +use monero_serai::{/*transaction::Output, */ rpc::Rpc, wallet::SpendableOutput}; + +use crate::{Output as OutputTrait, CoinError, Coin}; + +pub struct Output(SpendableOutput); +impl OutputTrait for Output { + // If Monero ever does support more than 255 outputs at once, which it could, this u8 could be a + // u16 which serializes as little endian, dropping the last byte if empty, without conflict + type Id = ([u8; 32], u8); + + fn id(&self) -> Self::Id { + (self.0.tx, self.0.o.try_into().unwrap()) + } + + fn amount(&self) -> u64 { + self.0.commitment.amount + } + + fn serialize(&self) -> Vec { + self.0.serialize() + } + + fn deserialize(reader: &mut R) -> std::io::Result { + SpendableOutput::deserialize(reader).map(|o| Output(o)) + } +} + +pub struct Monero(Rpc); + +impl Monero { + pub fn new(url: String) -> Monero { + Monero(Rpc::new(url)) + } +} + +#[async_trait] +impl Coin for Monero { + type Output = Output; + type Address = Address; + + async fn confirmations() -> usize { 10 } + async fn max_inputs() -> usize { 16 } // TODO + async fn max_outputs() -> usize { 16 } + + async fn get_height(&self) -> Result { + self.0.get_height().await.map_err(|_| CoinError::ConnectionError) + } + + async fn get_outputs_in_block(&self) -> Result, CoinError> { + todo!() + } + + async fn send( + &self, + _payments: &[(Address, u64)] + ) -> Result::Id>, CoinError> { + todo!() + } +} diff --git a/processor/src/lib.rs b/processor/src/lib.rs new file mode 100644 index 00000000..ac355340 --- /dev/null +++ b/processor/src/lib.rs @@ -0,0 +1,40 @@ +use async_trait::async_trait; +use thiserror::Error; + +pub mod coins; + +#[cfg(test)] +mod tests; + +trait Output: Sized { + type Id; + + fn id(&self) -> Self::Id; + fn amount(&self) -> u64; + + fn serialize(&self) -> Vec; + fn deserialize(reader: &mut R) -> std::io::Result; +} + +#[derive(Error, Debug)] +enum CoinError { + #[error("failed to connect to coin daemon")] + ConnectionError +} + +#[async_trait] +trait Coin { + type Output: Output; + type Address; + + async fn confirmations() -> usize; + async fn max_inputs() -> usize; + async fn max_outputs() -> usize; + + async fn get_height(&self) -> Result; + async fn get_outputs_in_block(&self) -> Result, CoinError>; + async fn send( + &self, + payments: &[(Self::Address, u64)] + ) -> Result::Id>, CoinError>; +} diff --git a/processor/src/scanner.rs b/processor/src/scanner.rs new file mode 100644 index 00000000..59cca8fe --- /dev/null +++ b/processor/src/scanner.rs @@ -0,0 +1,5 @@ +struct Scanner {} + +impl Scanner { + +} diff --git a/processor/src/scheduler.rs b/processor/src/scheduler.rs new file mode 100644 index 00000000..6c545eb4 --- /dev/null +++ b/processor/src/scheduler.rs @@ -0,0 +1,4 @@ +// For n existing inputs, and n target outputs, multiplex the inputs in while log scheduling the +// outputs out. Monero, which has a limit of 16 TXOs, could do 15 at a time, carrying a change +// Combined with the 20 minute lock, this is completely infeasible. By instead doing 15 TX seeds, +// and then 16 outputs on each, in just two lock cycles you can accomplish 240 TXs (not just 30). diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs new file mode 100644 index 00000000..272342f4 --- /dev/null +++ b/processor/src/tests/mod.rs @@ -0,0 +1,6 @@ +use crate::{Coin, coins::monero::Monero}; + +#[tokio::test] +async fn test() { + println!("{}", Monero::new("http://127.0.0.1:18081".to_string()).get_height().await.unwrap()); +}