diff --git a/.gitignore b/.gitignore index e3579b6..7bd123b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ /rust/joinstr/Cargo.lock /rust/simple_nostr_client/Cargo.lock /rust/simple_electrum_client/Cargo.lock +/dart/.dart_tool /dart/ios/ /dart/android/ +/dart/lib/ +/rust/joinstr/include Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 23866fb..27b7505 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,5 @@ resolver = "2" members = [ "rust/joinstr", "rust/simple_nostr_client", - "rust/simple_electrum_client", + "rust/simple_electrum_client", ] diff --git a/build_dart.sh b/contrib/bindings.sh similarity index 71% rename from build_dart.sh rename to contrib/bindings.sh index 1023eca..8ea5535 100755 --- a/build_dart.sh +++ b/contrib/bindings.sh @@ -3,10 +3,39 @@ # Exit if any command fails set -e +# Generate C bindings first as C headers needed to generate dart bindings + +# Remove old headers +rm -f rust/joinstr/include/c/joinstr.h +rm -f rust/joinstr/include/cpp/joinstr.h + +# C headers +cbindgen --lang c --crate joinstr -o rust/joinstr/include/c/joinstr.h + +# C++ headers +cbindgen --crate joinstr -o rust/joinstr/include/cpp/joinstr.h + +# Then generate dart bindings + +cd dart +dart pub get + +# Remove old bindings +rm -f lib/joinstr.dart + +# Automated bindings using ffigen +dart run ffigen --ignore-source-errors + +dart analyze + +# Generate rust librairies + +cd ../rust/joinstr + # Variables RUST_LIB_NAME="joinstr" -ANDROID_OUTPUT_DIR="./dart/android" -IOS_OUTPUT_DIR="./dart/ios/Frameworks" +ANDROID_OUTPUT_DIR="../../dart/android" +IOS_OUTPUT_DIR="../../dart/ios/Frameworks" # Set default ANDROID_NDK_HOME if not provided ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-/opt/android-ndk}" @@ -65,3 +94,4 @@ echo "Android .so files built and placed in $ANDROID_OUTPUT_DIR" # echo "iOS .a file built and placed in $IOS_OUTPUT_DIR" echo "Build complete!" + diff --git a/dart/.dart_tool/package_config.json b/dart/.dart_tool/package_config.json deleted file mode 100644 index f3d1f11..0000000 --- a/dart/.dart_tool/package_config.json +++ /dev/null @@ -1,311 +0,0 @@ -{ - "configVersion": 2, - "packages": [ - { - "name": "_fe_analyzer_shared", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-80.0.0", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "analyzer", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/analyzer-7.3.0", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "args", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/args-2.6.0", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "async", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/async-2.13.0", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "boolean_selector", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/boolean_selector-2.1.2", - "packageUri": "lib/", - "languageVersion": "3.1" - }, - { - "name": "collection", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/collection-1.19.1", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "convert", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/convert-3.1.2", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "coverage", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/coverage-1.11.1", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "crypto", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/crypto-3.0.6", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "ffi", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/ffi-2.1.4", - "packageUri": "lib/", - "languageVersion": "3.7" - }, - { - "name": "file", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/file-7.0.1", - "packageUri": "lib/", - "languageVersion": "3.0" - }, - { - "name": "frontend_server_client", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/frontend_server_client-4.0.0", - "packageUri": "lib/", - "languageVersion": "3.0" - }, - { - "name": "glob", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/glob-2.1.3", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "http_multi_server", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/http_multi_server-3.2.2", - "packageUri": "lib/", - "languageVersion": "3.2" - }, - { - "name": "http_parser", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/http_parser-4.1.2", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "io", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/io-1.0.5", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "js", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/js-0.7.2", - "packageUri": "lib/", - "languageVersion": "3.7" - }, - { - "name": "lints", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/lints-5.1.1", - "packageUri": "lib/", - "languageVersion": "3.6" - }, - { - "name": "logging", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/logging-1.3.0", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "matcher", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/matcher-0.12.17", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "meta", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/meta-1.16.0", - "packageUri": "lib/", - "languageVersion": "2.12" - }, - { - "name": "mime", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/mime-2.0.0", - "packageUri": "lib/", - "languageVersion": "3.2" - }, - { - "name": "node_preamble", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/node_preamble-2.0.2", - "packageUri": "lib/", - "languageVersion": "2.12" - }, - { - "name": "package_config", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/package_config-2.1.1", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "path", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/path-1.9.1", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "pool", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/pool-1.5.1", - "packageUri": "lib/", - "languageVersion": "2.12" - }, - { - "name": "pub_semver", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/pub_semver-2.1.5", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "shelf", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/shelf-1.4.2", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "shelf_packages_handler", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2", - "packageUri": "lib/", - "languageVersion": "2.17" - }, - { - "name": "shelf_static", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/shelf_static-1.1.3", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "shelf_web_socket", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0", - "packageUri": "lib/", - "languageVersion": "3.5" - }, - { - "name": "source_map_stack_trace", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.2", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "source_maps", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/source_maps-0.10.13", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "source_span", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/source_span-1.10.1", - "packageUri": "lib/", - "languageVersion": "3.1" - }, - { - "name": "stack_trace", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/stack_trace-1.12.1", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "stream_channel", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/stream_channel-2.1.4", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "string_scanner", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/string_scanner-1.4.1", - "packageUri": "lib/", - "languageVersion": "3.1" - }, - { - "name": "term_glyph", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/term_glyph-1.2.2", - "packageUri": "lib/", - "languageVersion": "3.1" - }, - { - "name": "test", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/test-1.25.15", - "packageUri": "lib/", - "languageVersion": "3.5" - }, - { - "name": "test_api", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/test_api-0.7.4", - "packageUri": "lib/", - "languageVersion": "3.5" - }, - { - "name": "test_core", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/test_core-0.6.8", - "packageUri": "lib/", - "languageVersion": "3.5" - }, - { - "name": "typed_data", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/typed_data-1.4.0", - "packageUri": "lib/", - "languageVersion": "3.5" - }, - { - "name": "vm_service", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/vm_service-15.0.0", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "watcher", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/watcher-1.1.1", - "packageUri": "lib/", - "languageVersion": "3.1" - }, - { - "name": "web", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/web-1.1.0", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "web_socket", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/web_socket-0.1.6", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "web_socket_channel", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/web_socket_channel-3.0.2", - "packageUri": "lib/", - "languageVersion": "3.3" - }, - { - "name": "webkit_inspection_protocol", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1", - "packageUri": "lib/", - "languageVersion": "3.0" - }, - { - "name": "yaml", - "rootUri": "file:///home/pyth/.pub-cache/hosted/pub.dev/yaml-3.1.3", - "packageUri": "lib/", - "languageVersion": "3.4" - }, - { - "name": "joinst", - "rootUri": "../", - "packageUri": "lib/", - "languageVersion": "3.7" - } - ], - "generated": "2025-02-22T17:50:58.215467Z", - "generator": "pub", - "generatorVersion": "3.7.0", - "flutterRoot": "file:///opt/flutter", - "flutterVersion": "3.29.0", - "pubCache": "file:///home/pyth/.pub-cache" -} diff --git a/dart/lib/joinstr.dart b/dart/lib/joinstr.dart deleted file mode 100644 index 876f75f..0000000 --- a/dart/lib/joinstr.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:ffi'; -import 'dart:io'; -import 'package:ffi/ffi.dart'; - -/// Enums mapping -class Network { - static const int Bitcoin = 0; - static const int Testnet = 1; - static const int Signet = 2; - static const int Regtest = 3; -} - -class JoinstrError { - static const int None = 0; - static const int Tokio = 1; - static const int CastString = 2; - static const int JsonError = 3; - static const int CString = 4; - static const int ListPools = 5; - static const int ListCoins = 6; - static const int InitiateConjoin = 7; - static const int SerdeJson = 8; - static const int PoolConfig = 9; - static const int PeerConfig = 10; -} - -/// Structs mapping -base class PoolConfig extends Struct { - @Double() - external double denomination; - - @Uint32() - external int fee; - - @Uint64() - external int maxDuration; - - @Uint8() - external int peers; - - @Int32() - external int network; -} - -base class PeerConfig extends Struct { - external Pointer electrumAddress; - @Uint16() - external int electrumPort; - external Pointer mnemonics; - external Pointer input; - external Pointer output; - external Pointer relay; -} - -base class Pools extends Struct { - external Pointer pools; - @Int32() - external int error; -} - -base class Coins extends Struct { - external Pointer coins; - @Int32() - external int error; -} - -base class Txid extends Struct { - external Pointer txid; - @Int32() - external int error; -} - -/// FFI Binding Loader -class JoinstrBindings { - static late final DynamicLibrary _lib; - - static void init() { - if (Platform.isIOS) { - // For static libraries (.a), use process() - _lib = DynamicLibrary.process(); - } else if (Platform.isAndroid) { - _lib = DynamicLibrary.open('libjoinstr.so'); - } else if (Platform.isLinux || Platform.isMacOS) { - _lib = DynamicLibrary.open('libjoinstr.dylib'); - } else { - throw UnsupportedError('Unsupported platform'); - } - } - - static late final Pools Function(int, int, Pointer) listPools = _lib - .lookupFunction< - Pools Function(Uint64, Uint64, Pointer), - Pools Function(int, int, Pointer) - >('list_pools'); - - static late final Coins Function( - Pointer, - Pointer, - int, - int, - int, - int, - ) - listCoins = _lib.lookupFunction< - Coins Function(Pointer, Pointer, Uint16, Int32, Uint32, Uint32), - Coins Function(Pointer, Pointer, int, int, int, int) - >('list_coins'); - - static late final Txid Function(PoolConfig, PeerConfig) initiateCoinjoin = - _lib.lookupFunction< - Txid Function(PoolConfig, PeerConfig), - Txid Function(PoolConfig, PeerConfig) - >('initiate_coinjoin'); - - static late final Txid Function(Pointer, PeerConfig) joinCoinjoin = _lib - .lookupFunction< - Txid Function(Pointer, PeerConfig), - Txid Function(Pointer, PeerConfig) - >('join_coinjoin'); -} diff --git a/dart/pubspec.lock b/dart/pubspec.lock index bc159c3..fe96ec1 100644 --- a/dart/pubspec.lock +++ b/dart/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" collection: dependency: transitive description: @@ -73,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + url: "https://pub.dev" + source: hosted + version: "3.0.1" ffi: dependency: "direct main" description: @@ -81,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + ffigen: + dependency: "direct dev" + description: + name: ffigen + sha256: "2648b974fb322b81f7450e9713f8651653e9b1353ac9bc04fab89530715e7b24" + url: "https://pub.dev" + source: hosted + version: "17.0.0" file: dependency: transitive description: @@ -217,6 +241,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.5" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" shelf: dependency: transitive description: @@ -393,5 +425,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5 + url: "https://pub.dev" + source: hosted + version: "2.2.2" sdks: dart: ">=3.7.0 <4.0.0" diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml index 27937b2..087ea37 100644 --- a/dart/pubspec.yaml +++ b/dart/pubspec.yaml @@ -1,13 +1,40 @@ -name: joinst -description: Voinjoin over nostr +name: joinstr +description: Coinjoin over nostr version: 1.0.0 environment: sdk: ^3.7.0 dependencies: - ffi: ^2.0.1 + ffi: ^2.1.4 dev_dependencies: lints: ^5.0.0 test: ^1.24.0 + ffigen: ^17.0.0 + +ffigen: + name: Joinstr + description: Coinjoin over nostr + output: lib/joinstr.dart + headers: + entry-points: + - '../rust/joinstr/include/c/joinstr.h' + exclude-all-by-default: true + functions: + include: + - 'list_pools' + - 'list_coins' + - 'initiate_coinjoin' + - 'join_coinjoin' + structs: + include: + - 'Network' + - 'PoolConfig' + - 'PeerConfig' + - 'Pools' + - 'Coins' + - 'Txid' + enums: + include: + - 'Error' diff --git a/justfile b/justfile new file mode 100644 index 0000000..bab4819 --- /dev/null +++ b/justfile @@ -0,0 +1,16 @@ +binding: + ./contrib/bindings.sh + +clean: + rm -fRd target + rm -fRd rust/joinstr/include/c + rm -fRd rust/joinstr/include/cpp + rm -fRd dart/lib/joinstr.dart + rm -fRd dart/android + rm -fRd dart/ios + rm -fRd dart/.dart_tool + + +test: + cargo clippy --all-features --all-targets -- -D warnings + cargo test -- --nocapture diff --git a/rust/joinstr/include/cpp/joinstr.cpp b/rust/joinstr/include/cpp/joinstr.cpp deleted file mode 100644 index 9591d70..0000000 --- a/rust/joinstr/include/cpp/joinstr.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "joinstr.h" - -auto errorToString(JoinstrError e) -> std::string { - switch (e) { - case JoinstrError::None: - return "None"; - case JoinstrError::Tokio: - return "Tokio"; - case JoinstrError::CastString: - return "CastString"; - case JoinstrError::JsonError: - return "Json"; - case JoinstrError::CString: - return "CString"; - case JoinstrError::ListPools: - return "ListPools"; - case ListCoins: - return "ListCoins"; - case InitiateConjoin: - return "InitiateCoinjoin"; - case SerdeJson: - return "SerdeJson"; - case PoolConfig: - return "PoolConfig"; - case PeerConfig: - return "PeerConfig"; - } - return "Unknown"; -} diff --git a/rust/joinstr/include/cpp/joinstr.h b/rust/joinstr/include/cpp/joinstr.h deleted file mode 100644 index e827216..0000000 --- a/rust/joinstr/include/cpp/joinstr.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include -#include -#include -using f64_t = double; - -extern "C" { - -enum Network { // NOLINT(performance-enum-size) - Bitcoin = 0, - Testnet, - Signet, - Regtest, -}; - -enum JoinstrError { // NOLINT(performance-enum-size) - None = 0, - Tokio, - CastString, - JsonError, - CString, - ListPools, - ListCoins, - InitiateConjoin, - SerdeJson, - PoolConfig, - PeerConfig, -}; - -struct PoolConfig { - f64_t denomination; - uint32_t fee; - uint64_t max_duration; - uint8_t peers; - Network network; -}; - -struct PeerConfig { - const char* electrum_address; - uint16_t electrum_port; - const char* mnemonics; - const char* input; - const char* output; - const char* relay; -}; - -struct Pools { - const char* pools; - JoinstrError error; -}; - -struct Coins { - const char* coins; - JoinstrError error; -}; - -struct Txid { - const char* txid; - JoinstrError error; -}; - -auto list_pools( // NOLINT(readability-identifier-naming) - uint64_t back, - uint64_t timeout, - const char* relay -) -> Pools; - -auto list_coins( // NOLINT(readability-identifier-naming) - const char* mnemonics, - const char* addr, - uint16_t port, - Network network, - uint32_t index_min, - uint32_t index_max -) -> Coins; - -auto initiate_coinjoin( // NOLINT(readability-identifier-naming) - struct PoolConfig config, - struct PeerConfig peer -) -> Txid; - -auto join_coinjoin( // NOLINT(readability-identifier-naming) - const char* pool, - struct PeerConfig peer -) -> Txid; - -} - -auto errorToString(JoinstrError e) -> std::string;