From b5bdb545ccb4581166b71e98679b565c2c36e864 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 28 May 2022 19:56:59 -0400 Subject: [PATCH] Add basic key management to the processor --- coins/monero/Cargo.toml | 3 +- processor/Cargo.toml | 6 ++++ processor/src/coins/monero.rs | 46 +++++++++++++++++++++++-------- processor/src/lib.rs | 52 ++++++++++++++++++++++++++++++----- processor/src/wallet.rs | 30 ++++++++++++++++++++ 5 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 processor/src/wallet.rs diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index 9c71c58c..68208ff0 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -22,9 +22,10 @@ curve25519-dalek = { version = "3", features = ["std"] } ff = { version = "0.11", optional = true } group = { version = "0.11", optional = true } + +dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true } transcript = { path = "../../crypto/transcript", optional = true } frost = { path = "../../crypto/frost", optional = true } -dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true } monero = { version = "0.16", features = ["experimental"] } diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 3de26e54..aa687755 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -8,9 +8,15 @@ edition = "2021" [dependencies] async-trait = "0.1" +rand_core = "0.6" thiserror = "1" curve25519-dalek = { version = "3", features = ["std"] } +blake2 = "0.10" + +dalek-ff-group = { path = "../crypto/dalek-ff-group" } +frost = { path = "../crypto/frost" } + monero = { version = "0.16", features = ["experimental"] } monero-serai = { path = "../coins/monero", features = ["multisig"] } diff --git a/processor/src/coins/monero.rs b/processor/src/coins/monero.rs index de5aa78d..614b01a4 100644 --- a/processor/src/coins/monero.rs +++ b/processor/src/coins/monero.rs @@ -1,11 +1,15 @@ use async_trait::async_trait; +use rand_core::{RngCore, CryptoRng}; -use curve25519_dalek::{traits::Identity, scalar::Scalar, edwards::EdwardsPoint}; +use curve25519_dalek::scalar::Scalar; + +use dalek_ff_group as dfg; +use frost::MultisigKeys; use monero::util::address::Address; -use monero_serai::{/*transaction::Output, */ rpc::Rpc, wallet::SpendableOutput}; +use monero_serai::{frost::Ed25519, rpc::Rpc, wallet::{SpendableOutput, SignableTransaction}}; -use crate::{Output as OutputTrait, CoinError, Coin}; +use crate::{Output as OutputTrait, CoinError, Coin, view_key}; pub struct Output(SpendableOutput); impl OutputTrait for Output { @@ -38,25 +42,28 @@ impl From for Output { pub struct Monero { rpc: Rpc, - view: Scalar, - spend: EdwardsPoint + view: Scalar } impl Monero { pub fn new(url: String) -> Monero { Monero { rpc: Rpc::new(url), - view: Scalar::zero(), - spend: EdwardsPoint::identity() + view: dfg::Scalar::from_hash(view_key::(0)).0 } } } #[async_trait] impl Coin for Monero { + type Curve = Ed25519; + type Output = Output; + type SignableTransaction = SignableTransaction; + type Address = Address; + fn id() -> &'static [u8] { b"Monero" } async fn confirmations() -> usize { 10 } // Testnet TX bb4d188a4c571f2f0de70dca9d475abc19078c10ffa8def26dd4f63ce1bcfd79 uses 146 inputs // while using less than 100kb of space, albeit with just 2 outputs (though outputs share a BP) @@ -71,17 +78,34 @@ impl Coin for Monero { self.rpc.get_height().await.map_err(|_| CoinError::ConnectionError) } - async fn get_outputs_in_block(&self, height: usize) -> Result, CoinError> { + async fn get_outputs_in_block( + &self, + height: usize, + key: dfg::EdwardsPoint + ) -> Result, CoinError> { Ok( self.rpc.get_block_transactions_possible(height).await.map_err(|_| CoinError::ConnectionError)? - .iter().flat_map(|tx| tx.scan(self.view, self.spend)).map(Output::from).collect() + .iter().flat_map(|tx| tx.scan(self.view, key.0)).map(Output::from).collect() ) } - async fn send( + async fn prepare_send( &self, + _keys: MultisigKeys, + _label: Vec, + _height: usize, + _inputs: Vec, _payments: &[(Address, u64)] - ) -> Result::Id>, CoinError> { + ) -> Result { + todo!() + } + + async fn attempt_send( + &self, + _rng: &mut R, + _transaction: SignableTransaction, + _included: &[u16] + ) -> Result<(Vec, Vec<::Id>), CoinError> { todo!() } } diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 41012af3..037c45b0 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -1,7 +1,15 @@ +use std::marker::Send; + use async_trait::async_trait; use thiserror::Error; +use rand_core::{RngCore, CryptoRng}; -pub mod coins; +use blake2::{digest::{Digest, Update}, Blake2b512}; + +use frost::{Curve, MultisigKeys}; + +mod coins; +mod wallet; #[cfg(test)] mod tests; @@ -16,7 +24,7 @@ trait Output: Sized { fn deserialize(reader: &mut R) -> std::io::Result; } -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] enum CoinError { #[error("failed to connect to coin daemon")] ConnectionError @@ -24,17 +32,47 @@ enum CoinError { #[async_trait] trait Coin { - type Output: Output; - type Address; + type Curve: Curve; + type Output: Output; + type SignableTransaction; + + type Address: Send; + + fn id() -> &'static [u8]; 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, height: usize) -> Result, CoinError>; - async fn send( + async fn get_outputs_in_block( &self, + height: usize, + key: ::G + ) -> Result, CoinError>; + + async fn prepare_send( + &self, + keys: MultisigKeys, + label: Vec, + height: usize, + inputs: Vec, payments: &[(Self::Address, u64)] - ) -> Result::Id>, CoinError>; + ) -> Result; + + async fn attempt_send( + &self, + rng: &mut R, + transaction: Self::SignableTransaction, + included: &[u16] + ) -> Result<(Vec, Vec<::Id>), CoinError>; +} + +// Generate a view key for a given chain in a globally consistent manner regardless of the current +// group key +// Takes an index, k, for more modern privacy protocols which use multiple view keys +// Doesn't run Curve::hash_to_F, instead returning the hash object, due to hash_to_F being a FROST +// definition instead of a wide reduction from a hash object +fn view_key(k: u64) -> Blake2b512 { + Blake2b512::new().chain(b"Serai DEX View Key").chain(C::id()).chain(k.to_le_bytes()) } diff --git a/processor/src/wallet.rs b/processor/src/wallet.rs new file mode 100644 index 00000000..347bd787 --- /dev/null +++ b/processor/src/wallet.rs @@ -0,0 +1,30 @@ +use frost::{Curve, MultisigKeys}; + +use crate::Coin; + +struct Wallet { + keys: MultisigKeys, + outputs: Vec +} + +impl Wallet { + fn new(keys: &MultisigKeys) -> Wallet { + Wallet { + keys: keys.offset( + C::Curve::hash_to_F( + // Use distinct keys on each network by applying an additive offset + // While it would be fine to just C::id(), including the group key creates distinct + // offsets instead of static offsets. Under a statically offset system, a BTC key could + // have X subtracted to find the potential group key, and then have Y added to find the + // potential BCH group key. While this shouldn't be an issue, as this isn't a private + // system, there are potentially other benefits to binding this to a specific group key + &[b"Serai Processor Wallet", C::id(), &C::Curve::G_to_bytes(&keys.group_key())].concat() + ) + ), + + outputs: vec![] + } + } + + async fn poll() { todo!() } +}