From a8e0d1b52c983a4fa38de967223eeaaf1dd7c76d Mon Sep 17 00:00:00 2001 From: cygnet Date: Sun, 2 Jun 2024 14:37:45 +0200 Subject: [PATCH] Move SilentPaymentAddress and Network to utils These structs are not meant to be used for the default sending and receiving flow, but can still be quite useful. Therefore the utils module is probably the best place for them. --- examples/create_wallet.rs | 2 +- examples/find_output.rs | 2 +- src/common.rs | 8 --- src/lib.rs | 1 - src/receiving.rs | 4 +- src/sending.rs | 120 ++--------------------------------- src/utils.rs | 5 ++ src/utils/common.rs | 129 ++++++++++++++++++++++++++++++++++++++ tests/vector_tests.rs | 2 +- 9 files changed, 143 insertions(+), 130 deletions(-) create mode 100644 src/utils/common.rs diff --git a/examples/create_wallet.rs b/examples/create_wallet.rs index a049b9d..6c37fd3 100644 --- a/examples/create_wallet.rs +++ b/examples/create_wallet.rs @@ -5,7 +5,7 @@ use bitcoin::bip32::{DerivationPath, Xpriv}; use bitcoin::secp256k1::Secp256k1; use silentpayments::receiving::{Label, Receiver}; -use silentpayments::Network; +use silentpayments::utils::Network; fn main() -> Result<(), Box> { let secp = Secp256k1::new(); diff --git a/examples/find_output.rs b/examples/find_output.rs index e400edc..77cfc3f 100644 --- a/examples/find_output.rs +++ b/examples/find_output.rs @@ -43,7 +43,7 @@ fn main() -> Result<(), Box> { scan_privkey.public_key(&secp), spend_privkey.public_key(&secp), change_label, - silentpayments::Network::Testnet, + silentpayments::utils::Network::Testnet, )?; let outpoints: Vec<(String, u32)> = tx diff --git a/src/common.rs b/src/common.rs index 6fc565e..0a8882e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,7 +2,6 @@ use crate::utils::hash::SharedSecretHash; use crate::Result; use bitcoin_hashes::Hash; use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey}; -use serde::{Deserialize, Serialize}; pub(crate) fn calculate_t_n(ecdh_shared_secret: &PublicKey, k: u32) -> Result { let hash = SharedSecretHash::from_ecdh_and_k(ecdh_shared_secret, k).to_byte_array(); @@ -18,10 +17,3 @@ pub(crate) fn calculate_P_n(B_spend: &PublicKey, t_n: Scalar) -> Result = std::result::Result; -pub type Network = common::Network; diff --git a/src/receiving.rs b/src/receiving.rs index 6d655d8..92082f0 100644 --- a/src/receiving.rs +++ b/src/receiving.rs @@ -17,8 +17,8 @@ use std::{ use crate::{ common::{calculate_P_n, calculate_t_n}, - utils::hash::LabelHash, - Error, Network, Result, + utils::{hash::LabelHash, Network}, + Error, Result, }; use bech32::ToBase32; use bimap::BiMap; diff --git a/src/sending.rs b/src/sending.rs index eb5a4ff..3bd8a6c 100644 --- a/src/sending.rs +++ b/src/sending.rs @@ -7,125 +7,13 @@ //! To do this, you can use [`calculate_partial_secret`](crate::utils::sending::calculate_partial_secret) from the `utils` module. //! See the [tests on github](https://github.com/cygnet3/rust-silentpayments/blob/master/tests/vector_tests.rs) //! for a concrete example. -use bech32::{FromBase32, ToBase32}; -use core::fmt; use secp256k1::{PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; use std::collections::HashMap; use crate::utils::sending::calculate_ecdh_shared_secret; -use crate::{common::calculate_t_n, error::Error, Network, Result}; - -/// A silent payment address struct. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -pub struct SilentPaymentAddress { - version: u8, - scan_pubkey: PublicKey, - m_pubkey: PublicKey, - network: Network, -} - -impl SilentPaymentAddress { - pub fn new( - scan_pubkey: PublicKey, - m_pubkey: PublicKey, - network: Network, - version: u8, - ) -> Result { - if version != 0 { - return Err(Error::GenericError( - "Can't have other version than 0 for now".to_owned(), - )); - } - - Ok(SilentPaymentAddress { - scan_pubkey, - m_pubkey, - network, - version, - }) - } - - pub fn get_scan_key(&self) -> PublicKey { - self.scan_pubkey - } - - pub fn get_spend_key(&self) -> PublicKey { - self.m_pubkey - } - - pub fn get_network(&self) -> Network { - self.network - } -} - -impl fmt::Display for SilentPaymentAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", >::into(*self)) - } -} - -impl TryFrom<&str> for SilentPaymentAddress { - type Error = Error; - - fn try_from(addr: &str) -> Result { - let (hrp, data, _variant) = bech32::decode(addr)?; - - if data.len() != 107 { - return Err(Error::GenericError("Address length is wrong".to_owned())); - } - - let version = data[0].to_u8(); - - let network = match hrp.as_str() { - "sp" => Network::Mainnet, - "tsp" => Network::Testnet, - "sprt" => Network::Regtest, - _ => { - return Err(Error::InvalidAddress(format!( - "Wrong prefix, expected \"sp\", \"tsp\", or \"sprt\", got \"{}\"", - &hrp - ))) - } - }; - - let data = Vec::::from_base32(&data[1..])?; - - let scan_pubkey = PublicKey::from_slice(&data[..33])?; - let m_pubkey = PublicKey::from_slice(&data[33..])?; - - SilentPaymentAddress::new(scan_pubkey, m_pubkey, network, version) - } -} - -impl TryFrom for SilentPaymentAddress { - type Error = Error; - - fn try_from(addr: String) -> Result { - addr.as_str().try_into() - } -} - -impl From for String { - fn from(val: SilentPaymentAddress) -> Self { - let hrp = match val.network { - Network::Testnet => "tsp", - Network::Regtest => "sprt", - Network::Mainnet => "sp", - }; - - let version = bech32::u5::try_from_u8(val.version).unwrap(); - - let B_scan_bytes = val.scan_pubkey.serialize(); - let B_m_bytes = val.m_pubkey.serialize(); - - let mut data = [B_scan_bytes, B_m_bytes].concat().to_base32(); - - data.insert(0, version); - - bech32::encode(hrp, data, bech32::Variant::Bech32m).unwrap() - } -} +use crate::utils::SilentPaymentAddress; +use crate::{common::calculate_t_n, Result}; /// Create outputs for a given set of silent payment recipients and their corresponding shared secrets. /// @@ -159,7 +47,7 @@ pub fn generate_recipient_pubkeys( HashMap::new(); for address in recipients { let address: SilentPaymentAddress = address.try_into()?; - let B_scan = address.scan_pubkey; + let B_scan = address.get_scan_key(); if let Some((_, payments)) = silent_payment_groups.get_mut(&B_scan) { payments.push(address); @@ -180,7 +68,7 @@ pub fn generate_recipient_pubkeys( let t_n = calculate_t_n(&ecdh_shared_secret, n)?; let res = t_n.public_key(&secp); - let reskey = res.combine(&addr.m_pubkey)?; + let reskey = res.combine(&addr.get_spend_key())?; let (reskey_xonly, _) = reskey.x_only_public_key(); let entry = result.entry(addr.into()).or_default(); diff --git a/src/utils.rs b/src/utils.rs index 1c66ac1..15c05ef 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,6 +8,11 @@ pub mod receiving; #[cfg(feature = "sending")] pub mod sending; +mod common; + +pub use common::Network; +pub use common::SilentPaymentAddress; + /// [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)-defined 'Nothing Up My Sleeve' point. pub const NUMS_H: [u8; 32] = [ 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, diff --git a/src/utils/common.rs b/src/utils/common.rs new file mode 100644 index 0000000..1745b2d --- /dev/null +++ b/src/utils/common.rs @@ -0,0 +1,129 @@ +use core::fmt; + +use crate::Error; +use crate::Result; +use bech32::{FromBase32, ToBase32}; +use secp256k1::PublicKey; +use serde::{Deserialize, Serialize}; + +/// The network format used for this silent payment address. +/// +/// There are three network types: Mainnet (`sp1..`), Testnet (`tsp1..`), and Regtest (`sprt1..`). +/// Signet uses the same network type as Testnet. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] +pub enum Network { + Mainnet, + Testnet, + Regtest, +} + +/// A silent payment address struct that can be used to deserialize a silent payment address string. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub struct SilentPaymentAddress { + version: u8, + scan_pubkey: PublicKey, + m_pubkey: PublicKey, + network: Network, +} + +impl SilentPaymentAddress { + pub fn new( + scan_pubkey: PublicKey, + m_pubkey: PublicKey, + network: Network, + version: u8, + ) -> Result { + if version != 0 { + return Err(Error::GenericError( + "Can't have other version than 0 for now".to_owned(), + )); + } + + Ok(SilentPaymentAddress { + scan_pubkey, + m_pubkey, + network, + version, + }) + } + + pub fn get_scan_key(&self) -> PublicKey { + self.scan_pubkey + } + + pub fn get_spend_key(&self) -> PublicKey { + self.m_pubkey + } + + pub fn get_network(&self) -> Network { + self.network + } +} + +impl fmt::Display for SilentPaymentAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", >::into(*self)) + } +} + +impl TryFrom<&str> for SilentPaymentAddress { + type Error = Error; + + fn try_from(addr: &str) -> Result { + let (hrp, data, _variant) = bech32::decode(addr)?; + + if data.len() != 107 { + return Err(Error::GenericError("Address length is wrong".to_owned())); + } + + let version = data[0].to_u8(); + + let network = match hrp.as_str() { + "sp" => Network::Mainnet, + "tsp" => Network::Testnet, + "sprt" => Network::Regtest, + _ => { + return Err(Error::InvalidAddress(format!( + "Wrong prefix, expected \"sp\", \"tsp\", or \"sprt\", got \"{}\"", + &hrp + ))) + } + }; + + let data = Vec::::from_base32(&data[1..])?; + + let scan_pubkey = PublicKey::from_slice(&data[..33])?; + let m_pubkey = PublicKey::from_slice(&data[33..])?; + + SilentPaymentAddress::new(scan_pubkey, m_pubkey, network, version) + } +} + +impl TryFrom for SilentPaymentAddress { + type Error = Error; + + fn try_from(addr: String) -> Result { + addr.as_str().try_into() + } +} + +impl From for String { + fn from(val: SilentPaymentAddress) -> Self { + let hrp = match val.network { + Network::Testnet => "tsp", + Network::Regtest => "sprt", + Network::Mainnet => "sp", + }; + + let version = bech32::u5::try_from_u8(val.version).unwrap(); + + let B_scan_bytes = val.scan_pubkey.serialize(); + let B_m_bytes = val.m_pubkey.serialize(); + + let mut data = [B_scan_bytes, B_m_bytes].concat().to_base32(); + + data.insert(0, version); + + bech32::encode(hrp, data, bech32::Variant::Bech32m).unwrap() + } +} diff --git a/tests/vector_tests.rs b/tests/vector_tests.rs index cbe6280..205d310 100644 --- a/tests/vector_tests.rs +++ b/tests/vector_tests.rs @@ -10,8 +10,8 @@ mod tests { calculate_ecdh_shared_secret, calculate_tweak_data, get_pubkey_from_input, is_p2tr, }, sending::calculate_partial_secret, + Network, }, - Network, }; use std::{collections::HashSet, io::Cursor, str::FromStr};