Smash out RPC, wallet

This commit is contained in:
Luke Parker
2024-06-16 18:40:15 -04:00
parent 3a1c6c7247
commit d740bd2924
76 changed files with 578 additions and 336 deletions

72
Cargo.lock generated
View File

@@ -4847,20 +4847,29 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "monero-rpc"
version = "0.1.0"
dependencies = [
"async-trait",
"curve25519-dalek",
"hex",
"monero-serai",
"serde",
"serde_json",
"std-shims",
"thiserror",
"zeroize",
]
[[package]] [[package]]
name = "monero-serai" name = "monero-serai"
version = "0.1.4-alpha" version = "0.1.4-alpha"
dependencies = [ dependencies = [
"async-trait",
"base58-monero",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group",
"digest_auth", "digest_auth",
"flexible-transcript",
"group",
"hex", "hex",
"hex-literal", "hex-literal",
"modular-frost",
"monero-borromean", "monero-borromean",
"monero-bulletproofs", "monero-bulletproofs",
"monero-clsag", "monero-clsag",
@@ -4868,6 +4877,43 @@ dependencies = [
"monero-io", "monero-io",
"monero-mlsag", "monero-mlsag",
"monero-primitives", "monero-primitives",
"rand_core",
"serde",
"serde_json",
"std-shims",
"thiserror",
"tokio",
"zeroize",
]
[[package]]
name = "monero-simple-request-rpc"
version = "0.1.0"
dependencies = [
"async-trait",
"digest_auth",
"hex",
"monero-rpc",
"simple-request",
"tokio",
]
[[package]]
name = "monero-wallet"
version = "0.1.0"
dependencies = [
"async-trait",
"base58-monero",
"curve25519-dalek",
"dalek-ff-group",
"flexible-transcript",
"group",
"hex",
"hex-literal",
"modular-frost",
"monero-rpc",
"monero-serai",
"monero-simple-request-rpc",
"pbkdf2 0.12.2", "pbkdf2 0.12.2",
"rand", "rand",
"rand_chacha", "rand_chacha",
@@ -4876,7 +4922,6 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha3", "sha3",
"simple-request",
"std-shims", "std-shims",
"subtle", "subtle",
"thiserror", "thiserror",
@@ -7867,7 +7912,7 @@ dependencies = [
"frost-schnorrkel", "frost-schnorrkel",
"hex", "hex",
"modular-frost", "modular-frost",
"monero-serai", "monero-wallet",
"multiaddr", "multiaddr",
"parity-scale-codec", "parity-scale-codec",
"rand_core", "rand_core",
@@ -8025,7 +8070,8 @@ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"dockertest", "dockertest",
"hex", "hex",
"monero-serai", "monero-simple-request-rpc",
"monero-wallet",
"parity-scale-codec", "parity-scale-codec",
"rand_core", "rand_core",
"serai-client", "serai-client",
@@ -8129,7 +8175,9 @@ dependencies = [
"monero-io", "monero-io",
"monero-mlsag", "monero-mlsag",
"monero-primitives", "monero-primitives",
"monero-rpc",
"monero-serai", "monero-serai",
"monero-wallet",
"multiexp", "multiexp",
"schnorr-signatures", "schnorr-signatures",
] ]
@@ -8230,7 +8278,8 @@ dependencies = [
"k256", "k256",
"log", "log",
"modular-frost", "modular-frost",
"monero-serai", "monero-simple-request-rpc",
"monero-wallet",
"parity-scale-codec", "parity-scale-codec",
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
@@ -8276,7 +8325,8 @@ dependencies = [
"ethereum-serai", "ethereum-serai",
"hex", "hex",
"k256", "k256",
"monero-serai", "monero-simple-request-rpc",
"monero-wallet",
"parity-scale-codec", "parity-scale-codec",
"rand_core", "rand_core",
"serai-client", "serai-client",

View File

@@ -51,6 +51,9 @@ members = [
"coins/monero/ringct/borromean", "coins/monero/ringct/borromean",
"coins/monero/ringct/bulletproofs", "coins/monero/ringct/bulletproofs",
"coins/monero", "coins/monero",
"coins/monero/rpc",
"coins/monero/rpc/simple-request",
"coins/monero/wallet",
"message-queue", "message-queue",

View File

@@ -18,32 +18,13 @@ workspace = true
[dependencies] [dependencies]
std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-features = false } std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-features = false }
async-trait = { version = "0.1", default-features = false }
thiserror = { version = "1", default-features = false, optional = true } thiserror = { version = "1", default-features = false, optional = true }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
subtle = { version = "^2.4", default-features = false }
rand_core = { version = "0.6", default-features = false } rand_core = { version = "0.6", default-features = false }
# Used to send transactions
rand = { version = "0.8", default-features = false }
rand_chacha = { version = "0.3", default-features = false }
# Used to select decoys
rand_distr = { version = "0.4", default-features = false }
sha3 = { version = "0.10", default-features = false }
pbkdf2 = { version = "0.12", features = ["simple"], default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] } curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
# Used for the hash to curve, along with the more complicated proofs
group = { version = "0.13", default-features = false }
dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.4", default-features = false }
# Needed for multisig
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true }
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", default-features = false, features = ["ed25519"], optional = true }
monero-io = { path = "io", version = "0.1", default-features = false } monero-io = { path = "io", version = "0.1", default-features = false }
monero-generators = { path = "generators", version = "0.4", default-features = false } monero-generators = { path = "generators", version = "0.4", default-features = false }
monero-primitives = { path = "primitives", version = "0.1", default-features = false } monero-primitives = { path = "primitives", version = "0.1", default-features = false }
@@ -57,22 +38,10 @@ hex = { version = "0.4", default-features = false, features = ["alloc"] }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] } serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] } serde_json = { version = "1", default-features = false, features = ["alloc"] }
base58-monero = { version = "2", default-features = false, features = ["check"] }
# Used for the provided HTTP RPC # Used for the provided HTTP RPC
digest_auth = { version = "0.3", default-features = false, optional = true } digest_auth = { version = "0.3", default-features = false, optional = true }
simple-request = { path = "../../common/request", version = "0.1", default-features = false, features = ["tls"], optional = true }
tokio = { version = "1", default-features = false, optional = true } tokio = { version = "1", default-features = false, optional = true }
[build-dependencies]
dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.4", default-features = false }
monero-generators = { path = "generators", version = "0.4", default-features = false }
[dev-dependencies]
tokio = { version = "1", features = ["sync", "macros"] }
frost = { package = "modular-frost", path = "../../crypto/frost", features = ["tests"] }
[features] [features]
std = [ std = [
"std-shims/std", "std-shims/std",
@@ -80,17 +49,8 @@ std = [
"thiserror", "thiserror",
"zeroize/std", "zeroize/std",
"subtle/std",
"rand_core/std", "rand_core/std",
"rand/std",
"rand_chacha/std",
"rand_distr/std",
"sha3/std",
"pbkdf2/std",
"transcript/std",
"monero-io/std", "monero-io/std",
"monero-generators/std", "monero-generators/std",
@@ -102,13 +62,9 @@ std = [
"hex/std", "hex/std",
"serde/std", "serde/std",
"serde_json/std", "serde_json/std",
"base58-monero/std",
] ]
compile-time-generators = ["curve25519-dalek/precomputed-tables", "monero-bulletproofs/compile-time-generators"] compile-time-generators = ["curve25519-dalek/precomputed-tables", "monero-bulletproofs/compile-time-generators"]
http-rpc = ["digest_auth", "simple-request", "tokio"] multisig = ["monero-clsag/multisig", "std"]
multisig = ["transcript", "frost", "monero-clsag/multisig", "std"] #binaries = ["tokio/rt-multi-thread", "tokio/macros", "http-rpc"]
binaries = ["tokio/rt-multi-thread", "tokio/macros", "http-rpc"] default = ["std"]
default = ["std", "http-rpc"]

View File

@@ -0,0 +1,46 @@
[package]
name = "monero-rpc"
version = "0.1.0"
description = "Trait for an RPC connection to a Monero daemon, built around monero-serai"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/rpc"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
std-shims = { path = "../../../common/std-shims", version = "^0.1.1", default-features = false }
async-trait = { version = "0.1", default-features = false }
thiserror = { version = "1", default-features = false, optional = true }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
hex = { version = "0.4", default-features = false, features = ["alloc"] }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
monero-serai = { path = "..", default-features = false }
[features]
std = [
"std-shims/std",
"thiserror",
"zeroize/std",
"hex/std",
"serde/std",
"serde_json/std",
"monero-serai/std",
]
default = ["std"]

21
coins/monero/rpc/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 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

@@ -0,0 +1,6 @@
# Monero RPC
Trait for an RPC connection to a Monero daemon, built around monero-serai.
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -0,0 +1,26 @@
[package]
name = "monero-simple-request-rpc"
version = "0.1.0"
description = "RPC connection to a Monero daemon via simple-request, built around monero-serai"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/rpc/simple-request"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
async-trait = { version = "0.1", default-features = false }
hex = { version = "0.4", default-features = false, features = ["alloc"] }
digest_auth = { version = "0.3", default-features = false }
simple-request = { path = "../../../../common/request", version = "0.1", default-features = false, features = ["tls"] }
tokio = { version = "1", default-features = false }
monero-rpc = { path = "..", default-features = false, features = ["std"] }

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 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

@@ -0,0 +1,3 @@
# Monero simple-request RPC
RPC connection to a Monero daemon via simple-request, built around monero-serai.

View File

@@ -1,3 +1,7 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
// #![deny(missing_docs)] // TODO
use std::{sync::Arc, io::Read, time::Duration}; use std::{sync::Arc, io::Read, time::Duration};
use async_trait::async_trait; use async_trait::async_trait;
@@ -10,7 +14,7 @@ use simple_request::{
Response, Client, Response, Client,
}; };
use crate::rpc::{RpcError, RpcConnection, Rpc}; use monero_rpc::{RpcError, RpcConnection, Rpc};
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30); const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
@@ -33,13 +37,13 @@ enum Authentication {
/// ///
/// Requires tokio. /// Requires tokio.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct HttpRpc { pub struct SimpleRequestRpc {
authentication: Authentication, authentication: Authentication,
url: String, url: String,
request_timeout: Duration, request_timeout: Duration,
} }
impl HttpRpc { impl SimpleRequestRpc {
fn digest_auth_challenge( fn digest_auth_challenge(
response: &Response, response: &Response,
) -> Result<Option<(WwwAuthenticateHeader, u64)>, RpcError> { ) -> Result<Option<(WwwAuthenticateHeader, u64)>, RpcError> {
@@ -60,7 +64,7 @@ impl HttpRpc {
/// ///
/// A daemon requiring authentication can be used via including the username and password in the /// A daemon requiring authentication can be used via including the username and password in the
/// URL. /// URL.
pub async fn new(url: String) -> Result<Rpc<HttpRpc>, RpcError> { pub async fn new(url: String) -> Result<Rpc<SimpleRequestRpc>, RpcError> {
Self::with_custom_timeout(url, DEFAULT_TIMEOUT).await Self::with_custom_timeout(url, DEFAULT_TIMEOUT).await
} }
@@ -71,7 +75,7 @@ impl HttpRpc {
pub async fn with_custom_timeout( pub async fn with_custom_timeout(
mut url: String, mut url: String,
request_timeout: Duration, request_timeout: Duration,
) -> Result<Rpc<HttpRpc>, RpcError> { ) -> Result<Rpc<SimpleRequestRpc>, RpcError> {
let authentication = if url.contains('@') { let authentication = if url.contains('@') {
// Parse out the username and password // Parse out the username and password
let url_clone = url; let url_clone = url;
@@ -119,11 +123,11 @@ impl HttpRpc {
Authentication::Unauthenticated(Client::with_connection_pool()) Authentication::Unauthenticated(Client::with_connection_pool())
}; };
Ok(Rpc(HttpRpc { authentication, url, request_timeout })) Ok(Rpc::new(SimpleRequestRpc { authentication, url, request_timeout }))
} }
} }
impl HttpRpc { impl SimpleRequestRpc {
async fn inner_post(&self, route: &str, body: Vec<u8>) -> Result<Vec<u8>, RpcError> { async fn inner_post(&self, route: &str, body: Vec<u8>) -> Result<Vec<u8>, RpcError> {
let request_fn = |uri| { let request_fn = |uri| {
Request::post(uri) Request::post(uri)
@@ -277,7 +281,7 @@ impl HttpRpc {
} }
#[async_trait] #[async_trait]
impl RpcConnection for HttpRpc { impl RpcConnection for SimpleRequestRpc {
async fn post(&self, route: &str, body: Vec<u8>) -> Result<Vec<u8>, RpcError> { async fn post(&self, route: &str, body: Vec<u8>) -> Result<Vec<u8>, RpcError> {
tokio::time::timeout(self.request_timeout, self.inner_post(route, body)) tokio::time::timeout(self.request_timeout, self.inner_post(route, body))
.await .await

View File

@@ -1,3 +1,8 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
// #![deny(missing_docs)] // TODO
#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt::Debug; use core::fmt::Debug;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::boxed::Box; use alloc::boxed::Box;
@@ -7,33 +12,72 @@ use std_shims::{
string::{String, ToString}, string::{String, ToString},
}; };
use zeroize::Zeroize;
use async_trait::async_trait; use async_trait::async_trait;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use monero_io::decompress_point;
use serde::{Serialize, Deserialize, de::DeserializeOwned}; use serde::{Serialize, Deserialize, de::DeserializeOwned};
use serde_json::{Value, json}; use serde_json::{Value, json};
use crate::{ use monero_serai::{
io::*,
Protocol, Protocol,
serialize::*,
transaction::{Input, Timelock, Transaction}, transaction::{Input, Timelock, Transaction},
block::Block, block::Block,
wallet::{FeePriority, Fee},
}; };
#[cfg(feature = "http-rpc")]
mod http;
#[cfg(feature = "http-rpc")]
pub use http::*;
// Number of blocks the fee estimate will be valid for // Number of blocks the fee estimate will be valid for
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/ // https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
// src/wallet/wallet2.cpp#L121 // src/wallet/wallet2.cpp#L121
const GRACE_BLOCKS_FOR_FEE_ESTIMATE: u64 = 10; const GRACE_BLOCKS_FOR_FEE_ESTIMATE: u64 = 10;
/// Fee struct, defined as a per-unit cost and a mask for rounding purposes.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct Fee {
pub per_weight: u64,
pub mask: u64,
}
impl Fee {
pub fn calculate_fee_from_weight(&self, weight: usize) -> u64 {
let fee = (((self.per_weight * u64::try_from(weight).unwrap()) + self.mask - 1) / self.mask) *
self.mask;
debug_assert_eq!(weight, self.calculate_weight_from_fee(fee), "Miscalculated weight from fee");
fee
}
pub fn calculate_weight_from_fee(&self, fee: u64) -> usize {
usize::try_from(fee / self.per_weight).unwrap()
}
}
/// Fee priority, determining how quickly a transaction is included in a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
pub enum FeePriority {
Unimportant,
Normal,
Elevated,
Priority,
Custom { priority: u32 },
}
/// https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/
/// src/simplewallet/simplewallet.cpp#L161
impl FeePriority {
pub(crate) fn fee_priority(&self) -> u32 {
match self {
FeePriority::Unimportant => 1,
FeePriority::Normal => 2,
FeePriority::Elevated => 3,
FeePriority::Priority => 4,
FeePriority::Custom { priority, .. } => *priority,
}
}
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct EmptyResponse {} pub struct EmptyResponse {}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@@ -132,6 +176,10 @@ pub trait RpcConnection: Clone + Debug {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Rpc<R: RpcConnection>(R); pub struct Rpc<R: RpcConnection>(R);
impl<R: RpcConnection> Rpc<R> { impl<R: RpcConnection> Rpc<R> {
pub fn new(connection: R) -> Self {
Self(connection)
}
/// Perform a RPC call to the specified route with the provided parameters. /// Perform a RPC call to the specified route with the provided parameters.
/// ///
/// This is NOT a JSON-RPC call. They use a route of "json_rpc" and are available via /// This is NOT a JSON-RPC call. They use a route of "json_rpc" and are available via

View File

@@ -1,3 +1,4 @@
/* TODO
#[cfg(feature = "binaries")] #[cfg(feature = "binaries")]
mod binaries { mod binaries {
pub(crate) use std::sync::Arc; pub(crate) use std::sync::Arc;
@@ -12,14 +13,14 @@ mod binaries {
ringct::{RctPrunable, bulletproofs::BatchVerifier}, ringct::{RctPrunable, bulletproofs::BatchVerifier},
transaction::{Input, Transaction}, transaction::{Input, Transaction},
block::Block, block::Block,
rpc::{RpcError, Rpc, HttpRpc}, rpc::{RpcError, Rpc, SimpleRequestRpc},
}; };
pub(crate) use monero_io::decompress_point; pub(crate) use monero_io::decompress_point;
pub(crate) use tokio::task::JoinHandle; pub(crate) use tokio::task::JoinHandle;
pub(crate) async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) { pub(crate) async fn check_block(rpc: Arc<Rpc<SimpleRequestRpc>>, block_i: usize) {
let hash = loop { let hash = loop {
match rpc.get_block_hash(block_i).await { match rpc.get_block_hash(block_i).await {
Ok(hash) => break hash, Ok(hash) => break hash,
@@ -157,7 +158,7 @@ mod binaries {
} }
async fn get_outs( async fn get_outs(
rpc: &Rpc<HttpRpc>, rpc: &Rpc<SimpleRequestRpc>,
amount: u64, amount: u64,
indexes: &[u64], indexes: &[u64],
) -> Vec<[EdwardsPoint; 2]> { ) -> Vec<[EdwardsPoint; 2]> {
@@ -268,9 +269,9 @@ async fn main() {
let nodes = if specified_nodes.is_empty() { default_nodes } else { specified_nodes }; let nodes = if specified_nodes.is_empty() { default_nodes } else { specified_nodes };
let rpc = |url: String| async move { let rpc = |url: String| async move {
HttpRpc::new(url.clone()) SimpleRequestRpc::new(url.clone())
.await .await
.unwrap_or_else(|_| panic!("couldn't create HttpRpc connected to {url}")) .unwrap_or_else(|_| panic!("couldn't create SimpleRequestRpc connected to {url}"))
}; };
let main_rpc = rpc(nodes[0].clone()).await; let main_rpc = rpc(nodes[0].clone()).await;
let mut rpcs = vec![]; let mut rpcs = vec![];
@@ -315,3 +316,6 @@ async fn main() {
fn main() { fn main() {
panic!("To run binaries, please build with `--feature binaries`."); panic!("To run binaries, please build with `--feature binaries`.");
} }
*/
fn main() {}

View File

@@ -27,14 +27,6 @@ pub mod transaction;
/// Block structs. /// Block structs.
pub mod block; pub mod block;
/// Monero daemon RPC interface.
pub mod rpc;
/// Wallet functionality, enabling scanning and sending transactions.
pub mod wallet;
#[cfg(test)]
mod tests;
pub const DEFAULT_LOCK_WINDOW: usize = 10; pub const DEFAULT_LOCK_WINDOW: usize = 10;
pub const COINBASE_LOCK_WINDOW: usize = 60; pub const COINBASE_LOCK_WINDOW: usize = 60;
pub const BLOCK_TIME: usize = 120; pub const BLOCK_TIME: usize = 120;
@@ -106,7 +98,7 @@ impl Protocol {
} }
} }
pub(crate) fn write<W: stdio::Write>(&self, w: &mut W) -> stdio::Result<()> { pub fn write<W: stdio::Write>(&self, w: &mut W) -> stdio::Result<()> {
match self { match self {
Protocol::v14 => w.write_all(&[0, 14]), Protocol::v14 => w.write_all(&[0, 14]),
Protocol::v16 => w.write_all(&[0, 16]), Protocol::v16 => w.write_all(&[0, 16]),
@@ -122,7 +114,7 @@ impl Protocol {
} }
} }
pub(crate) fn read<R: stdio::Read>(r: &mut R) -> stdio::Result<Protocol> { pub fn read<R: stdio::Read>(r: &mut R) -> stdio::Result<Protocol> {
Ok(match read_byte(r)? { Ok(match read_byte(r)? {
// Monero protocol // Monero protocol
0 => match read_byte(r)? { 0 => match read_byte(r)? {

View File

@@ -134,7 +134,7 @@ pub struct RctBase {
} }
impl RctBase { impl RctBase {
pub(crate) fn fee_weight(outputs: usize, fee: u64) -> usize { pub fn fee_weight(outputs: usize, fee: u64) -> usize {
// 1 byte for the RCT signature type // 1 byte for the RCT signature type
1 + (outputs * (8 + 32)) + varint_len(fee) 1 + (outputs * (8 + 32)) + varint_len(fee)
} }
@@ -227,7 +227,7 @@ pub enum RctPrunable {
} }
impl RctPrunable { impl RctPrunable {
pub(crate) fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize) -> usize { pub fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize) -> usize {
// 1 byte for number of BPs (technically a VarInt, yet there's always just zero or one) // 1 byte for number of BPs (technically a VarInt, yet there's always just zero or one)
1 + Bulletproof::fee_weight(protocol.bp_plus(), outputs) + 1 + Bulletproof::fee_weight(protocol.bp_plus(), outputs) +
(inputs * (Clsag::fee_weight(protocol.ring_len()) + 32)) (inputs * (Clsag::fee_weight(protocol.ring_len()) + 32))
@@ -383,7 +383,7 @@ impl RctSignatures {
} }
} }
pub(crate) fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize, fee: u64) -> usize { pub fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize, fee: u64) -> usize {
RctBase::fee_weight(outputs, fee) + RctPrunable::fee_weight(protocol, inputs, outputs) RctBase::fee_weight(outputs, fee) + RctPrunable::fee_weight(protocol, inputs, outputs)
} }

View File

@@ -23,7 +23,7 @@ pub enum Input {
} }
impl Input { impl Input {
pub(crate) fn fee_weight(offsets_weight: usize) -> usize { pub fn fee_weight(offsets_weight: usize) -> usize {
// Uses 1 byte for the input type // Uses 1 byte for the input type
// Uses 1 byte for the VarInt amount due to amount being 0 // Uses 1 byte for the VarInt amount due to amount being 0
1 + 1 + offsets_weight + 32 1 + 1 + offsets_weight + 32
@@ -82,7 +82,7 @@ pub struct Output {
} }
impl Output { impl Output {
pub(crate) fn fee_weight(view_tags: bool) -> usize { pub fn fee_weight(view_tags: bool) -> usize {
// Uses 1 byte for the output type // Uses 1 byte for the output type
// Uses 1 byte for the VarInt amount due to amount being 0 // Uses 1 byte for the VarInt amount due to amount being 0
1 + 1 + 32 + if view_tags { 1 } else { 0 } 1 + 1 + 32 + if view_tags { 1 } else { 0 }
@@ -182,7 +182,7 @@ pub struct TransactionPrefix {
} }
impl TransactionPrefix { impl TransactionPrefix {
pub(crate) fn fee_weight( pub fn fee_weight(
decoy_weights: &[usize], decoy_weights: &[usize],
outputs: usize, outputs: usize,
view_tags: bool, view_tags: bool,
@@ -254,7 +254,7 @@ pub struct Transaction {
} }
impl Transaction { impl Transaction {
pub(crate) fn fee_weight( pub fn fee_weight(
protocol: Protocol, protocol: Protocol,
decoy_weights: &[usize], decoy_weights: &[usize],
outputs: usize, outputs: usize,

View File

@@ -0,0 +1,88 @@
[package]
name = "monero-wallet"
version = "0.1.0"
description = "Wallet functionality for the Monero protocol, built around monero-serai"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/wallet"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
std-shims = { path = "../../../common/std-shims", version = "^0.1.1", default-features = false }
async-trait = { version = "0.1", default-features = false }
thiserror = { version = "1", default-features = false, optional = true }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
subtle = { version = "^2.4", default-features = false }
rand_core = { version = "0.6", default-features = false }
# Used to send transactions
rand = { version = "0.8", default-features = false }
rand_chacha = { version = "0.3", default-features = false }
# Used to select decoys
rand_distr = { version = "0.4", default-features = false }
sha3 = { version = "0.10", default-features = false }
pbkdf2 = { version = "0.12", features = ["simple"], default-features = false }
group = { version = "0.13", default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize", "group"] }
# Multisig dependencies
transcript = { package = "flexible-transcript", path = "../../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true }
dalek-ff-group = { path = "../../../crypto/dalek-ff-group", version = "0.4", default-features = false, optional = true }
frost = { package = "modular-frost", path = "../../../crypto/frost", default-features = false, features = ["ed25519"], optional = true }
hex = { version = "0.4", default-features = false, features = ["alloc"] }
base58-monero = { version = "2", default-features = false, features = ["check"] }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] }
monero-serai = { path = "..", default-features = false }
monero-rpc = { path = "../rpc", default-features = false }
[dev-dependencies]
hex-literal = "0.4"
frost = { package = "modular-frost", path = "../../../crypto/frost", default-features = false, features = ["ed25519", "tests"] }
tokio = { version = "1", features = ["sync", "macros"] }
monero-simple-request-rpc = { path = "../rpc/simple-request", default-features = false }
[features]
std = [
"std-shims/std",
"thiserror",
"zeroize/std",
"subtle/std",
"rand_core/std",
"rand/std",
"rand_chacha/std",
"rand_distr/std",
"sha3/std",
"pbkdf2/std",
"hex/std",
"base58-monero/std",
"serde/std",
"serde_json/std",
"monero-serai/std",
"monero-rpc/std",
]
multisig = ["transcript", "dalek-ff-group", "frost", "monero-serai/multisig", "std"]
default = ["std"]

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 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

@@ -0,0 +1,6 @@
# Monero Wallet
Wallet functionality for the Monero protocol, built around monero-serai.
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -5,7 +5,7 @@ use zeroize::Zeroize;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use crate::io::decompress_point; use monero_serai::io::decompress_point;
use base58_monero::base58::{encode_check, decode_check}; use base58_monero::base58::{encode_check, decode_check};

View File

@@ -9,11 +9,9 @@ use rand_distr::num_traits::Float;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use crate::{ use monero_serai::{DEFAULT_LOCK_WINDOW, COINBASE_LOCK_WINDOW, BLOCK_TIME};
wallet::SpendableOutput, use monero_rpc::{RpcError, RpcConnection, Rpc};
rpc::{RpcError, RpcConnection, Rpc}, use crate::SpendableOutput;
DEFAULT_LOCK_WINDOW, COINBASE_LOCK_WINDOW, BLOCK_TIME,
};
const RECENT_WINDOW: usize = 15; const RECENT_WINDOW: usize = 15;
const BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME; const BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME;
@@ -274,7 +272,7 @@ async fn select_decoys<R: RngCore + CryptoRng, RPC: RpcConnection>(
Ok(res) Ok(res)
} }
pub use monero_primitives::Decoys; pub use monero_serai::primitives::Decoys;
// TODO: Remove this trait // TODO: Remove this trait
#[cfg(feature = "std")] #[cfg(feature = "std")]

View File

@@ -8,7 +8,7 @@ use zeroize::Zeroize;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use crate::io::*; use monero_serai::io::*;
pub const MAX_TX_EXTRA_PADDING_COUNT: usize = 255; pub const MAX_TX_EXTRA_PADDING_COUNT: usize = 255;
pub const MAX_TX_EXTRA_NONCE_SIZE: usize = 255; pub const MAX_TX_EXTRA_NONCE_SIZE: usize = 255;
@@ -209,7 +209,7 @@ impl Extra {
} }
#[rustfmt::skip] #[rustfmt::skip]
pub(crate) fn fee_weight( pub fn fee_weight(
outputs: usize, outputs: usize,
additional: bool, additional: bool,
payment_id: bool, payment_id: bool,

View File

@@ -1,3 +1,8 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
// #![deny(missing_docs)] // TODO
#![cfg_attr(not(feature = "std"), no_std)]
use core::ops::Deref; use core::ops::Deref;
use std_shims::collections::{HashSet, HashMap}; use std_shims::collections::{HashSet, HashMap};
@@ -9,12 +14,15 @@ use curve25519_dalek::{
edwards::{EdwardsPoint, CompressedEdwardsY}, edwards::{EdwardsPoint, CompressedEdwardsY},
}; };
use crate::{ use monero_serai::{
io::write_varint, io::write_varint,
primitives::{Commitment, keccak256, keccak256_to_scalar}, primitives::{Commitment, keccak256, keccak256_to_scalar},
ringct::EncryptedAmount, ringct::EncryptedAmount,
transaction::Input, transaction::Input,
}; };
pub use monero_serai as monero;
pub use monero_rpc as rpc;
pub mod extra; pub mod extra;
pub(crate) use extra::{PaymentId, ExtraField, Extra}; pub(crate) use extra::{PaymentId, ExtraField, Extra};
@@ -33,7 +41,7 @@ pub use scan::{ReceivedOutput, SpendableOutput, Timelocked};
pub mod decoys; pub mod decoys;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
pub mod decoys { pub mod decoys {
pub use monero_primitives::Decoys; pub use monero_serai::primitives::Decoys;
pub trait DecoySelection {} pub trait DecoySelection {}
} }
pub use decoys::{DecoySelection, Decoys}; pub use decoys::{DecoySelection, Decoys};
@@ -47,6 +55,9 @@ pub(crate) use send::InternalPayment;
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
pub use send::TransactionMachine; pub use send::TransactionMachine;
#[cfg(test)]
mod tests;
fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> core::cmp::Ordering { fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> core::cmp::Ordering {
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse() x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
} }
@@ -109,12 +120,19 @@ pub(crate) fn compact_amount_encryption(amount: u64, key: Scalar) -> [u8; 8] {
(amount ^ u64::from_le_bytes(keccak256(amount_mask)[.. 8].try_into().unwrap())).to_le_bytes() (amount ^ u64::from_le_bytes(keccak256(amount_mask)[.. 8].try_into().unwrap())).to_le_bytes()
} }
impl EncryptedAmount { pub trait EncryptedAmountExt {
/// Decrypt an EncryptedAmount into the Commitment it encrypts. /// Decrypt an EncryptedAmount into the Commitment it encrypts.
/// ///
/// The caller must verify the decrypted Commitment matches with the actual Commitment used /// The caller must verify the decrypted Commitment matches with the actual Commitment used
/// within in the Monero protocol. /// within in the Monero protocol.
pub fn decrypt(&self, key: Scalar) -> Commitment { fn decrypt(&self, key: Scalar) -> Commitment;
}
impl EncryptedAmountExt for EncryptedAmount {
/// Decrypt an EncryptedAmount into the Commitment it encrypts.
///
/// The caller must verify the decrypted Commitment matches with the actual Commitment used
/// within in the Monero protocol.
fn decrypt(&self, key: Scalar) -> Commitment {
match self { match self {
// TODO: Add a test vector for this // TODO: Add a test vector for this
EncryptedAmount::Original { mask, amount } => { EncryptedAmount::Original { mask, amount } => {

View File

@@ -9,15 +9,15 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint}; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint};
use monero_io::decompress_point; use monero_rpc::{RpcError, RpcConnection, Rpc};
use monero_serai::{
use crate::{
io::*, io::*,
primitives::Commitment, primitives::Commitment,
transaction::{Input, Timelock, Transaction}, transaction::{Input, Timelock, Transaction},
block::Block, block::Block,
rpc::{RpcError, RpcConnection, Rpc}, };
wallet::{PaymentId, Extra, address::SubaddressIndex, Scanner, uniqueness, shared_key}, use crate::{
PaymentId, Extra, address::SubaddressIndex, Scanner, EncryptedAmountExt, uniqueness, shared_key,
}; };
/// An absolute output ID, defined as its transaction hash and output index. /// An absolute output ID, defined as its transaction hash and output index.

View File

@@ -11,7 +11,7 @@ use rand_core::{RngCore, CryptoRng};
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
use crate::wallet::seed::SeedError; use crate::seed::SeedError;
pub(crate) const CLASSIC_SEED_LENGTH: usize = 24; pub(crate) const CLASSIC_SEED_LENGTH: usize = 24;
pub(crate) const CLASSIC_SEED_LENGTH_WITH_CHECKSUM: usize = 25; pub(crate) const CLASSIC_SEED_LENGTH_WITH_CHECKSUM: usize = 25;

View File

@@ -2,12 +2,10 @@ use std::sync::{Arc, RwLock};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use monero_serai::Protocol;
use crate::{ use crate::{
Protocol, address::MoneroAddress, Fee, SpendableOutput, Change, Decoys, SignableTransaction,
wallet::{ TransactionError, extra::MAX_ARBITRARY_DATA_SIZE,
address::MoneroAddress, Fee, SpendableOutput, Change, Decoys, SignableTransaction,
TransactionError, extra::MAX_ARBITRARY_DATA_SIZE,
},
}; };
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]

View File

@@ -17,14 +17,13 @@ use curve25519_dalek::{
scalar::Scalar, scalar::Scalar,
edwards::EdwardsPoint, edwards::EdwardsPoint,
}; };
use dalek_ff_group as dfg;
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
use frost::FrostError; use frost::FrostError;
use monero_io::varint_len; use monero_rpc::RpcError;
pub use monero_rpc::{Fee, FeePriority};
use crate::{ use monero_serai::{
io::*, io::*,
primitives::{Commitment, keccak256}, primitives::{Commitment, keccak256},
Protocol, Protocol,
@@ -35,13 +34,12 @@ use crate::{
RctBase, RctPrunable, RctSignatures, RctBase, RctPrunable, RctSignatures,
}, },
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction}, transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
rpc::RpcError, };
wallet::{ use crate::{
address::{Network, AddressSpec, MoneroAddress}, address::{Network, AddressSpec, MoneroAddress},
ViewPair, SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort, uniqueness, ViewPair, SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort, uniqueness,
shared_key, commitment_mask, compact_amount_encryption, shared_key, commitment_mask, compact_amount_encryption,
extra::{ARBITRARY_DATA_MARKER, MAX_ARBITRARY_DATA_SIZE}, extra::{ARBITRARY_DATA_MARKER, MAX_ARBITRARY_DATA_SIZE},
},
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -53,7 +51,7 @@ pub use builder::SignableTransactionBuilder;
mod multisig; mod multisig;
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
pub use multisig::TransactionMachine; pub use multisig::TransactionMachine;
use crate::ringct::EncryptedAmount; use monero_serai::ringct::EncryptedAmount;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
@@ -264,51 +262,6 @@ fn calculate_weight_and_fee(
(weight, fee) (weight, fee)
} }
/// Fee struct, defined as a per-unit cost and a mask for rounding purposes.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct Fee {
pub per_weight: u64,
pub mask: u64,
}
impl Fee {
pub fn calculate_fee_from_weight(&self, weight: usize) -> u64 {
let fee = (((self.per_weight * u64::try_from(weight).unwrap()) + self.mask - 1) / self.mask) *
self.mask;
debug_assert_eq!(weight, self.calculate_weight_from_fee(fee), "Miscalculated weight from fee");
fee
}
pub fn calculate_weight_from_fee(&self, fee: u64) -> usize {
usize::try_from(fee / self.per_weight).unwrap()
}
}
/// Fee priority, determining how quickly a transaction is included in a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
pub enum FeePriority {
Unimportant,
Normal,
Elevated,
Priority,
Custom { priority: u32 },
}
/// https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/
/// src/simplewallet/simplewallet.cpp#L161
impl FeePriority {
pub(crate) fn fee_priority(&self) -> u32 {
match self {
FeePriority::Unimportant => 1,
FeePriority::Normal => 2,
FeePriority::Elevated => 3,
FeePriority::Priority => 4,
FeePriority::Custom { priority, .. } => *priority,
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub(crate) enum InternalPayment { pub(crate) enum InternalPayment {
Payment((MoneroAddress, u64), bool), Payment((MoneroAddress, u64), bool),
@@ -694,7 +647,7 @@ impl SignableTransaction {
} else { } else {
// If this used tx_key, randomize its R // If this used tx_key, randomize its R
// This is so when extra is created, there's a distinct R for it to use // This is so when extra is created, there's a distinct R for it to use
output.R = dfg::EdwardsPoint::random(&mut rng).0; output.R = EdwardsPoint::random(&mut rng);
} }
(output, payment_id) (output, payment_id)
} }

View File

@@ -24,14 +24,14 @@ use frost::{
}, },
}; };
use crate::{ use monero_serai::{
ringct::{ ringct::{
clsag::{ClsagContext, ClsagMultisigMaskSender, ClsagAddendum, ClsagMultisig}, clsag::{ClsagContext, ClsagMultisigMaskSender, ClsagAddendum, ClsagMultisig},
RctPrunable, RctPrunable,
}, },
transaction::{Input, Transaction}, transaction::{Input, Transaction},
wallet::{TransactionError, InternalPayment, SignableTransaction, key_image_sort, uniqueness},
}; };
use crate::{TransactionError, InternalPayment, SignableTransaction, key_image_sort, uniqueness};
/// FROST signing machine to produce a signed transaction. /// FROST signing machine to produce a signed transaction.
pub struct TransactionMachine { pub struct TransactionMachine {

View File

@@ -4,9 +4,9 @@ use rand_core::{RngCore, OsRng};
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
use monero_io::decompress_point; use monero_serai::io::decompress_point;
use crate::wallet::address::{Network, AddressType, AddressMeta, MoneroAddress}; use crate::address::{Network, AddressType, AddressMeta, MoneroAddress};
const SPEND: [u8; 32] = hex!("f8631661f6ab4e6fda310c797330d86e23a682f20d5bc8cc27b18051191f16d7"); const SPEND: [u8; 32] = hex!("f8631661f6ab4e6fda310c797330d86e23a682f20d5bc8cc27b18051191f16d7");
const VIEW: [u8; 32] = hex!("4a1535063ad1fee2dabbf909d4fd9a873e29541b401f0944754e17c9a41820ce"); const VIEW: [u8; 32] = hex!("4a1535063ad1fee2dabbf909d4fd9a873e29541b401f0944754e17c9a41820ce");

View File

@@ -1,10 +1,8 @@
use crate::{
wallet::{ExtraField, Extra, extra::MAX_TX_EXTRA_PADDING_COUNT},
serialize::write_varint,
};
use curve25519_dalek::edwards::{EdwardsPoint, CompressedEdwardsY}; use curve25519_dalek::edwards::{EdwardsPoint, CompressedEdwardsY};
use monero_serai::io::write_varint;
use crate::{ExtraField, Extra, extra::MAX_TX_EXTRA_PADDING_COUNT};
// Borrowed tests from // Borrowed tests from
// https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/ // https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/
// tests/unit_tests/test_tx_utils.cpp // tests/unit_tests/test_tx_utils.cpp

View File

@@ -4,9 +4,9 @@ use rand_core::OsRng;
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
use monero_primitives::keccak256; use monero_serai::primitives::keccak256;
use crate::wallet::seed::{ use crate::seed::{
Seed, SeedType, SeedError, Seed, SeedType, SeedError,
classic::{self, trim_by_lang}, classic::{self, trim_by_lang},
polyseed, polyseed,

View File

@@ -1,7 +1,5 @@
use monero_serai::{ use monero_serai::transaction::Transaction;
transaction::Transaction, use monero_wallet::{TransactionError, extra::MAX_ARBITRARY_DATA_SIZE};
wallet::{TransactionError, extra::MAX_ARBITRARY_DATA_SIZE},
};
mod runner; mod runner;

View File

@@ -1,9 +1,6 @@
use monero_serai::{ use monero_rpc::{Rpc, OutputResponse};
transaction::Transaction, use monero_serai::{transaction::Transaction, Protocol, DEFAULT_LOCK_WINDOW};
wallet::SpendableOutput, use monero_wallet::SpendableOutput;
rpc::{Rpc, OutputResponse},
Protocol, DEFAULT_LOCK_WINDOW,
};
mod runner; mod runner;

View File

@@ -1,11 +1,9 @@
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT; use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
use monero_serai::{ use monero_serai::transaction::Transaction;
transaction::Transaction, use monero_wallet::{
wallet::{ Eventuality,
Eventuality, address::{AddressType, AddressMeta, MoneroAddress},
address::{AddressType, AddressMeta, MoneroAddress},
},
}; };
mod runner; mod runner;

View File

@@ -8,15 +8,13 @@ use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use monero_serai::{ use monero_rpc::Rpc;
rpc::{HttpRpc, Rpc}, use monero_simple_request_rpc::SimpleRequestRpc;
wallet::{ use monero_serai::{transaction::Transaction, DEFAULT_LOCK_WINDOW};
ViewPair, Scanner, use monero_wallet::{
address::{Network, AddressType, AddressSpec, AddressMeta, MoneroAddress}, ViewPair, Scanner,
SpendableOutput, Fee, address::{Network, AddressType, AddressSpec, AddressMeta, MoneroAddress},
}, SpendableOutput, Fee,
transaction::Transaction,
DEFAULT_LOCK_WINDOW,
}; };
pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) { pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
@@ -36,7 +34,7 @@ pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
// TODO: Support transactions already on-chain // TODO: Support transactions already on-chain
// TODO: Don't have a side effect of mining blocks more blocks than needed under race conditions // TODO: Don't have a side effect of mining blocks more blocks than needed under race conditions
pub async fn mine_until_unlocked(rpc: &Rpc<HttpRpc>, addr: &str, tx_hash: [u8; 32]) { pub async fn mine_until_unlocked(rpc: &Rpc<SimpleRequestRpc>, addr: &str, tx_hash: [u8; 32]) {
// mine until tx is in a block // mine until tx is in a block
let mut height = rpc.get_height().await.unwrap(); let mut height = rpc.get_height().await.unwrap();
let mut found = false; let mut found = false;
@@ -66,7 +64,7 @@ pub async fn mine_until_unlocked(rpc: &Rpc<HttpRpc>, addr: &str, tx_hash: [u8; 3
// Mines 60 blocks and returns an unlocked miner TX output. // Mines 60 blocks and returns an unlocked miner TX output.
#[allow(dead_code)] #[allow(dead_code)]
pub async fn get_miner_tx_output(rpc: &Rpc<HttpRpc>, view: &ViewPair) -> SpendableOutput { pub async fn get_miner_tx_output(rpc: &Rpc<SimpleRequestRpc>, view: &ViewPair) -> SpendableOutput {
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new())); let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
// Mine 60 blocks to unlock a miner TX // Mine 60 blocks to unlock a miner TX
@@ -92,8 +90,9 @@ pub fn check_weight_and_fee(tx: &Transaction, fee_rate: Fee) {
assert_eq!(fee, expected_fee); assert_eq!(fee, expected_fee);
} }
pub async fn rpc() -> Rpc<HttpRpc> { pub async fn rpc() -> Rpc<SimpleRequestRpc> {
let rpc = HttpRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap(); let rpc =
SimpleRequestRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap();
// Only run once // Only run once
if rpc.get_height().await.unwrap() != 1 { if rpc.get_height().await.unwrap() != 1 {
@@ -171,12 +170,10 @@ macro_rules! test {
tests::{THRESHOLD, key_gen}, tests::{THRESHOLD, key_gen},
}; };
use monero_serai::{ use monero_wallet::{
wallet::{ address::{Network, AddressSpec},
address::{Network, AddressSpec}, ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority,
ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority, SignableTransaction, SignableTransactionBuilder,
SignableTransaction, SignableTransactionBuilder,
},
}; };
use runner::{ use runner::{

View File

@@ -1,9 +1,7 @@
use rand::RngCore; use rand_core::RngCore;
use monero_serai::{ use monero_serai::transaction::Transaction;
transaction::Transaction, use monero_wallet::{address::SubaddressIndex, extra::PaymentId};
wallet::{address::SubaddressIndex, extra::PaymentId},
};
mod runner; mod runner;

View File

@@ -1,13 +1,11 @@
use rand_core::OsRng; use rand_core::OsRng;
use monero_serai::{ use monero_serai::{transaction::Transaction, Protocol};
transaction::Transaction, use monero_rpc::Rpc;
wallet::{ use monero_simple_request_rpc::SimpleRequestRpc;
extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput, DecoySelection, use monero_wallet::{
Decoys, SignableTransactionBuilder, extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput, DecoySelection, Decoys,
}, SignableTransactionBuilder,
rpc::{Rpc, HttpRpc},
Protocol,
}; };
mod runner; mod runner;
@@ -15,7 +13,7 @@ mod runner;
// Set up inputs, select decoys, then add them to the TX builder // Set up inputs, select decoys, then add them to the TX builder
async fn add_inputs( async fn add_inputs(
protocol: Protocol, protocol: Protocol,
rpc: &Rpc<HttpRpc>, rpc: &Rpc<SimpleRequestRpc>,
outputs: Vec<ReceivedOutput>, outputs: Vec<ReceivedOutput>,
builder: &mut SignableTransactionBuilder, builder: &mut SignableTransactionBuilder,
) { ) {
@@ -101,7 +99,7 @@ test!(
), ),
( (
|protocol, rpc: Rpc<_>, _, _, outputs: Vec<ReceivedOutput>| async move { |protocol, rpc: Rpc<_>, _, _, outputs: Vec<ReceivedOutput>| async move {
use monero_serai::wallet::FeePriority; use monero_wallet::FeePriority;
let change_view = ViewPair::new( let change_view = ViewPair::new(
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE, &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
@@ -290,7 +288,7 @@ test!(
), ),
( (
|protocol, rpc: Rpc<_>, _, addr, outputs: Vec<ReceivedOutput>| async move { |protocol, rpc: Rpc<_>, _, addr, outputs: Vec<ReceivedOutput>| async move {
use monero_serai::wallet::FeePriority; use monero_wallet::FeePriority;
let mut builder = SignableTransactionBuilder::new( let mut builder = SignableTransactionBuilder::new(
protocol, protocol,

View File

@@ -5,19 +5,18 @@ use rand_core::{OsRng, RngCore};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
use monero_serai::{ use monero_serai::transaction::Transaction;
transaction::Transaction, use monero_rpc::{EmptyResponse, Rpc};
rpc::{EmptyResponse, HttpRpc, Rpc}, use monero_simple_request_rpc::SimpleRequestRpc;
wallet::{ use monero_wallet::{
address::{Network, AddressSpec, SubaddressIndex, MoneroAddress}, address::{Network, AddressSpec, SubaddressIndex, MoneroAddress},
extra::{MAX_TX_EXTRA_NONCE_SIZE, Extra, PaymentId}, extra::{MAX_TX_EXTRA_NONCE_SIZE, Extra, PaymentId},
Scanner, Scanner,
},
}; };
mod runner; mod runner;
async fn make_integrated_address(rpc: &Rpc<HttpRpc>, payment_id: [u8; 8]) -> String { async fn make_integrated_address(rpc: &Rpc<SimpleRequestRpc>, payment_id: [u8; 8]) -> String {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct IntegratedAddressResponse { struct IntegratedAddressResponse {
integrated_address: String, integrated_address: String,
@@ -34,8 +33,8 @@ async fn make_integrated_address(rpc: &Rpc<HttpRpc>, payment_id: [u8; 8]) -> Str
res.integrated_address res.integrated_address
} }
async fn initialize_rpcs() -> (Rpc<HttpRpc>, Rpc<HttpRpc>, String) { async fn initialize_rpcs() -> (Rpc<SimpleRequestRpc>, Rpc<SimpleRequestRpc>, String) {
let wallet_rpc = HttpRpc::new("http://127.0.0.1:18082".to_string()).await.unwrap(); let wallet_rpc = SimpleRequestRpc::new("http://127.0.0.1:18082".to_string()).await.unwrap();
let daemon_rpc = runner::rpc().await; let daemon_rpc = runner::rpc().await;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@@ -174,7 +173,7 @@ test!(
.add_payment(MoneroAddress::from_str(Network::Mainnet, &wallet_rpc_addr).unwrap(), 1000000); .add_payment(MoneroAddress::from_str(Network::Mainnet, &wallet_rpc_addr).unwrap(), 1000000);
(builder.build().unwrap(), wallet_rpc) (builder.build().unwrap(), wallet_rpc)
}, },
|_, tx: Transaction, _, data: Rpc<HttpRpc>| async move { |_, tx: Transaction, _, data: Rpc<SimpleRequestRpc>| async move {
// confirm receipt // confirm receipt
let _: EmptyResponse = data.json_rpc_call("refresh", None).await.unwrap(); let _: EmptyResponse = data.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data let transfer: TransfersResponse = data
@@ -208,7 +207,7 @@ test!(
.add_payment(MoneroAddress::from_str(Network::Mainnet, &addr.address).unwrap(), 1000000); .add_payment(MoneroAddress::from_str(Network::Mainnet, &addr.address).unwrap(), 1000000);
(builder.build().unwrap(), (wallet_rpc, addr.account_index)) (builder.build().unwrap(), (wallet_rpc, addr.account_index))
}, },
|_, tx: Transaction, _, data: (Rpc<HttpRpc>, u32)| async move { |_, tx: Transaction, _, data: (Rpc<SimpleRequestRpc>, u32)| async move {
// confirm receipt // confirm receipt
let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap(); let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data let transfer: TransfersResponse = data
@@ -260,7 +259,7 @@ test!(
]); ]);
(builder.build().unwrap(), (wallet_rpc, daemon_rpc, addrs.address_index)) (builder.build().unwrap(), (wallet_rpc, daemon_rpc, addrs.address_index))
}, },
|_, tx: Transaction, _, data: (Rpc<HttpRpc>, Rpc<HttpRpc>, u32)| async move { |_, tx: Transaction, _, data: (Rpc<SimpleRequestRpc>, Rpc<SimpleRequestRpc>, u32)| async move {
// confirm receipt // confirm receipt
let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap(); let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data let transfer: TransfersResponse = data
@@ -305,7 +304,7 @@ test!(
builder.add_payment(MoneroAddress::from_str(Network::Mainnet, &addr).unwrap(), 1000000); builder.add_payment(MoneroAddress::from_str(Network::Mainnet, &addr).unwrap(), 1000000);
(builder.build().unwrap(), (wallet_rpc, payment_id)) (builder.build().unwrap(), (wallet_rpc, payment_id))
}, },
|_, tx: Transaction, _, data: (Rpc<HttpRpc>, [u8; 8])| async move { |_, tx: Transaction, _, data: (Rpc<SimpleRequestRpc>, [u8; 8])| async move {
// confirm receipt // confirm receipt
let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap(); let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data let transfer: TransfersResponse = data
@@ -340,7 +339,7 @@ test!(
(builder.build().unwrap(), wallet_rpc) (builder.build().unwrap(), wallet_rpc)
}, },
|_, tx: Transaction, _, data: Rpc<HttpRpc>| async move { |_, tx: Transaction, _, data: Rpc<SimpleRequestRpc>| async move {
// confirm receipt // confirm receipt
let _: EmptyResponse = data.json_rpc_call("refresh", None).await.unwrap(); let _: EmptyResponse = data.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data let transfer: TransfersResponse = data

View File

@@ -53,7 +53,8 @@ ethereum-serai = { path = "../coins/ethereum", default-features = false, optiona
# Monero # Monero
dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"], optional = true } dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"], optional = true }
monero-serai = { path = "../coins/monero", default-features = false, features = ["std", "http-rpc", "multisig"], optional = true } monero-simple-request-rpc = { path = "../coins/monero/rpc/simple-request", default-features = false, optional = true }
monero-wallet = { path = "../coins/monero/wallet", default-features = false, features = ["std", "multisig"], optional = true }
# Application # Application
log = { version = "0.4", default-features = false, features = ["std"] } log = { version = "0.4", default-features = false, features = ["std"] }
@@ -87,7 +88,7 @@ bitcoin = ["dep:secp256k1", "secp256k1", "bitcoin-serai", "serai-client/bitcoin"
ethereum = ["secp256k1", "ethereum-serai/tests"] ethereum = ["secp256k1", "ethereum-serai/tests"]
ed25519 = ["dalek-ff-group", "frost/ed25519"] ed25519 = ["dalek-ff-group", "frost/ed25519"]
monero = ["ed25519", "monero-serai", "serai-client/monero"] monero = ["ed25519", "monero-simple-request-rpc", "monero-wallet", "serai-client/monero"]
binaries = ["env_logger", "serai-env", "message-queue"] binaries = ["env_logger", "serai-env", "message-queue"]
parity-db = ["serai-db/parity-db"] parity-db = ["serai-db/parity-db"]

View File

@@ -13,18 +13,14 @@ use ciphersuite::group::{ff::Field, Group};
use dalek_ff_group::{Scalar, EdwardsPoint}; use dalek_ff_group::{Scalar, EdwardsPoint};
use frost::{curve::Ed25519, ThresholdKeys}; use frost::{curve::Ed25519, ThresholdKeys};
use monero_serai::{ use monero_simple_request_rpc::SimpleRequestRpc;
Protocol, use monero_wallet::{
ringct::RctType, monero::{Protocol, ringct::RctType, transaction::Transaction, block::Block},
transaction::Transaction, rpc::{RpcError, Rpc},
block::Block, ViewPair, Scanner,
rpc::{RpcError, HttpRpc, Rpc}, address::{Network as MoneroNetwork, SubaddressIndex, AddressSpec},
wallet::{ Fee, SpendableOutput, Change, DecoySelection, Decoys, TransactionError,
ViewPair, Scanner, SignableTransaction as MSignableTransaction, Eventuality, TransactionMachine,
address::{Network as MoneroNetwork, SubaddressIndex, AddressSpec},
Fee, SpendableOutput, Change, DecoySelection, Decoys, TransactionError,
SignableTransaction as MSignableTransaction, Eventuality, TransactionMachine,
},
}; };
use tokio::time::sleep; use tokio::time::sleep;
@@ -226,7 +222,7 @@ impl BlockTrait<Monero> for Block {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Monero { pub struct Monero {
rpc: Rpc<HttpRpc>, rpc: Rpc<SimpleRequestRpc>,
} }
// Shim required for testing/debugging purposes due to generic arguments also necessitating trait // Shim required for testing/debugging purposes due to generic arguments also necessitating trait
// bounds // bounds
@@ -249,11 +245,11 @@ fn map_rpc_err(err: RpcError) -> NetworkError {
impl Monero { impl Monero {
pub async fn new(url: String) -> Monero { pub async fn new(url: String) -> Monero {
let mut res = HttpRpc::new(url.clone()).await; let mut res = SimpleRequestRpc::new(url.clone()).await;
while let Err(e) = res { while let Err(e) = res {
log::error!("couldn't connect to Monero node: {e:?}"); log::error!("couldn't connect to Monero node: {e:?}");
tokio::time::sleep(Duration::from_secs(5)).await; tokio::time::sleep(Duration::from_secs(5)).await;
res = HttpRpc::new(url.clone()).await; res = SimpleRequestRpc::new(url.clone()).await;
} }
Monero { rpc: res.unwrap() } Monero { rpc: res.unwrap() }
} }
@@ -760,7 +756,7 @@ impl Network for Monero {
async fn test_send(&self, address: Address) -> Block { async fn test_send(&self, address: Address) -> Block {
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use monero_serai::wallet::FeePriority; use monero_wallet::FeePriority;
let new_block = self.get_latest_block_number().await.unwrap() + 1; let new_block = self.get_latest_block_number().await.unwrap() + 1;
for _ in 0 .. 80 { for _ in 0 .. 80 {

View File

@@ -39,7 +39,7 @@ simple-request = { path = "../../common/request", version = "0.1", optional = tr
bitcoin = { version = "0.32", optional = true } bitcoin = { version = "0.32", optional = true }
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true } ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true }
monero-serai = { path = "../../coins/monero", version = "0.1.4-alpha", optional = true } monero-wallet = { path = "../../coins/monero/wallet", version = "0.1.0", default-features = false, features = ["std"], optional = true }
[dev-dependencies] [dev-dependencies]
rand_core = "0.6" rand_core = "0.6"
@@ -62,7 +62,7 @@ borsh = ["serai-abi/borsh"]
networks = [] networks = []
bitcoin = ["networks", "dep:bitcoin"] bitcoin = ["networks", "dep:bitcoin"]
monero = ["networks", "ciphersuite/ed25519", "monero-serai"] monero = ["networks", "ciphersuite/ed25519", "monero-wallet"]
# Assumes the default usage is to use Serai as a DEX, which doesn't actually # Assumes the default usage is to use Serai as a DEX, which doesn't actually
# require connecting to a Serai node # require connecting to a Serai node

View File

@@ -4,7 +4,7 @@ use scale::{Encode, Decode};
use ciphersuite::{Ciphersuite, Ed25519}; use ciphersuite::{Ciphersuite, Ed25519};
use monero_serai::wallet::address::{AddressError, Network, AddressType, AddressMeta, MoneroAddress}; use monero_wallet::address::{AddressError, Network, AddressType, AddressMeta, MoneroAddress};
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Address(MoneroAddress); pub struct Address(MoneroAddress);

View File

@@ -27,7 +27,8 @@ rand_core = { version = "0.6", default-features = false }
curve25519-dalek = { version = "4", features = ["rand_core"] } curve25519-dalek = { version = "4", features = ["rand_core"] }
bitcoin-serai = { path = "../../coins/bitcoin" } bitcoin-serai = { path = "../../coins/bitcoin" }
monero-serai = { path = "../../coins/monero" } monero-simple-request-rpc = { path = "../../coins/monero/rpc/simple-request" }
monero-wallet = { path = "../../coins/monero/wallet" }
scale = { package = "parity-scale-codec", version = "3" } scale = { package = "parity-scale-codec", version = "3" }
serde = "1" serde = "1"

View File

@@ -53,8 +53,8 @@ impl Handles {
pub async fn monero( pub async fn monero(
&self, &self,
ops: &DockerOperations, ops: &DockerOperations,
) -> monero_serai::rpc::Rpc<monero_serai::rpc::HttpRpc> { ) -> monero_wallet::rpc::Rpc<monero_simple_request_rpc::SimpleRequestRpc> {
use monero_serai::rpc::HttpRpc; use monero_simple_request_rpc::SimpleRequestRpc;
let rpc = ops.handle(&self.monero.0).host_port(self.monero.1).unwrap(); let rpc = ops.handle(&self.monero.0).host_port(self.monero.1).unwrap();
let rpc = format!("http://{RPC_USER}:{RPC_PASS}@{}:{}", rpc.0, rpc.1); let rpc = format!("http://{RPC_USER}:{RPC_PASS}@{}:{}", rpc.0, rpc.1);
@@ -62,7 +62,7 @@ impl Handles {
// If the RPC server has yet to start, sleep for up to 60s until it does // If the RPC server has yet to start, sleep for up to 60s until it does
for _ in 0 .. 60 { for _ in 0 .. 60 {
tokio::time::sleep(Duration::from_secs(1)).await; tokio::time::sleep(Duration::from_secs(1)).await;
let Ok(client) = HttpRpc::new(rpc.clone()).await else { continue }; let Ok(client) = SimpleRequestRpc::new(rpc.clone()).await else { continue };
if client.get_height().await.is_err() { if client.get_height().await.is_err() {
continue; continue;
} }

View File

@@ -88,7 +88,7 @@ async fn mint_and_burn_test() {
// Mine a Monero block // Mine a Monero block
let monero_blocks = { let monero_blocks = {
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use monero_serai::wallet::{ use monero_wallet::{
ViewPair, ViewPair,
address::{Network, AddressSpec}, address::{Network, AddressSpec},
}; };
@@ -345,14 +345,10 @@ async fn mint_and_burn_test() {
// Send in XMR // Send in XMR
{ {
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use monero_serai::{ use monero_wallet::{
Protocol, monero::{io::decompress_point, Protocol, transaction::Timelock},
transaction::Timelock, ViewPair, Scanner, DecoySelection, Decoys, Change, FeePriority, SignableTransaction,
wallet::{ address::{Network, AddressType, AddressMeta, MoneroAddress},
ViewPair, Scanner, DecoySelection, Decoys, Change, FeePriority, SignableTransaction,
address::{Network, AddressType, AddressMeta, MoneroAddress},
},
io::decompress_point,
}; };
// Grab the first output on the chain // Grab the first output on the chain
@@ -473,7 +469,7 @@ async fn mint_and_burn_test() {
let spend = ED25519_BASEPOINT_TABLE * &Scalar::random(&mut OsRng); let spend = ED25519_BASEPOINT_TABLE * &Scalar::random(&mut OsRng);
let view = Scalar::random(&mut OsRng); let view = Scalar::random(&mut OsRng);
use monero_serai::wallet::address::{Network, AddressType, AddressMeta, MoneroAddress}; use monero_wallet::address::{Network, AddressType, AddressMeta, MoneroAddress};
let addr = MoneroAddress::new( let addr = MoneroAddress::new(
AddressMeta::new(Network::Mainnet, AddressType::Standard), AddressMeta::new(Network::Mainnet, AddressType::Standard),
spend, spend,
@@ -578,7 +574,7 @@ async fn mint_and_burn_test() {
// Verify the received Monero TX // Verify the received Monero TX
{ {
use monero_serai::wallet::{ViewPair, Scanner}; use monero_wallet::{ViewPair, Scanner};
let rpc = handles[0].monero(&ops).await; let rpc = handles[0].monero(&ops).await;
let mut scanner = Scanner::from_view( let mut scanner = Scanner::from_view(
ViewPair::new(monero_spend, Zeroizing::new(monero_view)), ViewPair::new(monero_spend, Zeroizing::new(monero_view)),

View File

@@ -42,3 +42,5 @@ monero-mlsag = { path = "../../coins/monero/ringct/mlsag", default-features = fa
monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = false } monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = false }
monero-bulletproofs = { path = "../../coins/monero/ringct/bulletproofs", default-features = false } monero-bulletproofs = { path = "../../coins/monero/ringct/bulletproofs", default-features = false }
monero-serai = { path = "../../coins/monero", default-features = false } monero-serai = { path = "../../coins/monero", default-features = false }
monero-rpc = { path = "../../coins/monero/rpc", default-features = false }
monero-wallet = { path = "../../coins/monero/wallet", default-features = false }

View File

@@ -27,3 +27,4 @@ pub use monero_mlsag;
pub use monero_clsag; pub use monero_clsag;
pub use monero_bulletproofs; pub use monero_bulletproofs;
pub use monero_serai; pub use monero_serai;
pub use monero_rpc;

View File

@@ -31,7 +31,8 @@ bitcoin-serai = { path = "../../coins/bitcoin" }
k256 = "0.13" k256 = "0.13"
ethereum-serai = { path = "../../coins/ethereum" } ethereum-serai = { path = "../../coins/ethereum" }
monero-serai = { path = "../../coins/monero" } monero-simple-request-rpc = { path = "../../coins/monero/rpc/simple-request" }
monero-wallet = { path = "../../coins/monero/wallet" }
messages = { package = "serai-processor-messages", path = "../../processor/messages" } messages = { package = "serai-processor-messages", path = "../../processor/messages" }

View File

@@ -274,11 +274,11 @@ impl Coordinator {
} }
} }
NetworkId::Monero => { NetworkId::Monero => {
use monero_serai::rpc::HttpRpc; use monero_simple_request_rpc::SimpleRequestRpc;
// Monero's won't, so call get_height // Monero's won't, so call get_height
if handle if handle
.block_on(HttpRpc::new(rpc_url.clone())) .block_on(SimpleRequestRpc::new(rpc_url.clone()))
.ok() .ok()
.and_then(|rpc| handle.block_on(rpc.get_height()).ok()) .and_then(|rpc| handle.block_on(rpc.get_height()).ok())
.is_some() .is_some()
@@ -403,15 +403,13 @@ impl Coordinator {
} }
NetworkId::Monero => { NetworkId::Monero => {
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use monero_serai::{ use monero_simple_request_rpc::SimpleRequestRpc;
wallet::{ use monero_wallet::{
ViewPair, ViewPair,
address::{Network, AddressSpec}, address::{Network, AddressSpec},
},
rpc::HttpRpc,
}; };
let rpc = HttpRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC"); let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
let _: EmptyResponse = rpc let _: EmptyResponse = rpc
.json_rpc_call( .json_rpc_call(
"generateblocks", "generateblocks",
@@ -517,15 +515,18 @@ impl Coordinator {
} }
} }
NetworkId::Monero => { NetworkId::Monero => {
use monero_serai::rpc::HttpRpc; use monero_simple_request_rpc::SimpleRequestRpc;
let rpc = HttpRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC"); let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
let to = rpc.get_height().await.unwrap(); let to = rpc.get_height().await.unwrap();
for coordinator in others { for coordinator in others {
let other_rpc = let other_rpc = SimpleRequestRpc::new(network_rpc(
HttpRpc::new(network_rpc(coordinator.network, ops, &coordinator.network_handle)) coordinator.network,
.await ops,
.expect("couldn't connect to the Monero RPC"); &coordinator.network_handle,
))
.await
.expect("couldn't connect to the Monero RPC");
let from = other_rpc.get_height().await.unwrap(); let from = other_rpc.get_height().await.unwrap();
for b in from .. to { for b in from .. to {
@@ -574,10 +575,12 @@ impl Coordinator {
let _ = provider.send_raw_transaction(tx).await.unwrap(); let _ = provider.send_raw_transaction(tx).await.unwrap();
} }
NetworkId::Monero => { NetworkId::Monero => {
use monero_serai::{transaction::Transaction, rpc::HttpRpc}; use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::monero::transaction::Transaction;
let rpc = let rpc = SimpleRequestRpc::new(rpc_url)
HttpRpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Monero RPC"); .await
.expect("couldn't connect to the coordinator's Monero RPC");
rpc.publish_transaction(&Transaction::read(&mut &*tx).unwrap()).await.unwrap(); rpc.publish_transaction(&Transaction::read(&mut &*tx).unwrap()).await.unwrap();
} }
NetworkId::Serai => panic!("processor tests broadcasting block to Serai"), NetworkId::Serai => panic!("processor tests broadcasting block to Serai"),
@@ -672,10 +675,11 @@ impl Coordinator {
None None
} }
NetworkId::Monero => { NetworkId::Monero => {
use monero_serai::rpc::HttpRpc; use monero_simple_request_rpc::SimpleRequestRpc;
let rpc = let rpc = SimpleRequestRpc::new(rpc_url)
HttpRpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Monero RPC"); .await
.expect("couldn't connect to the coordinator's Monero RPC");
let mut hash = [0; 32]; let mut hash = [0; 32];
hash.copy_from_slice(tx); hash.copy_from_slice(tx);
if let Ok(tx) = rpc.get_transaction(hash).await { if let Ok(tx) = rpc.get_transaction(hash).await {

View File

@@ -103,8 +103,8 @@ pub enum Wallet {
Monero { Monero {
handle: String, handle: String,
spend_key: Zeroizing<curve25519_dalek::scalar::Scalar>, spend_key: Zeroizing<curve25519_dalek::scalar::Scalar>,
view_pair: monero_serai::wallet::ViewPair, view_pair: monero_wallet::ViewPair,
inputs: Vec<monero_serai::wallet::ReceivedOutput>, inputs: Vec<monero_wallet::ReceivedOutput>,
}, },
} }
@@ -189,12 +189,10 @@ impl Wallet {
NetworkId::Monero => { NetworkId::Monero => {
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use monero_serai::{ use monero_simple_request_rpc::SimpleRequestRpc;
wallet::{ use monero_wallet::{
ViewPair, Scanner, ViewPair, Scanner,
address::{Network, AddressSpec}, address::{Network, AddressSpec},
},
rpc::HttpRpc,
}; };
let mut bytes = [0; 64]; let mut bytes = [0; 64];
@@ -206,7 +204,7 @@ impl Wallet {
let view_pair = let view_pair =
ViewPair::new(ED25519_BASEPOINT_POINT * spend_key, Zeroizing::new(view_key)); ViewPair::new(ED25519_BASEPOINT_POINT * spend_key, Zeroizing::new(view_key));
let rpc = HttpRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC"); let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
let height = rpc.get_height().await.unwrap(); let height = rpc.get_height().await.unwrap();
// Mines 200 blocks so sufficient decoys exist, as only 60 is needed for maturity // Mines 200 blocks so sufficient decoys exist, as only 60 is needed for maturity
@@ -436,20 +434,17 @@ impl Wallet {
Wallet::Monero { handle, ref spend_key, ref view_pair, ref mut inputs } => { Wallet::Monero { handle, ref spend_key, ref view_pair, ref mut inputs } => {
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT; use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
use monero_serai::{ use monero_simple_request_rpc::SimpleRequestRpc;
Protocol, use monero_wallet::{
wallet::{ monero::{Protocol, io::decompress_point},
address::{Network, AddressType, AddressMeta, Address}, address::{Network, AddressType, AddressMeta, Address},
SpendableOutput, DecoySelection, Decoys, Change, FeePriority, Scanner, SpendableOutput, DecoySelection, Decoys, Change, FeePriority, Scanner,
SignableTransaction, SignableTransaction,
},
rpc::HttpRpc,
io::decompress_point,
}; };
use processor::{additional_key, networks::Monero}; use processor::{additional_key, networks::Monero};
let rpc_url = network_rpc(NetworkId::Monero, ops, handle); let rpc_url = network_rpc(NetworkId::Monero, ops, handle);
let rpc = HttpRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC"); let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
// Prepare inputs // Prepare inputs
let outputs = std::mem::take(inputs); let outputs = std::mem::take(inputs);
@@ -532,7 +527,7 @@ impl Wallet {
) )
.unwrap(), .unwrap(),
Wallet::Monero { view_pair, .. } => { Wallet::Monero { view_pair, .. } => {
use monero_serai::wallet::address::{Network, AddressSpec}; use monero_wallet::address::{Network, AddressSpec};
ExternalAddress::new( ExternalAddress::new(
networks::monero::Address::new( networks::monero::Address::new(
view_pair.address(Network::Mainnet, AddressSpec::Standard), view_pair.address(Network::Mainnet, AddressSpec::Standard),