mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Add basic key management to the processor
This commit is contained in:
@@ -22,9 +22,10 @@ curve25519-dalek = { version = "3", features = ["std"] }
|
|||||||
|
|
||||||
ff = { version = "0.11", optional = true }
|
ff = { version = "0.11", optional = true }
|
||||||
group = { 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 }
|
transcript = { path = "../../crypto/transcript", optional = true }
|
||||||
frost = { path = "../../crypto/frost", optional = true }
|
frost = { path = "../../crypto/frost", optional = true }
|
||||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true }
|
|
||||||
|
|
||||||
monero = { version = "0.16", features = ["experimental"] }
|
monero = { version = "0.16", features = ["experimental"] }
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,15 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
rand_core = "0.6"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
||||||
curve25519-dalek = { version = "3", features = ["std"] }
|
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 = { version = "0.16", features = ["experimental"] }
|
||||||
monero-serai = { path = "../coins/monero", features = ["multisig"] }
|
monero-serai = { path = "../coins/monero", features = ["multisig"] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
use async_trait::async_trait;
|
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::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);
|
pub struct Output(SpendableOutput);
|
||||||
impl OutputTrait for Output {
|
impl OutputTrait for Output {
|
||||||
@@ -38,25 +42,28 @@ impl From<SpendableOutput> for Output {
|
|||||||
|
|
||||||
pub struct Monero {
|
pub struct Monero {
|
||||||
rpc: Rpc,
|
rpc: Rpc,
|
||||||
view: Scalar,
|
view: Scalar
|
||||||
spend: EdwardsPoint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Monero {
|
impl Monero {
|
||||||
pub fn new(url: String) -> Monero {
|
pub fn new(url: String) -> Monero {
|
||||||
Monero {
|
Monero {
|
||||||
rpc: Rpc::new(url),
|
rpc: Rpc::new(url),
|
||||||
view: Scalar::zero(),
|
view: dfg::Scalar::from_hash(view_key::<Monero>(0)).0
|
||||||
spend: EdwardsPoint::identity()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Coin for Monero {
|
impl Coin for Monero {
|
||||||
|
type Curve = Ed25519;
|
||||||
|
|
||||||
type Output = Output;
|
type Output = Output;
|
||||||
|
type SignableTransaction = SignableTransaction;
|
||||||
|
|
||||||
type Address = Address;
|
type Address = Address;
|
||||||
|
|
||||||
|
fn id() -> &'static [u8] { b"Monero" }
|
||||||
async fn confirmations() -> usize { 10 }
|
async fn confirmations() -> usize { 10 }
|
||||||
// Testnet TX bb4d188a4c571f2f0de70dca9d475abc19078c10ffa8def26dd4f63ce1bcfd79 uses 146 inputs
|
// Testnet TX bb4d188a4c571f2f0de70dca9d475abc19078c10ffa8def26dd4f63ce1bcfd79 uses 146 inputs
|
||||||
// while using less than 100kb of space, albeit with just 2 outputs (though outputs share a BP)
|
// 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)
|
self.rpc.get_height().await.map_err(|_| CoinError::ConnectionError)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_outputs_in_block(&self, height: usize) -> Result<Vec<Self::Output>, CoinError> {
|
async fn get_outputs_in_block(
|
||||||
|
&self,
|
||||||
|
height: usize,
|
||||||
|
key: dfg::EdwardsPoint
|
||||||
|
) -> Result<Vec<Self::Output>, CoinError> {
|
||||||
Ok(
|
Ok(
|
||||||
self.rpc.get_block_transactions_possible(height).await.map_err(|_| CoinError::ConnectionError)?
|
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<R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
|
_keys: MultisigKeys<Ed25519>,
|
||||||
|
_label: Vec<u8>,
|
||||||
|
_height: usize,
|
||||||
|
_inputs: Vec<Output>,
|
||||||
_payments: &[(Address, u64)]
|
_payments: &[(Address, u64)]
|
||||||
) -> Result<Vec<<Self::Output as OutputTrait>::Id>, CoinError> {
|
) -> Result<SignableTransaction, CoinError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn attempt_send<R: RngCore + CryptoRng + std::marker::Send>(
|
||||||
|
&self,
|
||||||
|
_rng: &mut R,
|
||||||
|
_transaction: SignableTransaction,
|
||||||
|
_included: &[u16]
|
||||||
|
) -> Result<(Vec<u8>, Vec<<Self::Output as OutputTrait>::Id>), CoinError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
|
use std::marker::Send;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use thiserror::Error;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@@ -16,7 +24,7 @@ trait Output: Sized {
|
|||||||
fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self>;
|
fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Clone, Error, Debug)]
|
||||||
enum CoinError {
|
enum CoinError {
|
||||||
#[error("failed to connect to coin daemon")]
|
#[error("failed to connect to coin daemon")]
|
||||||
ConnectionError
|
ConnectionError
|
||||||
@@ -24,17 +32,47 @@ enum CoinError {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
trait Coin {
|
trait Coin {
|
||||||
type Output: Output;
|
type Curve: Curve;
|
||||||
type Address;
|
|
||||||
|
|
||||||
|
type Output: Output;
|
||||||
|
type SignableTransaction;
|
||||||
|
|
||||||
|
type Address: Send;
|
||||||
|
|
||||||
|
fn id() -> &'static [u8];
|
||||||
async fn confirmations() -> usize;
|
async fn confirmations() -> usize;
|
||||||
async fn max_inputs() -> usize;
|
async fn max_inputs() -> usize;
|
||||||
async fn max_outputs() -> usize;
|
async fn max_outputs() -> usize;
|
||||||
|
|
||||||
async fn get_height(&self) -> Result<usize, CoinError>;
|
async fn get_height(&self) -> Result<usize, CoinError>;
|
||||||
async fn get_outputs_in_block(&self, height: usize) -> Result<Vec<Self::Output>, CoinError>;
|
async fn get_outputs_in_block(
|
||||||
async fn send(
|
|
||||||
&self,
|
&self,
|
||||||
|
height: usize,
|
||||||
|
key: <Self::Curve as Curve>::G
|
||||||
|
) -> Result<Vec<Self::Output>, CoinError>;
|
||||||
|
|
||||||
|
async fn prepare_send<R: RngCore + CryptoRng>(
|
||||||
|
&self,
|
||||||
|
keys: MultisigKeys<Self::Curve>,
|
||||||
|
label: Vec<u8>,
|
||||||
|
height: usize,
|
||||||
|
inputs: Vec<Self::Output>,
|
||||||
payments: &[(Self::Address, u64)]
|
payments: &[(Self::Address, u64)]
|
||||||
) -> Result<Vec<<Self::Output as Output>::Id>, CoinError>;
|
) -> Result<Self::SignableTransaction, CoinError>;
|
||||||
|
|
||||||
|
async fn attempt_send<R: RngCore + CryptoRng + Send>(
|
||||||
|
&self,
|
||||||
|
rng: &mut R,
|
||||||
|
transaction: Self::SignableTransaction,
|
||||||
|
included: &[u16]
|
||||||
|
) -> Result<(Vec<u8>, Vec<<Self::Output as Output>::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<C: Coin>(k: u64) -> Blake2b512 {
|
||||||
|
Blake2b512::new().chain(b"Serai DEX View Key").chain(C::id()).chain(k.to_le_bytes())
|
||||||
}
|
}
|
||||||
|
|||||||
30
processor/src/wallet.rs
Normal file
30
processor/src/wallet.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use frost::{Curve, MultisigKeys};
|
||||||
|
|
||||||
|
use crate::Coin;
|
||||||
|
|
||||||
|
struct Wallet<C: Coin> {
|
||||||
|
keys: MultisigKeys<C::Curve>,
|
||||||
|
outputs: Vec<C::Output>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Coin> Wallet<C> {
|
||||||
|
fn new(keys: &MultisigKeys<C::Curve>) -> Wallet<C> {
|
||||||
|
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!() }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user