Skip to content

Commit

Permalink
bindings:implement a generic interface
Browse files Browse the repository at this point in the history
  • Loading branch information
pythcoiner committed Jan 18, 2025
1 parent 761ece3 commit d308f82
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 8 deletions.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
[package]
name = "rust-joinstr"
name = "joinstr"
version = "0.0.1"
edition = "2021"

[lib]
crate-type = ["rlib", "cdylib", "staticlib"]

[dependencies]
home = "=0.5.9"
bitcoin = "=0.32.2"
bip39 = { version = "2.0.0", features = ["rand"] }
hex-conservative = "0.2.1"
miniscript = {version = "12.2.0", features = ["base64", "serde"]}
simple_electrum_client = { git = "https://github.com/pythcoiner/simple_electrum_client.git", branch = "master"}
simple_electrum_client = { git = "https://github.com/pythcoiner/simple_electrum_client.git", branch = "openssl_vendored"}
nostr-sdk = "0.35.0"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
tokio = "1.40.0"
log = "0.4.22"
env_logger = "=0.10.2"
rand = "0.8.5"
lazy_static = "1.5.0"

[dev-dependencies]
electrsd = { git = "https://github.com/pythcoiner/electrsd.git", branch = "buffered_logs"}
Expand Down
28 changes: 28 additions & 0 deletions joinstr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

typedef enum {
None = 0,
Tokio,
CastString,
Json,
CString,
ListPools
} Error;

typedef struct Pools {
const char* pools;
Error error;
} Pools;

Pools list_pools(uint64_t back, uint64_t timeout, const char* relay);

#ifdef __cplusplus
}
#endif

111 changes: 111 additions & 0 deletions src/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::{fmt::Display, time::Duration};

use bitcoin::Network;
use nostr_sdk::Keys;
use tokio::time::sleep;

use crate::nostr::client::NostrClient;

pub enum Error {
Unknown,
NostrClient(crate::nostr::client::Error),
SerdeJson(serde_json::Error),
}

impl From<crate::nostr::client::Error> for Error {
fn from(value: crate::nostr::client::Error) -> Self {
Self::NostrClient(value)
}
}

impl From<serde_json::Error> for Error {
fn from(value: serde_json::Error) -> Self {
Self::SerdeJson(value)
}
}

impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Unknown => write!(f, "Unknown error!"),
Error::NostrClient(e) => write!(f, "NostrClient error: {:?}", e),
Error::SerdeJson(e) => write!(f, "serde_json error: {:?}", e),
}
}
}

pub struct PoolConfig {
pub denomination: f64,
pub fee: u32,
pub max_duration: u32,
pub peers: u8,
pub network: Network,
}

pub struct PeerConfig {
pub outpoint: String,
pub electrum: String,
pub mnemonics: String,
pub address: String,
pub relay: String,
}

/// Initiate and participate to a coinjoin
///
/// # Arguments
/// * `config` - configuration of the pool to initiate
/// * `peer` - information about the peer
///
pub fn initiate_coinjoin(
_config: PoolConfig,
_peer: PeerConfig,
) -> Result<String /* Txid */, Error> {
// TODO:
Ok(String::new())
}

/// List available pools
///
/// # Arguments
/// * `back` - how many second back look in the past
/// * `timeout` - how many microseconds we will wait before fetching relay notifications
/// * `relay` - the relay url, must start w/ `wss://` or `ws://`
///
/// # Returns a [`Vec`] of [`String`] containing a json serialization of a [`Pool`]
pub async fn list_pools(
back: u64,
timeout: u64,
relay: String,
) -> Result<Vec<String /* Pool */>, Error> {
let mut pools = Vec::new();
let relays = vec![relay];
let mut pool_listener = NostrClient::new("pool_listener")
.relays(&relays)?
.keys(Keys::generate())?;
pool_listener.connect_nostr().await.unwrap();
// subscribe to 2020 event up to 1 day back in time
pool_listener.subscribe_pools(back).await.unwrap();

sleep(Duration::from_micros(timeout)).await;

while let Some(pool) = pool_listener.receive_pool_notification()? {
let str = serde_json::to_string(&pool)?;
pools.push(str)
}

Ok(pools)
}

/// Try to join an already initiated coinjoin
///
/// # Arguments
/// * `pool` - [`String`] containing a json serialization of a [`Pool`]
/// * `peer` - information about the peer
///
pub fn join_coinjoin(
_pool: String, /* Pool */
_peer: PeerConfig,
) -> Result<String /* Txid */, Error> {
// TODO:
Ok(String::new())
}
2 changes: 1 addition & 1 deletion src/joinstr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct Joinstr<'a> {
final_tx: Option<miniscript::bitcoin::Transaction>,
}

