From dc3f99ebfa6143b3c3818e8f14bbb65f50ec9ec7 Mon Sep 17 00:00:00 2001 From: scilio Date: Thu, 14 Sep 2023 11:49:04 -0400 Subject: [PATCH] move onion creation to a separate library --- Cargo.lock | 37 ++++++ Cargo.toml | 4 + onion/Cargo.toml | 38 +++++++ {src => onion/src}/crypto/comsig.rs | 0 {src => onion/src}/crypto/dalek.rs | 12 -- {src => onion/src}/crypto/mod.rs | 0 {src => onion/src}/crypto/secp.rs | 55 --------- onion/src/lib.rs | 170 ++++++++++++++++++++++++++++ {src => onion/src}/onion.rs | 120 +------------------- {src => onion/src}/util.rs | 0 src/client.rs | 6 +- src/main.rs | 6 +- src/servers/mix.rs | 25 ++-- src/servers/mix_rpc.rs | 4 +- src/servers/swap.rs | 82 +++++++------- src/servers/swap_rpc.rs | 12 +- src/store.rs | 27 +++-- src/tx.rs | 41 +++++++ 18 files changed, 374 insertions(+), 265 deletions(-) create mode 100644 onion/Cargo.toml rename {src => onion/src}/crypto/comsig.rs (100%) rename {src => onion/src}/crypto/dalek.rs (97%) rename {src => onion/src}/crypto/mod.rs (100%) rename {src => onion/src}/crypto/secp.rs (64%) create mode 100644 onion/src/lib.rs rename {src => onion/src}/onion.rs (75%) rename {src => onion/src}/util.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 924569a..b671a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1354,6 +1354,42 @@ dependencies = [ "zeroize", ] +[[package]] +name = "grin_onion" +version = "0.1.0" +dependencies = [ + "blake2-rfc", + "byteorder", + "bytes 0.5.6", + "chacha20", + "curve25519-dalek 2.1.3", + "ed25519-dalek", + "grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_secp256k1zkp", + "grin_servers", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.1.1", + "grin_wallet_api", + "grin_wallet_impls", + "grin_wallet_libwallet", + "grin_wallet_util", + "hmac 0.12.0", + "itertools", + "lazy_static", + "rand 0.7.3", + "rpassword", + "serde", + "serde_derive", + "serde_json", + "sha2 0.10.0", + "thiserror", + "toml", + "x25519-dalek 0.6.0", +] + [[package]] name = "grin_p2p" version = "5.2.0-alpha.1" @@ -2555,6 +2591,7 @@ dependencies = [ "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_onion", "grin_secp256k1zkp", "grin_servers", "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", diff --git a/Cargo.toml b/Cargo.toml index 2e3e3f6..fbd4750 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = ["onion"] + [dependencies] blake2 = { package = "blake2-rfc", version = "0.2"} byteorder = "1" @@ -36,6 +39,7 @@ thiserror = "1.0.31" tokio = { version = "1", features = ["full"] } toml = "0.5" x25519-dalek = "0.6.0" +grin_onion = { path = "./onion" } grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]} grin_util = "5" grin_api = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } diff --git a/onion/Cargo.toml b/onion/Cargo.toml new file mode 100644 index 0000000..43a9e4b --- /dev/null +++ b/onion/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "grin_onion" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +blake2 = { package = "blake2-rfc", version = "0.2"} +byteorder = "1" +bytes = "0.5.6" +chacha20 = "0.8.1" +curve25519-dalek = "2.1" +ed25519-dalek = "1.0.1" +hmac = { version = "0.12.0", features = ["std"]} +itertools = { version = "0.10.3"} +lazy_static = "1" +rand = "0.7.3" +rpassword = "4.0" +serde = { version = "1", features= ["derive"]} +serde_derive = "1" +serde_json = "1" +sha2 = "0.10.0" +thiserror = "1.0.31" +toml = "0.5" +x25519-dalek = "0.6.0" +grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]} +grin_util = "5" +grin_api = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_core = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_chain = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_keychain = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_servers = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_store = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } \ No newline at end of file diff --git a/src/crypto/comsig.rs b/onion/src/crypto/comsig.rs similarity index 100% rename from src/crypto/comsig.rs rename to onion/src/crypto/comsig.rs diff --git a/src/crypto/dalek.rs b/onion/src/crypto/dalek.rs similarity index 97% rename from src/crypto/dalek.rs rename to onion/src/crypto/dalek.rs index 9af9eaf..70c7654 100644 --- a/src/crypto/dalek.rs +++ b/onion/src/crypto/dalek.rs @@ -159,18 +159,6 @@ pub fn sign(sk: &SecretKey, message: &[u8]) -> Result (SecretKey, DalekPublicKey) { - let sk = random_secret(); - let pk = DalekPublicKey::from_secret(&sk); - (sk, pk) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/crypto/mod.rs b/onion/src/crypto/mod.rs similarity index 100% rename from src/crypto/mod.rs rename to onion/src/crypto/mod.rs diff --git a/src/crypto/secp.rs b/onion/src/crypto/secp.rs similarity index 64% rename from src/crypto/secp.rs rename to onion/src/crypto/secp.rs index 6a682d2..89923b9 100644 --- a/src/crypto/secp.rs +++ b/onion/src/crypto/secp.rs @@ -60,58 +60,3 @@ pub fn sign(sk: &SecretKey, msg: &Message) -> Result Commitment { - secp::commit(rand::thread_rng().next_u64(), &secp::random_secret()).unwrap() - } - - pub fn rand_hash() -> Hash { - Hash::from_hex(secp::random_secret().to_hex().as_str()).unwrap() - } - - pub fn rand_proof() -> RangeProof { - let secp = Secp256k1::new(); - secp.bullet_proof( - rand::thread_rng().next_u64(), - secp::random_secret(), - secp::random_secret(), - secp::random_secret(), - None, - None, - ) - } - - pub fn proof( - value: u64, - fee: u32, - input_blind: &SecretKey, - hop_excesses: &Vec<&SecretKey>, - ) -> (Commitment, RangeProof) { - let secp = Secp256k1::new(); - - let mut blind = input_blind.clone(); - for hop_excess in hop_excesses { - blind.add_assign(&secp, &hop_excess).unwrap(); - } - - let out_value = value - (fee as u64); - - let rp = secp.bullet_proof( - out_value, - blind.clone(), - secp::random_secret(), - secp::random_secret(), - None, - None, - ); - - (secp::commit(out_value, &blind).unwrap(), rp) - } -} diff --git a/onion/src/lib.rs b/onion/src/lib.rs new file mode 100644 index 0000000..d08860d --- /dev/null +++ b/onion/src/lib.rs @@ -0,0 +1,170 @@ +pub mod crypto; +pub mod onion; +pub mod util; + +use crate::crypto::secp::{random_secret, Commitment, SecretKey}; +use crate::onion::{new_stream_cipher, Onion, OnionError, Payload, RawBytes}; + +use chacha20::cipher::StreamCipher; +use grin_core::core::FeeFields; +use secp256k1zkp::pedersen::RangeProof; +use x25519_dalek::PublicKey as xPublicKey; +use x25519_dalek::{SharedSecret, StaticSecret}; + +#[derive(Clone)] +pub struct Hop { + pub server_pubkey: xPublicKey, + pub excess: SecretKey, + pub fee: FeeFields, + pub rangeproof: Option, +} + +pub fn new_hop( + server_key: &SecretKey, + hop_excess: &SecretKey, + fee: u32, + proof: Option, +) -> Hop { + Hop { + server_pubkey: xPublicKey::from(&StaticSecret::from(server_key.0.clone())), + excess: hop_excess.clone(), + fee: FeeFields::from(fee as u32), + rangeproof: proof, + } +} + +/// Create an Onion for the Commitment, encrypting the payload for each hop +pub fn create_onion(commitment: &Commitment, hops: &Vec) -> Result { + if hops.is_empty() { + return Ok(Onion { + ephemeral_pubkey: xPublicKey::from([0u8; 32]), + commit: commitment.clone(), + enc_payloads: vec![], + }); + } + + let mut shared_secrets: Vec = Vec::new(); + let mut enc_payloads: Vec = Vec::new(); + let mut ephemeral_sk = StaticSecret::from(random_secret().0); + let onion_ephemeral_pk = xPublicKey::from(&ephemeral_sk); + for i in 0..hops.len() { + let hop = &hops[i]; + let shared_secret = ephemeral_sk.diffie_hellman(&hop.server_pubkey); + shared_secrets.push(shared_secret); + + ephemeral_sk = StaticSecret::from(random_secret().0); + let next_ephemeral_pk = if i < (hops.len() - 1) { + xPublicKey::from(&ephemeral_sk) + } else { + xPublicKey::from([0u8; 32]) + }; + + let payload = Payload { + next_ephemeral_pk, + excess: hop.excess.clone(), + fee: hop.fee.clone(), + rangeproof: hop.rangeproof.clone(), + }; + enc_payloads.push(payload.serialize()?); + } + + for i in (0..shared_secrets.len()).rev() { + let mut cipher = new_stream_cipher(&shared_secrets[i])?; + for j in i..shared_secrets.len() { + cipher.apply_keystream(&mut enc_payloads[j]); + } + } + + let onion = Onion { + ephemeral_pubkey: onion_ephemeral_pk, + commit: commitment.clone(), + enc_payloads, + }; + Ok(onion) +} + +pub mod test_util { + use super::*; + use crate::crypto::dalek::DalekPublicKey; + use crate::crypto::secp; + + use grin_core::core::hash::Hash; + use grin_util::ToHex; + use rand::{thread_rng, RngCore}; + use secp256k1zkp::Secp256k1; + + pub fn rand_onion() -> Onion { + let commit = rand_commit(); + let mut hops = Vec::new(); + let k = (thread_rng().next_u64() % 5) + 1; + for i in 0..k { + let rangeproof = if i == (k - 1) { + Some(rand_proof()) + } else { + None + }; + let hop = new_hop( + &random_secret(), + &random_secret(), + thread_rng().next_u32(), + rangeproof, + ); + hops.push(hop); + } + + create_onion(&commit, &hops).unwrap() + } + + pub fn rand_commit() -> Commitment { + secp::commit(rand::thread_rng().next_u64(), &secp::random_secret()).unwrap() + } + + pub fn rand_hash() -> Hash { + Hash::from_hex(secp::random_secret().to_hex().as_str()).unwrap() + } + + pub fn rand_proof() -> RangeProof { + let secp = Secp256k1::new(); + secp.bullet_proof( + rand::thread_rng().next_u64(), + secp::random_secret(), + secp::random_secret(), + secp::random_secret(), + None, + None, + ) + } + + pub fn proof( + value: u64, + fee: u32, + input_blind: &SecretKey, + hop_excesses: &Vec<&SecretKey>, + ) -> (Commitment, RangeProof) { + let secp = Secp256k1::new(); + + let mut blind = input_blind.clone(); + for hop_excess in hop_excesses { + blind.add_assign(&secp, &hop_excess).unwrap(); + } + + let out_value = value - (fee as u64); + + let rp = secp.bullet_proof( + out_value, + blind.clone(), + secp::random_secret(), + secp::random_secret(), + None, + None, + ); + + (secp::commit(out_value, &blind).unwrap(), rp) + } + + pub fn rand_keypair() -> (SecretKey, DalekPublicKey) { + let sk = random_secret(); + let pk = DalekPublicKey::from_secret(&sk); + (sk, pk) + } +} diff --git a/src/onion.rs b/onion/src/onion.rs similarity index 75% rename from src/onion.rs rename to onion/src/onion.rs index 7153028..974b743 100644 --- a/src/onion.rs +++ b/onion/src/onion.rs @@ -1,5 +1,4 @@ use crate::crypto::secp::{self, Commitment, RangeProof, SecretKey}; -use crate::onion::OnionError::{InvalidKeyLength, SerializationError}; use crate::util::{read_optional, vec_to_array, write_optional}; use chacha20::cipher::{NewCipher, StreamCipher}; @@ -19,7 +18,7 @@ use thiserror::Error; use x25519_dalek::{PublicKey as xPublicKey, SharedSecret, StaticSecret}; type HmacSha256 = Hmac; -type RawBytes = Vec; +pub type RawBytes = Vec; const CURRENT_ONION_VERSION: u8 = 0; @@ -70,7 +69,6 @@ impl Payload { Ok(payload) } - #[cfg(test)] pub fn serialize(&self) -> Result, ser::Error> { let mut vec = vec![]; ser::serialize_default(&mut vec, &self)?; @@ -166,7 +164,7 @@ impl Onion { } } -fn new_stream_cipher(shared_secret: &SharedSecret) -> Result { +pub fn new_stream_cipher(shared_secret: &SharedSecret) -> Result { let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?; mu_hmac.update(shared_secret.as_bytes()); let mukey = mu_hmac.finalize().into_bytes(); @@ -318,129 +316,21 @@ pub enum OnionError { impl From for OnionError { fn from(_err: InvalidLength) -> OnionError { - InvalidKeyLength + OnionError::InvalidKeyLength } } impl From for OnionError { fn from(err: ser::Error) -> OnionError { - SerializationError(err) - } -} - -#[cfg(test)] -pub mod test_util { - use super::{Onion, OnionError, Payload, RawBytes}; - use crate::crypto::secp::test_util::{rand_commit, rand_proof}; - use crate::crypto::secp::{random_secret, Commitment, SecretKey}; - - use chacha20::cipher::StreamCipher; - use grin_core::core::FeeFields; - use rand::{thread_rng, RngCore}; - use secp256k1zkp::pedersen::RangeProof; - use x25519_dalek::PublicKey as xPublicKey; - use x25519_dalek::{SharedSecret, StaticSecret}; - - #[derive(Clone)] - pub struct Hop { - pub server_pubkey: xPublicKey, - pub excess: SecretKey, - pub fee: FeeFields, - pub rangeproof: Option, - } - - pub fn new_hop( - server_key: &SecretKey, - hop_excess: &SecretKey, - fee: u32, - proof: Option, - ) -> Hop { - Hop { - server_pubkey: xPublicKey::from(&StaticSecret::from(server_key.0.clone())), - excess: hop_excess.clone(), - fee: FeeFields::from(fee as u32), - rangeproof: proof, - } - } - - /// Create an Onion for the Commitment, encrypting the payload for each hop - pub fn create_onion(commitment: &Commitment, hops: &Vec) -> Result { - if hops.is_empty() { - return Ok(Onion { - ephemeral_pubkey: xPublicKey::from([0u8; 32]), - commit: commitment.clone(), - enc_payloads: vec![], - }); - } - - let mut shared_secrets: Vec = Vec::new(); - let mut enc_payloads: Vec = Vec::new(); - let mut ephemeral_sk = StaticSecret::from(random_secret().0); - let onion_ephemeral_pk = xPublicKey::from(&ephemeral_sk); - for i in 0..hops.len() { - let hop = &hops[i]; - let shared_secret = ephemeral_sk.diffie_hellman(&hop.server_pubkey); - shared_secrets.push(shared_secret); - - ephemeral_sk = StaticSecret::from(random_secret().0); - let next_ephemeral_pk = if i < (hops.len() - 1) { - xPublicKey::from(&ephemeral_sk) - } else { - xPublicKey::from([0u8; 32]) - }; - - let payload = Payload { - next_ephemeral_pk, - excess: hop.excess.clone(), - fee: hop.fee.clone(), - rangeproof: hop.rangeproof.clone(), - }; - enc_payloads.push(payload.serialize()?); - } - - for i in (0..shared_secrets.len()).rev() { - let mut cipher = super::new_stream_cipher(&shared_secrets[i])?; - for j in i..shared_secrets.len() { - cipher.apply_keystream(&mut enc_payloads[j]); - } - } - - let onion = Onion { - ephemeral_pubkey: onion_ephemeral_pk, - commit: commitment.clone(), - enc_payloads, - }; - Ok(onion) - } - - pub fn rand_onion() -> Onion { - let commit = rand_commit(); - let mut hops = Vec::new(); - let k = (thread_rng().next_u64() % 5) + 1; - for i in 0..k { - let rangeproof = if i == (k - 1) { - Some(rand_proof()) - } else { - None - }; - let hop = new_hop( - &random_secret(), - &random_secret(), - thread_rng().next_u32(), - rangeproof, - ); - hops.push(hop); - } - - create_onion(&commit, &hops).unwrap() + OnionError::SerializationError(err) } } #[cfg(test)] pub mod tests { - use super::test_util::{new_hop, Hop}; use super::*; use crate::crypto::secp::random_secret; + use crate::{new_hop, Hop}; use grin_core::core::FeeFields; diff --git a/src/util.rs b/onion/src/util.rs similarity index 100% rename from src/util.rs rename to onion/src/util.rs diff --git a/src/client.rs b/src/client.rs index d286a1b..1785e26 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,9 +1,9 @@ use crate::config::ServerConfig; use crate::crypto::dalek; -use crate::onion::Onion; use crate::servers::mix_rpc::MixReq; use crate::tx::TxComponents; use crate::{tor, DalekPublicKey}; +use grin_onion::onion::Onion; use grin_api::client; use grin_api::json_rpc::build_request; @@ -97,8 +97,8 @@ impl MixClient for MixClientImpl { #[cfg(test)] pub mod mock { use super::{ClientError, MixClient}; - use crate::onion::Onion; use crate::tx::TxComponents; + use grin_onion::onion::Onion; use std::collections::HashMap; @@ -136,12 +136,12 @@ pub mod test_util { use super::{ClientError, MixClient}; use crate::crypto::dalek; use crate::crypto::secp::SecretKey; - use crate::onion::Onion; use crate::servers::mix::MixServer; use crate::tx::TxComponents; use crate::DalekPublicKey; use grin_core::ser; use grin_core::ser::ProtocolVersion; + use grin_onion::onion::Onion; use std::sync::Arc; /// Implementation of the 'MixClient' trait that calls a mix server implementation directly. diff --git a/src/main.rs b/src/main.rs index 0f4b0e0..fdf84bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,12 +4,13 @@ use store::SwapStore; use wallet::HttpWallet; use crate::client::{MixClient, MixClientImpl}; -use crate::crypto::dalek::DalekPublicKey; use crate::node::GrinNode; use crate::store::StoreError; use clap::App; use grin_core::global; use grin_core::global::ChainTypes; +use grin_onion::crypto; +use grin_onion::crypto::dalek::DalekPublicKey; use grin_util::{StopState, ZeroingString}; use rpassword; use std::path::PathBuf; @@ -21,14 +22,11 @@ extern crate clap; mod client; mod config; -mod crypto; mod node; -mod onion; mod servers; mod store; mod tor; mod tx; -mod util; mod wallet; const DEFAULT_INTERVAL: u32 = 12 * 60 * 60; diff --git a/src/servers/mix.rs b/src/servers/mix.rs index a65430e..4510521 100644 --- a/src/servers/mix.rs +++ b/src/servers/mix.rs @@ -1,19 +1,19 @@ +use crate::client::MixClient; use crate::config::ServerConfig; -use crate::crypto::dalek::{self, DalekSignature}; -use crate::onion::{Onion, OnionError, PeeledOnion}; +use crate::tx::TxComponents; use crate::wallet::Wallet; use crate::{node, tx, GrinNode}; -use std::collections::{HashMap, HashSet}; -use crate::client::MixClient; -use crate::tx::TxComponents; use grin_core::core::{Output, OutputFeatures, TransactionBody}; use grin_core::global::DEFAULT_ACCEPT_FEE_BASE; use grin_core::ser; use grin_core::ser::ProtocolVersion; +use grin_onion::crypto::dalek::{self, DalekSignature}; +use grin_onion::onion::{Onion, OnionError, PeeledOnion}; use itertools::Itertools; use secp256k1zkp::key::ZERO_KEY; use secp256k1zkp::Secp256k1; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use thiserror::Error; @@ -320,14 +320,13 @@ mod test_util { #[cfg(test)] mod tests { - use crate::crypto::dalek; - use crate::crypto::secp::{self, Commitment}; use crate::node::mock::MockGrinNode; - use crate::onion::test_util; use crate::{DalekPublicKey, MixClient}; - use crate::onion::test_util::Hop; use ::function_name::named; + use grin_onion::crypto::secp::{self, Commitment}; + use grin_onion::test_util as onion_test_util; + use grin_onion::{create_onion, new_hop, Hop}; use secp256k1zkp::pedersen::RangeProof; use secp256k1zkp::SecretKey; use std::collections::HashSet; @@ -353,7 +352,7 @@ mod tests { impl ServerVars { fn new(fee: u32) -> Self { - let (sk, pk) = dalek::test_util::rand_keypair(); + let (sk, pk) = onion_test_util::rand_keypair(); let excess = secp::random_secret(); ServerVars { fee, @@ -364,7 +363,7 @@ mod tests { } fn build_hop(&self, proof: Option) -> Hop { - test_util::new_hop(&self.sk, &self.excess, self.fee, proof) + new_hop(&self.sk, &self.excess, self.fee, proof) } } @@ -408,7 +407,7 @@ mod tests { ); // Build rangeproof - let (output_commit, proof) = secp::test_util::proof( + let (output_commit, proof) = onion_test_util::proof( input1_value, swap_vars.fee + mix1_vars.fee + mix2_vars.fee, &input1_blind, @@ -416,7 +415,7 @@ mod tests { ); // Create Onion - let onion = test_util::create_onion( + let onion = create_onion( &input1_commit, &vec![ swap_vars.build_hop(None), diff --git a/src/servers/mix_rpc.rs b/src/servers/mix_rpc.rs index 033639b..7cb5181 100644 --- a/src/servers/mix_rpc.rs +++ b/src/servers/mix_rpc.rs @@ -1,11 +1,11 @@ +use crate::client::MixClient; use crate::config::ServerConfig; use crate::crypto::dalek::{self, DalekSignature}; use crate::node::GrinNode; -use crate::onion::Onion; use crate::servers::mix::{MixError, MixServer, MixServerImpl}; use crate::wallet::Wallet; -use crate::client::MixClient; +use grin_onion::onion::Onion; use grin_util::StopState; use jsonrpc_derive::rpc; use jsonrpc_http_server::jsonrpc_core::{self as jsonrpc, IoHandler}; diff --git a/src/servers/swap.rs b/src/servers/swap.rs index e918d08..a02095c 100644 --- a/src/servers/swap.rs +++ b/src/servers/swap.rs @@ -1,19 +1,19 @@ +use crate::client::MixClient; use crate::config::ServerConfig; use crate::crypto::comsig::ComSignature; use crate::crypto::secp::{Commitment, Secp256k1, SecretKey}; use crate::node::{self, GrinNode}; -use crate::onion::{Onion, OnionError}; use crate::store::{StoreError, SwapData, SwapStatus, SwapStore}; use crate::tx; use crate::wallet::Wallet; -use std::collections::HashSet; -use crate::client::MixClient; use grin_core::core::hash::Hashed; use grin_core::core::{Input, Output, OutputFeatures, Transaction, TransactionBody}; use grin_core::global::DEFAULT_ACCEPT_FEE_BASE; +use grin_onion::onion::{Onion, OnionError}; use itertools::Itertools; use secp256k1zkp::key::ZERO_KEY; +use std::collections::HashSet; use std::result::Result; use std::sync::{Arc, Mutex}; use thiserror::Error; @@ -268,9 +268,9 @@ impl SwapServer for SwapServerImpl { pub mod mock { use super::{SwapError, SwapServer}; use crate::crypto::comsig::ComSignature; - use crate::onion::Onion; use grin_core::core::Transaction; + use grin_onion::onion::Onion; use std::collections::HashMap; pub struct MockSwapServer { @@ -339,12 +339,7 @@ pub mod test_util { #[cfg(test)] mod tests { - use crate::crypto::comsig::ComSignature; - use crate::crypto::dalek; - use crate::crypto::secp; use crate::node::mock::MockGrinNode; - use crate::onion::test_util::{self, Hop}; - use crate::onion::Onion; use crate::servers::swap::{SwapError, SwapServer}; use crate::store::{SwapData, SwapStatus}; use crate::tx::TxComponents; @@ -353,6 +348,11 @@ mod tests { use ::function_name::named; use grin_core::core::hash::Hashed; use grin_core::core::{Committed, Input, Output, OutputFeatures, Transaction, Weighting}; + use grin_onion::crypto::comsig::ComSignature; + use grin_onion::crypto::secp; + use grin_onion::onion::Onion; + use grin_onion::test_util as onion_test_util; + use grin_onion::{create_onion, new_hop, Hop}; use secp256k1zkp::key::ZERO_KEY; use std::sync::Arc; use x25519_dalek::PublicKey as xPublicKey; @@ -392,10 +392,10 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); - let (output_commit, proof) = secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + let (output_commit, proof) = onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop.clone()])?; + let onion = create_onion(&input_commit, &vec![hop.clone()])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); @@ -464,24 +464,24 @@ mod tests { // Swapper data let swap_fee: u32 = 50_000_000; - let (swap_sk, _swap_pk) = dalek::test_util::rand_keypair(); + let (swap_sk, _swap_pk) = onion_test_util::rand_keypair(); let swap_hop_excess = secp::random_secret(); - let swap_hop = test_util::new_hop(&swap_sk, &swap_hop_excess, swap_fee, None); + let swap_hop = new_hop(&swap_sk, &swap_hop_excess, swap_fee, None); // Mixer data let mixer_fee: u32 = 30_000_000; - let (mixer_sk, mixer_pk) = dalek::test_util::rand_keypair(); + let (mixer_sk, mixer_pk) = onion_test_util::rand_keypair(); let mixer_hop_excess = secp::random_secret(); - let (output_commit, proof) = secp::test_util::proof( + let (output_commit, proof) = onion_test_util::proof( value, swap_fee + mixer_fee, &blind, &vec![&swap_hop_excess, &mixer_hop_excess], ); - let mixer_hop = test_util::new_hop(&mixer_sk, &mixer_hop_excess, mixer_fee, Some(proof)); + let mixer_hop = new_hop(&mixer_sk, &mixer_hop_excess, mixer_fee, Some(proof)); // Create onion - let onion = test_util::create_onion(&input_commit, &vec![swap_hop, mixer_hop])?; + let onion = create_onion(&input_commit, &vec![swap_hop, mixer_hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; // Mock mixer @@ -540,11 +540,11 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); let (_output_commit, proof) = - secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); let hops: Vec = vec![hop.clone(), hop.clone()]; // Multiple payloads - let onion = test_util::create_onion(&input_commit, &hops)?; + let onion = create_onion(&input_commit, &hops)?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); @@ -575,10 +575,10 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); let (_output_commit, proof) = - secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let wrong_blind = secp::random_secret(); let comsig = ComSignature::sign(value, &wrong_blind, &onion.serialize()?)?; @@ -612,10 +612,10 @@ mod tests { let hop_excess = secp::random_secret(); let wrong_value = value + 10_000_000; let (_output_commit, proof) = - secp::test_util::proof(wrong_value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + onion_test_util::proof(wrong_value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); @@ -645,9 +645,9 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, None); + let hop = new_hop(&server_key, &hop_excess, fee, None); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); @@ -678,10 +678,10 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); let (_output_commit, proof) = - secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new()); @@ -717,10 +717,10 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); let (_output_commit, proof) = - secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); @@ -753,12 +753,12 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); let (_output_commit, proof) = - secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); let wrong_server_key = secp::random_secret(); - let hop = test_util::new_hop(&wrong_server_key, &hop_excess, fee, Some(proof)); + let hop = new_hop(&wrong_server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); @@ -785,10 +785,10 @@ mod tests { let server_key = secp::random_secret(); let hop_excess = secp::random_secret(); let (_output_commit, proof) = - secp::test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = test_util::new_hop(&server_key, &hop_excess, fee, Some(proof)); + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let onion = create_onion(&input_commit, &vec![hop])?; let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); diff --git a/src/servers/swap_rpc.rs b/src/servers/swap_rpc.rs index 313b450..788e4c1 100644 --- a/src/servers/swap_rpc.rs +++ b/src/servers/swap_rpc.rs @@ -1,12 +1,12 @@ +use crate::client::MixClient; use crate::config::ServerConfig; -use crate::crypto::comsig::{self, ComSignature}; use crate::node::GrinNode; -use crate::onion::Onion; use crate::servers::swap::{SwapError, SwapServer, SwapServerImpl}; use crate::store::SwapStore; use crate::wallet::Wallet; -use crate::client::MixClient; +use grin_onion::crypto::comsig::{self, ComSignature}; +use grin_onion::onion::Onion; use grin_util::StopState; use jsonrpc_core::Value; use jsonrpc_derive::rpc; @@ -133,11 +133,11 @@ mod tests { use crate::config::ServerConfig; use crate::crypto::comsig::ComSignature; use crate::crypto::secp; - use crate::onion::test_util; use crate::servers::swap::mock::MockSwapServer; use crate::servers::swap::{SwapError, SwapServer}; use crate::servers::swap_rpc::{RPCSwapServer, SwapReq}; + use grin_onion::create_onion; use std::net::TcpListener; use std::sync::{Arc, Mutex}; @@ -208,7 +208,7 @@ mod tests { #[test] fn swap_success() -> Result<(), Box> { let commitment = secp::commit(1234, &secp::random_secret())?; - let onion = test_util::create_onion(&commitment, &vec![])?; + let onion = create_onion(&commitment, &vec![])?; let comsig = ComSignature::sign(1234, &secp::random_secret(), &onion.serialize()?)?; let swap = SwapReq { onion: onion.clone(), @@ -247,7 +247,7 @@ mod tests { #[test] fn swap_utxo_missing() -> Result<(), Box> { let commitment = secp::commit(1234, &secp::random_secret())?; - let onion = test_util::create_onion(&commitment, &vec![])?; + let onion = create_onion(&commitment, &vec![])?; let comsig = ComSignature::sign(1234, &secp::random_secret(), &onion.serialize()?)?; let swap = SwapReq { onion: onion.clone(), diff --git a/src/store.rs b/src/store.rs index cf5b689..2122cfc 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,7 +1,7 @@ -use crate::crypto::secp::{self, Commitment, RangeProof, SecretKey}; -use crate::onion::Onion; -use crate::util::{read_optional, write_optional}; use grin_core::core::hash::Hash; +use grin_onion::crypto::secp::{self, Commitment, RangeProof, SecretKey}; +use grin_onion::onion::Onion; +use grin_onion::util::{read_optional, write_optional}; use grin_core::core::Input; use grin_core::ser::{ @@ -244,13 +244,12 @@ impl SwapStore { #[cfg(test)] mod tests { - use crate::crypto::secp; - use crate::crypto::secp::test_util::{rand_commit, rand_hash, rand_proof}; - use crate::onion::test_util::rand_onion; use crate::store::{SwapData, SwapStatus, SwapStore}; use crate::StoreError; use grin_core::core::{Input, OutputFeatures}; use grin_core::global::{self, ChainTypes}; + use grin_onion::crypto::secp; + use grin_onion::test_util as onion_test_util; use rand::RngCore; use std::cmp::Ordering; @@ -264,11 +263,11 @@ mod tests { fn rand_swap_with_status(status: SwapStatus) -> SwapData { SwapData { excess: secp::random_secret(), - output_commit: rand_commit(), - rangeproof: Some(rand_proof()), - input: Input::new(OutputFeatures::Plain, rand_commit()), + output_commit: onion_test_util::rand_commit(), + rangeproof: Some(onion_test_util::rand_proof()), + input: Input::new(OutputFeatures::Plain, onion_test_util::rand_commit()), fee: rand::thread_rng().next_u64(), - onion: rand_onion(), + onion: onion_test_util::rand_onion(), status, } } @@ -279,12 +278,12 @@ mod tests { SwapStatus::Unprocessed } else if s == 1 { SwapStatus::InProcess { - kernel_hash: rand_hash(), + kernel_hash: onion_test_util::rand_hash(), } } else { SwapStatus::Completed { - kernel_hash: rand_hash(), - block_hash: rand_hash(), + kernel_hash: onion_test_util::rand_hash(), + block_hash: onion_test_util::rand_hash(), } }; rand_swap_with_status(status) @@ -331,7 +330,7 @@ mod tests { assert!(store.swap_exists(&swap.input.commit)?); swap.status = SwapStatus::InProcess { - kernel_hash: rand_hash(), + kernel_hash: onion_test_util::rand_hash(), }; let result = store.save_swap(&swap, false); assert_eq!( diff --git a/src/tx.rs b/src/tx.rs index 7cd47a9..73ca7fd 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -167,6 +167,47 @@ fn add_kernel_and_collect_fees( }) } +/// Builds a transaction kernel for the Grin network. +/// +/// Transaction kernels are a critical part of the Grin transaction process. Each transaction contains a +/// kernel. It includes features chosen for this transaction, a fee chosen for this transaction, and +/// a proof that the total sum of outputs, transaction fees and block reward equals the total sum of inputs. +/// The `build_kernel` function handles this process, building the kernel and handling any potential errors. +/// +/// # Arguments +/// +/// * `excess`: A reference to a `SecretKey`. This key is used as an excess value for the transaction. +/// The excess is a kind of cryptographic proof that the total sum of outputs and fees equals the +/// total sum of inputs. +/// * `fee`: An unsigned 64-bit integer representing the transaction fee in nanogrin. This is the fee +/// that will be paid to the miner who mines the block containing this transaction. +/// +/// # Returns +/// +/// The function returns a `Result` enum with `TxKernel` as the Ok variant and `TxError` as the Err variant. +/// If the kernel is successfully built, it is returned as part of the Ok variant. If there is an error at any point +/// during the process, it is returned as part of the Err variant. +/// +/// # Errors +/// +/// This function can return several types of errors, all defined in the `TxError` enum. These include: +/// +/// * `KernelFeeError`: There was an error building the kernel's fee fields. +/// * `KernelExcessError`: There was an error computing the kernel's excess. +/// * `KernelSigMessageError`: There was an error computing the kernel's signature message. +/// * `KernelSigError`: There was an error signing the kernel. +/// * `KernelVerifyError`: The built kernel failed to verify. +/// +/// # Example +/// +/// ```rust +/// use secp256k1zkp::key::SecretKey; +/// use crate::crypto::secp; +/// +/// let secret_key = SecretKey::new(&mut secp::rand::thread_rng()); +/// let fee = 10; // 10 nanogrin +/// let kernel = build_kernel(&secret_key, fee); +/// ``` pub fn build_kernel(excess: &SecretKey, fee: u64) -> Result { let mut kernel = TxKernel::with_features(KernelFeatures::Plain { fee: FeeFields::new(0, fee).map_err(TxError::KernelFeeError)?,