Make coin a dedicated library

Closes https://github.com/serai-dex/serai/issues/128.
This commit is contained in:
Luke Parker 2022-10-15 23:21:43 -04:00
parent f50148d17a
commit 65664dafa4
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
9 changed files with 119 additions and 42 deletions

28
Cargo.lock generated
View file

@ -4564,7 +4564,7 @@ dependencies = [
[[package]] [[package]]
name = "modular-frost" name = "modular-frost"
version = "0.2.3" version = "0.2.4"
dependencies = [ dependencies = [
"dalek-ff-group", "dalek-ff-group",
"dleq", "dleq",
@ -7497,6 +7497,23 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
[[package]]
name = "serai-coin"
version = "0.1.0"
dependencies = [
"async-trait",
"curve25519-dalek 3.2.0",
"dalek-ff-group",
"flexible-transcript",
"group",
"modular-frost",
"monero-serai",
"rand_core 0.6.4",
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "serai-consensus" name = "serai-consensus"
version = "0.1.0" version = "0.1.0"
@ -7587,20 +7604,13 @@ name = "serai-processor"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"blake2",
"curve25519-dalek 3.2.0",
"dalek-ff-group",
"flexible-transcript", "flexible-transcript",
"futures", "futures",
"group", "group",
"hex", "hex",
"k256",
"modular-frost", "modular-frost",
"monero-serai",
"rand_core 0.6.4", "rand_core 0.6.4",
"serde", "serai-coin",
"serde_json",
"sha3 0.10.5",
"thiserror", "thiserror",
"tokio", "tokio",
] ]

41
coin/Cargo.toml Normal file
View file

@ -0,0 +1,41 @@
[package]
name = "serai-coin"
version = "0.1.0"
description = "Abstract interface to represent a coin"
license = "MIT"
repository = "https://github.com/serai-dex/serai"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
publish = false
[dependencies]
async-trait = "0.1"
thiserror = "1"
curve25519-dalek = { version = "3", features = ["std"] }
transcript = { package = "flexible-transcript", path = "../crypto/transcript", features = ["recommended"] }
dalek-ff-group = { path = "../crypto/dalek-ff-group" }
frost = { package = "modular-frost", path = "../crypto/frost", features = ["secp256k1", "ed25519"] }
monero-serai = { path = "../coins/monero", features = ["multisig"] }
# Test Dependencies
rand_core = { version = "0.6", optional = true }
group = { version = "0.12", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
[dev-dependencies]
rand_core = "0.6"
group = "0.12"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[features]
test = ["rand_core", "group", "serde", "serde_json"]

21
coin/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Luke Parker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -6,6 +6,8 @@ use thiserror::Error;
use transcript::RecommendedTranscript; use transcript::RecommendedTranscript;
use frost::{curve::Curve, FrostKeys, sign::PreprocessMachine}; use frost::{curve::Curve, FrostKeys, sign::PreprocessMachine};
pub(crate) mod utils;
pub mod monero; pub mod monero;
pub use self::monero::Monero; pub use self::monero::Monero;
@ -79,9 +81,12 @@ pub trait Coin {
tx: &Self::Transaction, tx: &Self::Transaction,
) -> Result<(Vec<u8>, Vec<<Self::Output as Output>::Id>), CoinError>; ) -> Result<(Vec<u8>, Vec<<Self::Output as Output>::Id>), CoinError>;
#[cfg(test)] #[cfg(any(test, feature = "test"))]
async fn get_fee(&self) -> Self::Fee;
#[cfg(any(test, feature = "test"))]
async fn mine_block(&self); async fn mine_block(&self);
#[cfg(test)] #[cfg(any(test, feature = "test"))]
async fn test_send(&self, key: Self::Address); async fn test_send(&self, key: Self::Address);
} }

View file

