Skip to content

Commit

Permalink
Add ability to configure Bitcoin Network
Browse files Browse the repository at this point in the history
  • Loading branch information
nobu-maeda committed May 8, 2024
1 parent b5962e3 commit f7bd37c
Show file tree
Hide file tree
Showing 11 changed files with 443 additions and 136 deletions.
122 changes: 100 additions & 22 deletions src/common/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,17 @@ impl SerdeGenericTrait for SerdeGenericsPlaceholder {
}

#[derive(
Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Debug, EnumString, Display, IntoStaticStr,
PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize, EnumString, Display, IntoStaticStr,
)]
pub enum BitcoinNetwork {
Mainnet,
Testnet,
Signet,
Regtest,
}

#[derive(
PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize, EnumString, Display, IntoStaticStr,
)]
pub enum BitcoinSettlementMethod {
Onchain,
Expand Down Expand Up @@ -121,9 +131,9 @@ pub enum FiatPaymentMethod {
Venmo,
}

#[derive(PartialEq, Eq, Hash, Clone, Debug, Display, Deserialize, Serialize, IntoStaticStr)]
#[derive(PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize, Display, IntoStaticStr)]
pub enum ObligationKind {
Bitcoin(Option<BitcoinSettlementMethod>),
Bitcoin(BitcoinNetwork, Option<BitcoinSettlementMethod>),
Fiat(Currency, Option<FiatPaymentMethod>),
Custom(String),
}
Expand All @@ -133,26 +143,26 @@ const OBLIGATION_KIND_SPLIT_CHAR: &str = "-";
impl ObligationKind {
pub fn is_bitcoin(&self) -> bool {
match self {
ObligationKind::Bitcoin(_) => true,
ObligationKind::Bitcoin(_, _) => true,
ObligationKind::Fiat(_, _) => false,
ObligationKind::Custom(_) => false,
}
}

pub fn is_same_currency_as(&self, kind: ObligationKind) -> bool {
match self {
ObligationKind::Bitcoin(_) => match kind {
ObligationKind::Bitcoin(_) => true,
ObligationKind::Bitcoin(_, _) => match kind {
ObligationKind::Bitcoin(_, _) => true,
ObligationKind::Fiat(_, _) => false,
ObligationKind::Custom(_) => false,
},
ObligationKind::Fiat(self_currency, _) => match kind {
ObligationKind::Bitcoin(_) => false,
ObligationKind::Bitcoin(_, _) => false,
ObligationKind::Fiat(kind_currency, _) => self_currency.to_owned() == kind_currency,
ObligationKind::Custom(_) => false,
},
ObligationKind::Custom(self_custom) => match kind {
ObligationKind::Bitcoin(_) => false,
ObligationKind::Bitcoin(_, _) => false,
ObligationKind::Fiat(_, _) => false,
ObligationKind::Custom(kind_custom) => self_custom.to_owned() == kind_custom,
},
Expand All @@ -161,19 +171,33 @@ impl ObligationKind {

pub fn to_tag_strings(&self) -> HashSet<String> {
let mut tag_string_set: HashSet<String>;
let obligation_kind_prefix_bitcoin = ObligationKind::Bitcoin(None).to_string();
let obligation_kind_prefix_bitcoin =
ObligationKind::Bitcoin(BitcoinNetwork::Mainnet, None).to_string();
let obligation_kind_prefix_fiat = ObligationKind::Fiat(Currency::XXX, None).to_string();
let obligation_kind_prefix_custom = ObligationKind::Custom("".to_string()).to_string();

match self {
ObligationKind::Bitcoin(settlement_method) => {
ObligationKind::Bitcoin(network, settlement_method) => {
let prefix_string = obligation_kind_prefix_bitcoin;
tag_string_set = HashSet::from([prefix_string.clone()]);
let mut networked_string = prefix_string.clone();

match network {
BitcoinNetwork::Mainnet => {}
BitcoinNetwork::Testnet | BitcoinNetwork::Signet | BitcoinNetwork::Regtest => {
networked_string = format!(
"{}{}{}",
prefix_string,
OBLIGATION_KIND_SPLIT_CHAR,
network.to_string()
);
}
}
tag_string_set = HashSet::from([networked_string.clone()]);

if let Some(settlement_method) = settlement_method {
let tag_string = format!(
"{}{}{}",
prefix_string,
networked_string,
OBLIGATION_KIND_SPLIT_CHAR,
settlement_method.to_string()
);
Expand Down Expand Up @@ -218,7 +242,8 @@ impl ObligationKind {
}

pub fn from_tag_strings(tags: HashSet<String>) -> Result<HashSet<ObligationKind>, N3xbError> {
let obligation_kind_prefix_bitcoin = ObligationKind::Bitcoin(None).to_string();
let obligation_kind_prefix_bitcoin =
ObligationKind::Bitcoin(BitcoinNetwork::Mainnet, None).to_string();
let obligation_kind_prefix_fiat = ObligationKind::Fiat(Currency::XXX, None).to_string();
let obligation_kind_prefix_custom = ObligationKind::Custom("".to_string()).to_string();

Expand Down Expand Up @@ -255,9 +280,22 @@ impl ObligationKind {
}

if &obligation_kind_prefix_bitcoin == kind_prefix.as_ref().unwrap() {
if splits_set.len() > 1 {
let bitcoin_method = BitcoinSettlementMethod::from_str(splits_set[1])?;
obligation_kinds.insert(ObligationKind::Bitcoin(Some(bitcoin_method)));
if splits_set.len() > 2 {
let network = BitcoinNetwork::from_str(splits_set[1])?;
let method = BitcoinSettlementMethod::from_str(splits_set[2])?;
obligation_kinds.insert(ObligationKind::Bitcoin(network, Some(method)));
} else if splits_set.len() > 1 {
match BitcoinSettlementMethod::from_str(splits_set[1]) {
Ok(method) => {
obligation_kinds.insert(ObligationKind::Bitcoin(
BitcoinNetwork::Mainnet,
Some(method),
));
}
Err(_) => {
continue;
}
}
}
} else if &obligation_kind_prefix_fiat == kind_prefix.as_ref().unwrap() {
if splits_set.len() > 1 {
Expand Down Expand Up @@ -313,8 +351,11 @@ mod tests {
}

#[test]
fn bitcoin_onchain_obligation_kind_to_tags() {
let obligation_kind = ObligationKind::Bitcoin(Some(BitcoinSettlementMethod::Onchain));
fn bitcoin_mainnet_onchain_obligation_kind_to_tags() {
let obligation_kind = ObligationKind::Bitcoin(
BitcoinNetwork::Mainnet,
Some(BitcoinSettlementMethod::Onchain),
);
let obligation_tags = obligation_kind.to_tag_strings();
let expected_tags = HashSet::from(["Bitcoin-Onchain".to_string(), "Bitcoin".to_string()]);
print!(
Expand All @@ -325,12 +366,49 @@ mod tests {
}

#[test]
fn bitcoin_onchain_obligation_kind_from_tags() {
fn bitcoin_testnet_onchain_obligation_kind_to_tags() {
let obligation_kind = ObligationKind::Bitcoin(
BitcoinNetwork::Testnet,
Some(BitcoinSettlementMethod::Onchain),
);
let obligation_tags = obligation_kind.to_tag_strings();
let expected_tags = HashSet::from([
"Bitcoin-Testnet-Onchain".to_string(),
"Bitcoin-Testnet".to_string(),
]);
print!(
"Obligation: {:?} Expected: {:?}",
obligation_tags, expected_tags
);
assert_eq!(obligation_tags, expected_tags);
}

#[test]
fn bitcoin_mainnet_onchain_obligation_kind_from_tags() {
let obligation_tags = HashSet::from(["Bitcoin-Onchain".to_string(), "Bitcoin".to_string()]);
let obligation_kinds = ObligationKind::from_tag_strings(obligation_tags).unwrap();
let expected_kinds = HashSet::from([ObligationKind::Bitcoin(Some(
BitcoinSettlementMethod::Onchain,
))]);
let expected_kinds: HashSet<ObligationKind> = HashSet::from([ObligationKind::Bitcoin(
BitcoinNetwork::Mainnet,
Some(BitcoinSettlementMethod::Onchain),
)]);
print!(
"Obligation Kind: {:?} Expected: {:?}",
obligation_kinds, expected_kinds
);
assert_eq!(obligation_kinds, expected_kinds);
}

#[test]
fn bitcoin_signet_onchain_obligation_kind_from_tags() {
let obligation_tags = HashSet::from([
"Bitcoin-Signet-Onchain".to_string(),
"Bitcoin-Signet".to_string(),
]);
let obligation_kinds = ObligationKind::from_tag_strings(obligation_tags).unwrap();
let expected_kinds = HashSet::from([ObligationKind::Bitcoin(
BitcoinNetwork::Signet,
Some(BitcoinSettlementMethod::Onchain),
)]);
print!(
"Obligation Kind: {:?} Expected: {:?}",
obligation_kinds, expected_kinds
Expand Down
46 changes: 31 additions & 15 deletions src/comms/comms.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Borrow;
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
use std::path::Path;
Expand All @@ -14,7 +15,9 @@ use tokio::sync::{mpsc, oneshot};
use uuid::Uuid;

use crate::common::error::N3xbError;
use crate::common::types::{EventIdString, ObligationKind, SerdeGenericTrait, SerdeGenericType};
use crate::common::types::{
BitcoinNetwork, EventIdString, ObligationKind, SerdeGenericTrait, SerdeGenericType,
};
use crate::offer::Offer;
use crate::order::{
EventKind, FilterTag, MakerObligation, Order, OrderEnvelope, OrderTag, TakerObligation,
Expand Down Expand Up @@ -259,29 +262,32 @@ impl Comms {

pub(crate) async fn new(
trade_engine_name: impl Into<String>,
network: impl Borrow<BitcoinNetwork>,
data_dir_path: impl AsRef<Path>,
) -> Self {
let secp = Secp256k1::new();
let (secret_key, _) = secp.generate_keypair(&mut OsRng);
Self::new_with_key(secret_key, trade_engine_name, data_dir_path).await
Self::new_with_key(secret_key, trade_engine_name, network, data_dir_path).await
}

pub(crate) async fn new_with_key(
secret_key: SecretKey,
trade_engine_name: impl Into<String>,
network: impl Borrow<BitcoinNetwork>,
data_dir_path: impl AsRef<Path>,
) -> Self {
let client = Self::new_nostr_client(secret_key).await;
Self::new_with_nostr_client(client, trade_engine_name, data_dir_path).await
Self::new_with_nostr_client(client, trade_engine_name, network, data_dir_path).await
}

pub(super) async fn new_with_nostr_client(
client: Client,
trade_engine_name: impl Into<String>,
network: impl Borrow<BitcoinNetwork>,
data_dir_path: impl AsRef<Path>,
) -> Self {
let (tx, rx) = mpsc::channel::<CommsRequest>(Self::INTEFACER_REQUEST_CHANNEL_SIZE);
let actor = CommsActor::new(rx, trade_engine_name, client, data_dir_path).await;
let actor = CommsActor::new(rx, trade_engine_name, network, client, data_dir_path).await;
let task_handle = tokio::spawn(async move { actor.run().await });
Self { tx, task_handle }
}
Expand Down Expand Up @@ -387,6 +393,7 @@ pub(super) enum CommsRequest {
pub(super) struct CommsActor {
rx: mpsc::Receiver<CommsRequest>,
trade_engine_name: String,
network: BitcoinNetwork,
pubkey: XOnlyPublicKey,
data: CommsData,
client: Client,
Expand All @@ -399,6 +406,7 @@ impl CommsActor {
pub(super) async fn new(
rx: mpsc::Receiver<CommsRequest>,
trade_engine_name: impl Into<String>,
network: impl Borrow<BitcoinNetwork>,
client: Client,
data_dir_path: impl AsRef<Path>,
) -> Self {
Expand All @@ -419,6 +427,7 @@ impl CommsActor {
let actor = CommsActor {
rx,
trade_engine_name: trade_engine_name.into(),
network: network.borrow().to_owned(),
pubkey,
data,
client,
Expand Down Expand Up @@ -1002,7 +1011,7 @@ impl CommsActor {
) {
let order_tags = OrderTag::from_filter_tags(filter_tags, &self.trade_engine_name);

let filter = Self::create_event_tag_filter(order_tags);
let filter = Self::create_event_tag_filter(order_tags, &self.network);
let timeout = Duration::from_secs(1);
let events = match self.client.get_events_of(vec![filter], Some(timeout)).await {
Ok(events) => events,
Expand Down Expand Up @@ -1181,15 +1190,19 @@ impl CommsActor {
order_envelopes
}

fn consume_tags_for_filter(tags: Vec<OrderTag>, filter: Filter) -> Filter {
fn consume_tags_for_filter(
tags: Vec<OrderTag>,
filter: Filter,
network: impl Borrow<BitcoinNetwork>,
) -> Filter {
if let Some(tag) = tags.first() {
match tag {
OrderTag::TradeUUID(trade_uuid) => {
let filter = filter.custom_tag(
Alphabet::try_from(tag.key()).unwrap(),
[trade_uuid.to_string()].to_vec(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network)
}
OrderTag::MakerObligations(obligations) => {
let filter = filter.custom_tag(
Expand All @@ -1200,7 +1213,7 @@ impl CommsActor {
.flat_map(|kind| kind.to_tag_strings())
.collect(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network.borrow())
}
OrderTag::TakerObligations(obligations) => {
let filter = filter.custom_tag(
Expand All @@ -1211,7 +1224,7 @@ impl CommsActor {
.flat_map(|kind| kind.to_tag_strings())
.collect(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network.borrow())
}
OrderTag::TradeDetailParameters(parameters) => {
let filter = filter.custom_tag(
Expand All @@ -1220,38 +1233,41 @@ impl CommsActor {
.into_iter()
.collect(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network)
}
OrderTag::TradeEngineName(name) => {
let filter = filter.custom_tag(
Alphabet::try_from(tag.key()).unwrap(),
[name.to_owned()].to_vec(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network)
}
OrderTag::EventKind(kind) => {
let filter = filter.custom_tag(
Alphabet::try_from(tag.key()).unwrap(),
[kind.to_string()].to_vec(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network)
}
OrderTag::ApplicationTag(app_tag) => {
let filter = filter.custom_tag(
Alphabet::try_from(tag.key()).unwrap(),
[app_tag.to_owned()].to_vec(),
);
Self::consume_tags_for_filter(tags[1..].to_vec(), filter)
Self::consume_tags_for_filter(tags[1..].to_vec(), filter, network)
}
}
} else {
filter
}
}

fn create_event_tag_filter(tags: Vec<OrderTag>) -> Filter {
fn create_event_tag_filter(
tags: Vec<OrderTag>,
network: impl Borrow<BitcoinNetwork>,
) -> Filter {
let filter = Filter::new().kind(Self::MAKER_ORDER_NOTE_KIND);
Self::consume_tags_for_filter(tags, filter)
Self::consume_tags_for_filter(tags, filter, network)
}

async fn send_peer_message(
Expand Down
Loading

0 comments on commit f7bd37c

Please sign in to comment.