Skip to content

Commit

Permalink
project: add a joinstr_wallet crate
Browse files Browse the repository at this point in the history
  • Loading branch information
pythcoiner committed Feb 23, 2025
1 parent 9873cab commit 8282aac
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ resolver = "2"
members = [
"rust/joinstr",
"rust/simple_nostr_client",
"rust/simple_electrum_client",
"rust/simple_electrum_client",
"rust/joinstr_wallet",
]
4 changes: 3 additions & 1 deletion rust/joinstr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub mod utils;
pub use bip39;
pub use log;
pub use miniscript;
pub use serde;
pub use serde_json;

#[cfg(feature = "async")]
pub use nostr_sdk;
Expand All @@ -19,7 +21,7 @@ use std::{
ptr::null,
};

fn serialize_to_cstring<T>(value: T) -> Result<CString, Error>
pub fn serialize_to_cstring<T>(value: T) -> Result<CString, Error>
where
T: Serialize,
{
Expand Down
16 changes: 16 additions & 0 deletions rust/joinstr_wallet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "joinstr_wallet"
version = "0.1.0"
edition = "2021"

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

[dependencies]
dirs = "6.0.0"
joinstr = { path = "../joinstr" }
libc = "0.2.170"
serde = { version = "1.0.218", features = ["derive"] }
url = "2.5.4"


2 changes: 2 additions & 0 deletions rust/joinstr_wallet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod settings;
pub use settings::*;
223 changes: 223 additions & 0 deletions rust/joinstr_wallet/src/settings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
use std::{
ffi::{c_char, c_int, CStr, CString},
fs::File,
io::Read,
path::{Path, PathBuf},
ptr,
str::FromStr,
};

use libc::malloc;

use joinstr::{bip39::Mnemonic, serde_json};
use serde::{Deserialize, Serialize};
use url::Url;

type MutStrPtr = *mut *mut c_char;
type ConstStr = *const c_char;

fn datadir() -> PathBuf {
#[cfg(target_os = "linux")]
let mut dir = {
let mut dir = dirs::home_dir().unwrap();
dir.push(".joinstr");
dir
};

#[cfg(not(target_os = "linux"))]
let mut dir = {
let mut dir = dirs::config_dir().unwrap();
dir.push("Joinstr");
dir
};

maybe_create_dir(&dir);

dir.push("joinstr.conf");

dir
}

fn maybe_create_dir(dir: &PathBuf) {
if !dir.exists() {
#[cfg(unix)]
{
use std::fs::DirBuilder;
use std::os::unix::fs::DirBuilderExt;

let mut builder = DirBuilder::new();
builder.mode(0o700).recursive(true).create(dir).unwrap();
}

#[cfg(not(unix))]
std::fs::create_dir_all(dir).unwrap();
}
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn is_mnemonic_valid(mnemonic: ConstStr) -> c_int {
let cstr = unsafe { CStr::from_ptr(mnemonic) };
let mnemonic = match cstr.to_str() {
Ok(r) => r,
Err(_) => return -1,
};
match Mnemonic::from_str(mnemonic).is_ok() {
true => 0,
false => -2,
}
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn is_electrum_valid(addr: ConstStr) -> c_int {
let cstr = unsafe { CStr::from_ptr(addr) };
let electrum = match cstr.to_str() {
Ok(r) => r,
Err(_) => return -1,
};
let separators = electrum.chars().filter(|c| *c == ':').count();
if separators != 1 {
return -2;
}
let (url, port) = electrum.split_once(':').expect("checked");
let port = u16::from_str(port).is_ok();
let url = Url::parse(url).is_ok();
if !url {
-3
} else if !port {
-4
} else {
0
}
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn is_relay_valid(addr: ConstStr) -> c_int {
let cstr = unsafe { CStr::from_ptr(addr) };
let url = match cstr.to_str() {
Ok(r) => r,
Err(_) => return -1,
};
match Url::parse(url).is_ok() {
true => 0,
false => -2,
}
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn save_settings(
mnemonics: ConstStr,
electrum: ConstStr,
relay: ConstStr,
) -> c_int {
if is_mnemonic_valid(mnemonics) != 0 {
return -1;
}
if is_electrum_valid(electrum) != 0 {
return -2;
}
if is_relay_valid(relay) != 0 {
return -3;
}
let mnemonics = unsafe { CStr::from_ptr(mnemonics) }.to_str();
let electrum = unsafe { CStr::from_ptr(electrum) }.to_str();
let relay = unsafe { CStr::from_ptr(relay) }.to_str();

if let (Ok(mnemonics), Ok(electrum), Ok(relay)) = (mnemonics, electrum, relay) {
if Settings::new(mnemonics, electrum, relay).to_file(&datadir()) != 0 {
-4
} else {
0
}
} else {
-5
}
}

unsafe fn write_string(src: &str, dst: *mut *mut c_char) -> c_int {
let c_str = match CString::new(src) {
Ok(r) => r,
Err(_) => return -1,
};
let len = c_str.as_bytes_with_nul().len();
let mem = malloc(len) as *mut c_char;
if mem.is_null() {
return -2;
}
ptr::copy_nonoverlapping(c_str.as_ptr(), mem, len);
*dst = mem;
0
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn load_settings(
mnemonics: MutStrPtr,
electrum: MutStrPtr,
relay: MutStrPtr,
) -> c_int {
if mnemonics.is_null() || electrum.is_null() || relay.is_null() {
return -1;
}
let settings = match Settings::from_file(&datadir()) {
Some(s) => s,
None => return -2,
};

if write_string(&settings.mnemonics, mnemonics) != 0 {
return -3;
}
if write_string(&settings.electrum, electrum) != 0 {
return -4;
}
if write_string(&settings.relay, relay) != 0 {
return -5;
}

0
}

#[derive(Debug, Serialize, Deserialize)]
struct Settings {
pub mnemonics: String,
pub electrum: String,
pub relay: String,
}

impl Settings {
pub fn new(mnemonics: &str, electrum: &str, relay: &str) -> Self {
Settings {
mnemonics: mnemonics.into(),
electrum: electrum.into(),
relay: relay.into(),
}
}

pub fn to_file(&self, path: &Path) -> c_int {
let path: &str = &path.to_string_lossy();
let file = match File::create(path) {
Ok(f) => f,
Err(_) => return -1,
};
if serde_json::to_writer_pretty(file, self).is_ok() {
0
} else {
-2
}
}

pub fn from_file(path: &Path) -> Option<Self> {
if !path.exists() || !path.is_file() {
return None;
}

let mut file = File::open(path).ok()?;
let mut settings_str = String::new();
let _conf_size = file.read_to_string(&mut settings_str).ok()?;
let conf: Self = serde_json::from_str(&settings_str).ok()?;
Some(conf)
}
}

0 comments on commit 8282aac

Please sign in to comment.