impl<'a> Default for Joinstr<'a> {
impl Default for Joinstr<'_> {
fn default() -> Self {
Self {
initiator: false,
Expand Down
128 changes: 128 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,135 @@
#![allow(dead_code)]
pub mod coinjoin;
pub mod electrum;
pub mod interface;
pub mod joinstr;
pub mod nostr;
pub mod signer;
pub mod utils;

use lazy_static::lazy_static;
use serde::Serialize;
use std::{
ffi::{c_char, CStr, CString},
ptr::null,
sync::Mutex,
};
use tokio::runtime::Runtime;

lazy_static! {
static ref RT: Mutex<Runtime> = Mutex::new(Runtime::new().unwrap());
}

fn cast_to_cstring<T>(value: T) -> Result<CString, Error>
where
T: Serialize,
{
match serde_json::to_string(&value) {
Ok(v) => match CString::new(v) {
Ok(s) => Ok(s),
Err(_) => {
log::error!("fail to convert json string to C string!");
Err(Error::Json)
}
},
Err(_) => {
log::error!("fail to convert pool list to it json string representation!");
Err(Error::CString)
}
}
}

#[repr(C)]
#[derive(Clone, Copy)]
pub enum Network {
/// Mainnet Bitcoin.
Bitcoin,
/// Bitcoin's testnet network.
Testnet,
/// Bitcoin's signet network.
Signet,
/// Bitcoin's regtest network.
Regtest,
}

#[repr(C)]
pub struct PoolConfig {
pub denomination: f64,
pub fee: u32,
pub max_duration: u32,
pub peers: u8,
pub network: Network,
}

#[repr(C)]
pub struct PeerConfig {
pub outpoint: *mut c_char,
pub electrum: *mut c_char,
pub mnemonics: *mut c_char,
pub address: *mut c_char,
pub relay: *mut c_char,
}

#[repr(C)]
#[derive(Clone, Copy)]
pub enum Error {
None,
Tokio,
CastString,
Json,
CString,
ListPools,
}


impl Pools {
pub fn ok(pools: CString) -> Self {
Pools {
pools: pools.into_raw(),
error: Error::None,
}
}
pub fn error(e: Error) -> Self {
Pools {
pools: null(),
error: e,
}
}
}

#[repr(C)]
pub struct Pools {
pools: *const c_char,
error: Error,
}

#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn list_pools(back: u64, timeout: u64, relay: *const c_char) -> Pools {
let relay = unsafe { CStr::from_ptr(relay) };
let relay = if let Ok(relay) = relay.to_str() {
relay.to_owned()
} else {
log::error!("list_pool(): fail to cast `relay` arg!");
return Pools::error(Error::CastString);
};
let future = async {
match interface::list_pools(back, timeout, relay).await {
Ok(p) => cast_to_cstring(p),
Err(e) => {
log::error!("list_pools() fail: {}", e);
Err(Error::ListPools)
}
}
};

if let Ok(runtime) = RT.lock() {
match runtime.block_on(future) {
Ok(p) => Pools::ok(p),
Err(e) => Pools::error(e),
}
} else {
log::error!("list_pools(): fail to get a lock on tokio runtime!");
Pools::error(Error::Tokio)
}
}
1 change: 1 addition & 0 deletions src/nostr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct InputDataSigned {
pub amount: Option<Amount>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Error {
NoInput,
TooMuchInputs,
Expand Down
2 changes: 1 addition & 1 deletion tests/coinjoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ pub mod utils;
use crate::utils::{funded_wallet, generate};

use electrsd::bitcoind::bitcoincore_rpc::RpcApi;
use joinstr::{coinjoin::CoinJoin, electrum::Client, signer::CoinPath};
use miniscript::bitcoin::Amount;
use rust_joinstr::{coinjoin::CoinJoin, electrum::Client, signer::CoinPath};

#[test]
fn simple_tx() {
Expand Down
6 changes: 3 additions & 3 deletions tests/joinstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ use std::{sync::Once, time::Duration};

use crate::utils::{bootstrap_electrs, funded_wallet_with_bitcoind};
use electrsd::bitcoind::bitcoincore_rpc::RpcApi;
use miniscript::bitcoin::Network;
use rust_joinstr::{
use joinstr::{
electrum::Client,
signer::{CoinPath, WpkhHotSigner},
utils::now,
};
use miniscript::bitcoin::Network;

use joinstr::{joinstr::Joinstr, nostr::client::NostrClient};
use nostr_sdk::{Event, Keys, Kind};
use nostrd::NostrD;
use rust_joinstr::{joinstr::Joinstr, nostr::client::NostrClient};
use tokio::time::sleep;

static INIT: Once = Once::new();
Expand Down
2 changes: 1 addition & 1 deletion tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use electrsd::{
},
ElectrsD,
};
use joinstr::{electrum::Client, signer::WpkhHotSigner};
use miniscript::bitcoin::{Address, Amount, Network};
use rust_joinstr::{electrum::Client, signer::WpkhHotSigner};

pub fn bootstrap_electrs() -> (
String, /* url */
Expand Down

0 comments on commit d308f82

Please sign in to comment.