@ -17,10 +17,7 @@ use monero_serai::{
}, },
}; };
use crate::{ use crate::{CoinError, Output as OutputTrait, Coin, utils::additional_key};
coin::{CoinError, Output as OutputTrait, Coin},
view_key,
};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Output(SpendableOutput); pub struct Output(SpendableOutput);
@ -70,14 +67,14 @@ pub struct Monero {
impl Monero { impl Monero {
pub async fn new(url: String) -> Monero { pub async fn new(url: String) -> Monero {
Monero { rpc: Rpc::new(url), view: view_key::<Monero>(0).0 } Monero { rpc: Rpc::new(url), view: additional_key::<Monero>(0).0 }
} }
fn scanner(&self, spend: dfg::EdwardsPoint) -> Scanner { fn scanner(&self, spend: dfg::EdwardsPoint) -> Scanner {
Scanner::from_view(ViewPair::new(spend.0, self.view), Network::Mainnet, None) Scanner::from_view(ViewPair::new(spend.0, self.view), Network::Mainnet, None)
} }
#[cfg(test)] #[cfg(any(test, feature = "test"))]
fn empty_scanner() -> Scanner { fn empty_scanner() -> Scanner {
use group::Group; use group::Group;
Scanner::from_view( Scanner::from_view(
@ -87,7 +84,7 @@ impl Monero {
) )
} }
#[cfg(test)] #[cfg(any(test, feature = "test"))]
fn empty_address() -> Address { fn empty_address() -> Address {
Self::empty_scanner().address() Self::empty_scanner().address()
} }
@ -209,7 +206,12 @@ impl Coin for Monero {
Ok((tx.hash().to_vec(), tx.prefix.outputs.iter().map(|output| output.key.to_bytes()).collect())) Ok((tx.hash().to_vec(), tx.prefix.outputs.iter().map(|output| output.key.to_bytes()).collect()))
} }
#[cfg(test)] #[cfg(any(test, feature = "test"))]
async fn get_fee(&self) -> Self::Fee {
self.rpc.get_fee().await.unwrap()
}
#[cfg(any(test, feature = "test"))]
async fn mine_block(&self) { async fn mine_block(&self) {
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug)]
struct EmptyResponse {} struct EmptyResponse {}
@ -229,7 +231,7 @@ impl Coin for Monero {
.unwrap(); .unwrap();
} }
#[cfg(test)] #[cfg(any(test, feature = "test"))]
async fn test_send(&self, address: Self::Address) { async fn test_send(&self, address: Self::Address) {
use rand_core::OsRng; use rand_core::OsRng;

11
coin/src/utils.rs Normal file
View file

@ -0,0 +1,11 @@
use frost::curve::Curve;
use crate::Coin;
// Generate a static additional key for a given chain in a globally consistent manner
// Doesn't consider the current group key to increase the simplicity of verifying Serai's status
// Takes an index, k, to support protocols which use multiple secondary keys
// Presumably a view key
pub(crate) fn additional_key<C: Coin>(k: u64) -> <C::Curve as Curve>::F {
C::Curve::hash_to_F(b"Serai DEX Additional Key", &[C::ID, &k.to_le_bytes()].concat())
}

View file

@ -3,7 +3,7 @@ name = "serai-processor"
version = "0.1.0" version = "0.1.0"
description = "Multichain processor premised on canonicity to reach distributed consensus automatically" description = "Multichain processor premised on canonicity to reach distributed consensus automatically"
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/processor" repository = "https://github.com/serai-dex/serai"
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = [] keywords = []
edition = "2021" edition = "2021"
@ -14,24 +14,17 @@ async-trait = "0.1"
rand_core = "0.6" rand_core = "0.6"
thiserror = "1" thiserror = "1"
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha3 = "0.10"
blake2 = "0.10"
group = "0.12" group = "0.12"
k256 = { version = "0.11", features = ["arithmetic", "keccak256", "ecdsa"] }
curve25519-dalek = { version = "3", features = ["std"] }
transcript = { package = "flexible-transcript", path = "../crypto/transcript", features = ["recommended"] } transcript = { package = "flexible-transcript", path = "../crypto/transcript", features = ["recommended"] }
dalek-ff-group = { path = "../crypto/dalek-ff-group" }
frost = { package = "modular-frost", path = "../crypto/frost", features = ["secp256k1", "ed25519"] } frost = { package = "modular-frost", path = "../crypto/frost", features = ["secp256k1", "ed25519"] }
monero-serai = { path = "../coins/monero", features = ["multisig"] } serai-coin = { path = "../coin" }
[dev-dependencies] [dev-dependencies]
group = "0.12" hex = "0.4"
futures = "0.3" futures = "0.3"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
serai-coin = { path = "../coin", features = ["test"] }

View file

@ -5,8 +5,9 @@ use thiserror::Error;
use frost::{curve::Curve, FrostError}; use frost::{curve::Curve, FrostError};
mod coin; pub use serai_coin as coin;
use coin::{CoinError, Coin}; use coin::{CoinError, Coin};
mod wallet; mod wallet;
#[cfg(test)] #[cfg(test)]
@ -29,10 +30,3 @@ pub enum SignError {
#[error("network had an error {0}")] #[error("network had an error {0}")]
NetworkError(NetworkError), NetworkError(NetworkError),
} }
// Generate a static view key for a given chain in a globally consistent manner
// Doesn't consider the current group key to increase the simplicity of verifying Serai's status
// Takes an index, k, for more modern privacy protocols which use multiple view keys
pub fn view_key<C: Coin>(k: u64) -> <C::Curve as Curve>::F {
C::Curve::hash_to_F(b"Serai DEX View Key", &[C::ID, &k.to_le_bytes()].concat())
}

View file

@ -112,6 +112,6 @@ async fn test_send<C: Coin + Clone>(coin: C, fee: C::Fee) {
#[tokio::test] #[tokio::test]
async fn monero() { async fn monero() {
let monero = Monero::new("http://127.0.0.1:18081".to_string()).await; let monero = Monero::new("http://127.0.0.1:18081".to_string()).await;
let fee = monero.rpc.get_fee().await.unwrap(); let fee = monero.get_fee().await;
test_send(monero, fee).await; test_send(monero, fee).await;
} }