From 5c594299dafa35555b75ff632081f26b4a751bbb Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 19 Mar 2026 17:58:10 +0800 Subject: [PATCH 1/2] Make corepc-node optional in payjoin-test-utils Add a bitcoind feature flag to gate corepc-node dependency and move bitcoind helpers behind #[cfg(feature = "bitcoind")]. Downstream crates opt in via features = ["bitcoind"]. --- payjoin-cli/Cargo.toml | 2 +- payjoin-ffi/Cargo.toml | 2 +- payjoin-ffi/javascript/test-utils/Cargo.toml | 1 + payjoin-test-utils/Cargo.toml | 9 ++- payjoin-test-utils/src/bitcoind.rs | 72 ++++++++++++++++++ payjoin-test-utils/src/lib.rs | 77 ++------------------ payjoin/Cargo.toml | 2 +- 7 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 payjoin-test-utils/src/bitcoind.rs diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index 786b4a760..33ce480ee 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -55,5 +55,5 @@ url = { version = "2.5.4", features = ["serde"] } [dev-dependencies] nix = { version = "0.30.1", features = ["aio", "process", "signal"] } -payjoin-test-utils = { version = "0.0.1" } +payjoin-test-utils = { version = "0.0.1", features = ["bitcoind"] } tempfile = "3.20.0" diff --git a/payjoin-ffi/Cargo.toml b/payjoin-ffi/Cargo.toml index 942c45397..2028dc94f 100644 --- a/payjoin-ffi/Cargo.toml +++ b/payjoin-ffi/Cargo.toml @@ -9,7 +9,7 @@ exclude = ["tests"] default = [] csharp = ["dep:uniffi-bindgen-cs"] dart = ["dep:uniffi-dart"] -_test-utils = ["payjoin-test-utils", "tokio"] +_test-utils = ["payjoin-test-utils", "payjoin-test-utils/bitcoind", "tokio"] _manual-tls = ["payjoin/_manual-tls"] wasm_js = ["getrandom/js"] diff --git a/payjoin-ffi/javascript/test-utils/Cargo.toml b/payjoin-ffi/javascript/test-utils/Cargo.toml index 69cc0fe67..34eb9be37 100644 --- a/payjoin-ffi/javascript/test-utils/Cargo.toml +++ b/payjoin-ffi/javascript/test-utils/Cargo.toml @@ -13,6 +13,7 @@ serde_json = "1.0.142" tokio = { version = "1.47.1", features = ["full"] } [dependencies.payjoin-test-utils] +features = ["bitcoind"] path = "../../../payjoin-test-utils" [build-dependencies] diff --git a/payjoin-test-utils/Cargo.toml b/payjoin-test-utils/Cargo.toml index ee8a1b5fa..03976079d 100644 --- a/payjoin-test-utils/Cargo.toml +++ b/payjoin-test-utils/Cargo.toml @@ -8,10 +8,17 @@ repository = "https://github.com/payjoin/rust-payjoin" rust-version = "1.85" license = "MIT" +[features] +default = [] +bitcoind = ["corepc-node"] + [dependencies] axum-server = { version = "0.8", features = ["tls-rustls-no-provider"] } bitcoin = { version = "0.32.7", features = ["base64"] } -corepc-node = { version = "0.10.0", features = ["download", "29_0"] } +corepc-node = { version = "0.10.0", features = [ + "download", + "29_0", +], optional = true } http = "1.3.1" ohttp = { package = "bitcoin-ohttp", version = "0.6.0" } once_cell = "1.21.3" diff --git a/payjoin-test-utils/src/bitcoind.rs b/payjoin-test-utils/src/bitcoind.rs new file mode 100644 index 000000000..b4e233cb2 --- /dev/null +++ b/payjoin-test-utils/src/bitcoind.rs @@ -0,0 +1,72 @@ +use bitcoin::Amount; +use corepc_node::AddressType; +use tracing::Level; + +use crate::BoxError; + +pub fn init_bitcoind() -> Result { + let bitcoind_exe = corepc_node::exe_path()?; + let mut conf = corepc_node::Conf::default(); + conf.view_stdout = tracing::enabled!(target: "corepc", Level::TRACE); + // conf.args.push("-txindex"); + let bitcoind = corepc_node::Node::with_conf(bitcoind_exe, &conf)?; + Ok(bitcoind) +} + +pub fn init_bitcoind_sender_receiver( + sender_address_type: Option, + receiver_address_type: Option, +) -> Result<(corepc_node::Node, corepc_node::Client, corepc_node::Client), BoxError> { + let bitcoind = init_bitcoind()?; + let mut wallets = create_and_fund_wallets( + &bitcoind, + vec![("receiver", receiver_address_type), ("sender", sender_address_type)], + )?; + let receiver = wallets.pop().expect("receiver to exist"); + let sender = wallets.pop().expect("sender to exist"); + + Ok((bitcoind, receiver, sender)) +} + +fn create_and_fund_wallets>( + bitcoind: &corepc_node::Node, + wallets: Vec<(W, Option)>, +) -> Result, BoxError> { + let mut funded_wallets = vec![]; + let funding_wallet = bitcoind.create_wallet("funding_wallet")?; + let funding_address = funding_wallet.new_address()?; + // 100 blocks would work here, we add a extra block to cover fees between transfers + bitcoind.client.generate_to_address(101 + wallets.len(), &funding_address)?; + for (wallet_name, address_type) in wallets { + let wallet = bitcoind.create_wallet(wallet_name)?; + let address = wallet.get_new_address(None, address_type)?.into_model()?.0.assume_checked(); + funding_wallet.send_to_address(&address, Amount::from_btc(50.0)?)?; + funded_wallets.push(wallet); + } + // Mine the block which funds the different wallets + bitcoind.client.generate_to_address(1, &funding_address)?; + + for wallet in funded_wallets.iter() { + let balances = wallet.get_balances()?.into_model()?; + assert_eq!( + balances.mine.trusted, + Amount::from_btc(50.0)?, + "wallet doesn't have expected amount of bitcoin" + ); + } + + Ok(funded_wallets) +} + +pub fn init_bitcoind_multi_sender_single_reciever( + number_of_senders: usize, +) -> Result<(corepc_node::Node, Vec, corepc_node::Client), BoxError> { + let bitcoind = init_bitcoind()?; + let wallets_to_create = + (0..number_of_senders + 1).map(|i| (format!("sender_{i}"), None)).collect::>(); + let mut wallets = create_and_fund_wallets(&bitcoind, wallets_to_create)?; + let receiver = wallets.pop().expect("receiver to exist"); + let senders = wallets; + + Ok((bitcoind, senders, receiver)) +} diff --git a/payjoin-test-utils/src/lib.rs b/payjoin-test-utils/src/lib.rs index cb3103742..ca9977400 100644 --- a/payjoin-test-utils/src/lib.rs +++ b/payjoin-test-utils/src/lib.rs @@ -4,9 +4,7 @@ use std::sync::Arc; use std::time::Duration; use axum_server::tls_rustls::RustlsConfig; -use bitcoin::{Amount, Psbt}; -pub use corepc_node; // re-export for convenience -use corepc_node::AddressType; +use bitcoin::Psbt; use http::StatusCode; use ohttp::hpke::{Aead, Kdf, Kem}; use ohttp::{KeyId, SymmetricSuite}; @@ -19,9 +17,13 @@ use rustls::pki_types::CertificateDer; use rustls::RootCertStore; use tempfile::tempdir; use tokio::task::JoinHandle; -use tracing::Level; use tracing_subscriber::{EnvFilter, FmtSubscriber}; +#[cfg(feature = "bitcoind")] +mod bitcoind; +#[cfg(feature = "bitcoind")] +pub use {bitcoind::*, corepc_node}; + pub type BoxError = Box; pub type BoxSendSyncError = Box; @@ -173,77 +175,10 @@ pub fn local_cert_key() -> rcgen::CertifiedKey { .expect("Failed to generate cert") } -pub fn init_bitcoind() -> Result { - let bitcoind_exe = corepc_node::exe_path()?; - let mut conf = corepc_node::Conf::default(); - conf.view_stdout = tracing::enabled!(target: "corepc", Level::TRACE); - // conf.args.push("-txindex"); - let bitcoind = corepc_node::Node::with_conf(bitcoind_exe, &conf)?; - Ok(bitcoind) -} - -pub fn init_bitcoind_sender_receiver( - sender_address_type: Option, - receiver_address_type: Option, -) -> Result<(corepc_node::Node, corepc_node::Client, corepc_node::Client), BoxError> { - let bitcoind = init_bitcoind()?; - let mut wallets = create_and_fund_wallets( - &bitcoind, - vec![("receiver", receiver_address_type), ("sender", sender_address_type)], - )?; - let receiver = wallets.pop().expect("receiver to exist"); - let sender = wallets.pop().expect("sender to exist"); - - Ok((bitcoind, receiver, sender)) -} - -fn create_and_fund_wallets>( - bitcoind: &corepc_node::Node, - wallets: Vec<(W, Option)>, -) -> Result, BoxError> { - let mut funded_wallets = vec![]; - let funding_wallet = bitcoind.create_wallet("funding_wallet")?; - let funding_address = funding_wallet.new_address()?; - // 100 blocks would work here, we add a extra block to cover fees between transfers - bitcoind.client.generate_to_address(101 + wallets.len(), &funding_address)?; - for (wallet_name, address_type) in wallets { - let wallet = bitcoind.create_wallet(wallet_name)?; - let address = wallet.get_new_address(None, address_type)?.into_model()?.0.assume_checked(); - funding_wallet.send_to_address(&address, Amount::from_btc(50.0)?)?; - funded_wallets.push(wallet); - } - // Mine the block which funds the different wallets - bitcoind.client.generate_to_address(1, &funding_address)?; - - for wallet in funded_wallets.iter() { - let balances = wallet.get_balances()?.into_model()?; - assert_eq!( - balances.mine.trusted, - Amount::from_btc(50.0)?, - "wallet doesn't have expected amount of bitcoin" - ); - } - - Ok(funded_wallets) -} - pub fn http_agent(cert_der: Vec) -> Result { Ok(http_agent_builder(cert_der).build()?) } -pub fn init_bitcoind_multi_sender_single_reciever( - number_of_senders: usize, -) -> Result<(corepc_node::Node, Vec, corepc_node::Client), BoxError> { - let bitcoind = init_bitcoind()?; - let wallets_to_create = - (0..number_of_senders + 1).map(|i| (format!("sender_{i}"), None)).collect::>(); - let mut wallets = create_and_fund_wallets(&bitcoind, wallets_to_create)?; - let receiver = wallets.pop().expect("receiver to exist"); - let senders = wallets; - - Ok((bitcoind, senders, receiver)) -} - fn http_agent_builder(cert_der: Vec) -> ClientBuilder { ClientBuilder::new().http1_only().use_rustls_tls().add_root_certificate( reqwest::tls::Certificate::from_der(cert_der.as_slice()) diff --git a/payjoin/Cargo.toml b/payjoin/Cargo.toml index 662b619ec..6d97b9179 100644 --- a/payjoin/Cargo.toml +++ b/payjoin/Cargo.toml @@ -62,7 +62,7 @@ web-time = "1.1.0" [dev-dependencies] once_cell = "1.21.3" -payjoin-test-utils = { version = "0.0.1" } +payjoin-test-utils = { version = "0.0.1", features = ["bitcoind"] } tokio = { version = "1.47.1", features = ["full"] } tracing = "0.1.41" From cd56b573d5bdf9504fd3616420c54ca5a3d4e8af Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 23 Mar 2026 23:03:47 +0800 Subject: [PATCH 2/2] Gate payjoin-ffi bitcoind types behind feature Split _test-utils into _test-utils (constants + TestServices only) and _test-utils-bitcoind (adds BitcoindEnv, RpcClient, etc.). This lets FFI consumers like Dart unit tests import test constants without triggering the corepc-node/bitcoind download. Update all binding-generation and test scripts to use _test-utils-bitcoind where integration-test types are needed. --- payjoin-ffi/Cargo.toml | 3 +- payjoin-ffi/contrib/lint.sh | 2 +- payjoin-ffi/contrib/test.sh | 2 +- payjoin-ffi/csharp/README.md | 6 +- .../csharp/scripts/generate_bindings.ps1 | 4 +- .../csharp/scripts/generate_bindings.sh | 4 +- payjoin-ffi/dart/hook/build.dart | 2 +- payjoin-ffi/dart/native/Cargo.toml | 1 + payjoin-ffi/dart/scripts/generate_bindings.sh | 4 +- .../python/scripts/generate_bindings.sh | 10 +-- payjoin-ffi/src/test_utils/bitcoind.rs | 80 +++++++++++++++++ .../src/{test_utils.rs => test_utils/mod.rs} | 87 ++----------------- 12 files changed, 107 insertions(+), 98 deletions(-) create mode 100644 payjoin-ffi/src/test_utils/bitcoind.rs rename payjoin-ffi/src/{test_utils.rs => test_utils/mod.rs} (65%) diff --git a/payjoin-ffi/Cargo.toml b/payjoin-ffi/Cargo.toml index 2028dc94f..ce8cd3632 100644 --- a/payjoin-ffi/Cargo.toml +++ b/payjoin-ffi/Cargo.toml @@ -9,7 +9,8 @@ exclude = ["tests"] default = [] csharp = ["dep:uniffi-bindgen-cs"] dart = ["dep:uniffi-dart"] -_test-utils = ["payjoin-test-utils", "payjoin-test-utils/bitcoind", "tokio"] +_test-utils = ["payjoin-test-utils", "tokio"] +_test-utils-bitcoind = ["_test-utils", "payjoin-test-utils/bitcoind"] _manual-tls = ["payjoin/_manual-tls"] wasm_js = ["getrandom/js"] diff --git a/payjoin-ffi/contrib/lint.sh b/payjoin-ffi/contrib/lint.sh index 3370b2e25..93061b118 100755 --- a/payjoin-ffi/contrib/lint.sh +++ b/payjoin-ffi/contrib/lint.sh @@ -2,7 +2,7 @@ set -e # Individual features with no defaults. -features=("_manual-tls" "_test-utils") +features=("_manual-tls" "_test-utils" "_test-utils-bitcoind") for feature in "${features[@]}"; do # Don't duplicate --all-targets clippy. Clippy end-user code, not tests. diff --git a/payjoin-ffi/contrib/test.sh b/payjoin-ffi/contrib/test.sh index 3a8468532..22043992c 100755 --- a/payjoin-ffi/contrib/test.sh +++ b/payjoin-ffi/contrib/test.sh @@ -4,7 +4,7 @@ set -e RUST_VERSION=$(rustc --version | awk '{print $2}') if [[ ! $RUST_VERSION =~ ^1\.85\. ]]; then - cargo test --package payjoin-ffi --verbose --features=_manual-tls,_test-utils + cargo test --package payjoin-ffi --verbose --features=_manual-tls,_test-utils-bitcoind else echo "Skipping payjoin-ffi tests for Rust version $RUST_VERSION (MSRV)" fi diff --git a/payjoin-ffi/csharp/README.md b/payjoin-ffi/csharp/README.md index 9afccc5c3..737cc80de 100644 --- a/payjoin-ffi/csharp/README.md +++ b/payjoin-ffi/csharp/README.md @@ -56,12 +56,12 @@ dotnet test Generation uses the Cargo-managed C# generator from `payjoin-ffi/Cargo.toml`. -By default, generation builds `payjoin-ffi` with `_test-utils` enabled to keep parity with other language test scripts. Override via `PAYJOIN_FFI_FEATURES`. +By default, generation builds `payjoin-ffi` with `_test-utils-bitcoind` enabled to keep parity with other language test scripts. Override via `PAYJOIN_FFI_FEATURES`. ### Unix shells ```shell -export PAYJOIN_FFI_FEATURES=_test-utils # default behavior +export PAYJOIN_FFI_FEATURES=_test-utils-bitcoind # default behavior # export PAYJOIN_FFI_FEATURES="" # build without extra features bash ./scripts/generate_bindings.sh ``` @@ -69,7 +69,7 @@ bash ./scripts/generate_bindings.sh ### PowerShell ```powershell -$env:PAYJOIN_FFI_FEATURES = "_test-utils" # default behavior +$env:PAYJOIN_FFI_FEATURES = "_test-utils-bitcoind" # default behavior # $env:PAYJOIN_FFI_FEATURES = "" # build without extra features powershell -ExecutionPolicy Bypass -File .\scripts\generate_bindings.ps1 dotnet build diff --git a/payjoin-ffi/csharp/scripts/generate_bindings.ps1 b/payjoin-ffi/csharp/scripts/generate_bindings.ps1 index 52e782508..999f3c557 100644 --- a/payjoin-ffi/csharp/scripts/generate_bindings.ps1 +++ b/payjoin-ffi/csharp/scripts/generate_bindings.ps1 @@ -40,8 +40,8 @@ Write-Host "Generating payjoin C#..." if ($null -ne $env:PAYJOIN_FFI_FEATURES) { $payjoinFfiFeatures = $env:PAYJOIN_FFI_FEATURES } else { - # Keep parity with other language test scripts: include _test-utils by default. - $payjoinFfiFeatures = "_test-utils" + # Keep parity with other language test scripts: include _test-utils-bitcoind by default. + $payjoinFfiFeatures = "_test-utils-bitcoind" } if ($payjoinFfiFeatures) { diff --git a/payjoin-ffi/csharp/scripts/generate_bindings.sh b/payjoin-ffi/csharp/scripts/generate_bindings.sh index 630ab2146..b79b830a4 100755 --- a/payjoin-ffi/csharp/scripts/generate_bindings.sh +++ b/payjoin-ffi/csharp/scripts/generate_bindings.sh @@ -22,8 +22,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR/../.." echo "Generating payjoin C#..." -# Keep parity with other language test scripts: include _test-utils by default. -PAYJOIN_FFI_FEATURES=${PAYJOIN_FFI_FEATURES:-_test-utils} +# Keep parity with other language test scripts: include _test-utils-bitcoind by default. +PAYJOIN_FFI_FEATURES=${PAYJOIN_FFI_FEATURES:-_test-utils-bitcoind} GENERATOR_FEATURES="csharp" if [[ -n $PAYJOIN_FFI_FEATURES ]]; then GENERATOR_FEATURES="$GENERATOR_FEATURES,$PAYJOIN_FFI_FEATURES" diff --git a/payjoin-ffi/dart/hook/build.dart b/payjoin-ffi/dart/hook/build.dart index 6713619be..4fe2e1bd2 100644 --- a/payjoin-ffi/dart/hook/build.dart +++ b/payjoin-ffi/dart/hook/build.dart @@ -5,7 +5,7 @@ void main(List args) async { await build(args, (input, output) async { await RustBuilder( assetName: 'uniffi:payjoin_ffi', - features: ['_test-utils'], + features: ['_test-utils-bitcoind'], ).run(input: input, output: output); }); } diff --git a/payjoin-ffi/dart/native/Cargo.toml b/payjoin-ffi/dart/native/Cargo.toml index 78add00bc..a7dc1c888 100644 --- a/payjoin-ffi/dart/native/Cargo.toml +++ b/payjoin-ffi/dart/native/Cargo.toml @@ -10,6 +10,7 @@ publish = false [features] _test-utils = ["payjoin-ffi/_test-utils"] +_test-utils-bitcoind = ["payjoin-ffi/_test-utils-bitcoind"] [lib] name = "payjoin_ffi_wrapper" diff --git a/payjoin-ffi/dart/scripts/generate_bindings.sh b/payjoin-ffi/dart/scripts/generate_bindings.sh index 1eae49889..832bd1d61 100755 --- a/payjoin-ffi/dart/scripts/generate_bindings.sh +++ b/payjoin-ffi/dart/scripts/generate_bindings.sh @@ -19,7 +19,7 @@ fi cd ../ echo "Generating payjoin dart..." -cargo build --features dart,_test-utils --profile dev -cargo run --features dart,_test-utils --profile dev --bin uniffi-bindgen -- --library ../target/debug/$LIBNAME --language dart --out-dir dart/lib/ +cargo build --features dart,_test-utils-bitcoind --profile dev +cargo run --features dart,_test-utils-bitcoind --profile dev --bin uniffi-bindgen -- --library ../target/debug/$LIBNAME --language dart --out-dir dart/lib/ echo "All done!" diff --git a/payjoin-ffi/python/scripts/generate_bindings.sh b/payjoin-ffi/python/scripts/generate_bindings.sh index 69d560e34..deedd7cf8 100644 --- a/payjoin-ffi/python/scripts/generate_bindings.sh +++ b/payjoin-ffi/python/scripts/generate_bindings.sh @@ -19,15 +19,15 @@ uv run python --version cd ../ # This is a test script the actual release should not include the test utils feature -cargo build --features _test-utils --profile dev -cargo run --features _test-utils --profile dev --bin uniffi-bindgen generate --library ../target/debug/$LIBNAME --language python --out-dir python/src/payjoin/ +cargo build --features _test-utils-bitcoind --profile dev +cargo run --features _test-utils-bitcoind --profile dev --bin uniffi-bindgen generate --library ../target/debug/$LIBNAME --language python --out-dir python/src/payjoin/ if [[ $OS == "Darwin" ]]; then echo "Generating native binaries..." rustup target add aarch64-apple-darwin x86_64-apple-darwin # This is a test script the actual release should not include the test utils feature - cargo build --profile dev --target aarch64-apple-darwin --features _test-utils & - cargo build --profile dev --target x86_64-apple-darwin --features _test-utils & + cargo build --profile dev --target aarch64-apple-darwin --features _test-utils-bitcoind & + cargo build --profile dev --target x86_64-apple-darwin --features _test-utils-bitcoind & wait echo "Building macos fat library" @@ -39,7 +39,7 @@ else echo "Generating native binaries..." rustup target add x86_64-unknown-linux-gnu # This is a test script the actual release should not include the test utils feature - cargo build --profile dev --target x86_64-unknown-linux-gnu --features _test-utils + cargo build --profile dev --target x86_64-unknown-linux-gnu --features _test-utils-bitcoind echo "Copying payjoin_ffi binary" cp ../target/x86_64-unknown-linux-gnu/debug/$LIBNAME python/src/payjoin/$LIBNAME diff --git a/payjoin-ffi/src/test_utils/bitcoind.rs b/payjoin-ffi/src/test_utils/bitcoind.rs new file mode 100644 index 000000000..4e452e422 --- /dev/null +++ b/payjoin-ffi/src/test_utils/bitcoind.rs @@ -0,0 +1,80 @@ +use std::sync::Arc; + +use payjoin_test_utils::corepc_node::{self, AddressType}; +use serde_json::Value; + +#[derive(uniffi::Object)] +pub struct BitcoindEnv { + pub bitcoind: Arc, + pub receiver: Arc, + pub sender: Arc, +} + +#[uniffi::export] +impl BitcoindEnv { + pub fn get_receiver(&self) -> Arc { self.receiver.clone() } + + pub fn get_sender(&self) -> Arc { self.sender.clone() } + + pub fn get_bitcoind(&self) -> Arc { self.bitcoind.clone() } +} + +#[derive(uniffi::Object)] +pub struct BitcoindInstance { + _inner: corepc_node::Node, +} + +#[derive(uniffi::Object)] +pub struct RpcClient { + inner: corepc_node::Client, +} + +#[uniffi::export] +impl RpcClient { + pub fn call(&self, method: String, params: Vec>) -> Result { + let parsed_params: Vec = params + .into_iter() + .map(|param| match param { + Some(p) => serde_json::from_str(&p).unwrap_or(Value::String(p)), + None => Value::Null, + }) + .collect(); + + let result = self + .inner + .call::(&method, &parsed_params) + .map_err(|e| FfiError::new(format!("RPC call failed: {e}")))?; + + serde_json::to_string(&result) + .map_err(|e| FfiError::new(format!("Serialization error: {e}"))) + } +} + +#[derive(Debug, thiserror::Error, PartialEq, Eq, uniffi::Error)] +pub enum FfiError { + #[error("Init error: {0}")] + InitError(String), + #[error("Rpc error: {0}")] + RpcError(String), + #[error("{0}")] + Message(String), +} + +impl FfiError { + pub fn new(msg: impl Into) -> Self { FfiError::Message(msg.into()) } +} + +#[uniffi::export] +pub fn init_bitcoind_sender_receiver() -> Result, FfiError> { + let (bitcoind, receiver, sender) = payjoin_test_utils::init_bitcoind_sender_receiver( + Some(AddressType::Bech32), + Some(AddressType::Bech32), + ) + .map_err(|e| FfiError::InitError(e.to_string()))?; + + Ok(Arc::new(BitcoindEnv { + bitcoind: Arc::new(BitcoindInstance { _inner: bitcoind }), + receiver: Arc::new(RpcClient { inner: receiver }), + sender: Arc::new(RpcClient { inner: sender }), + })) +} diff --git a/payjoin-ffi/src/test_utils.rs b/payjoin-ffi/src/test_utils/mod.rs similarity index 65% rename from payjoin-ffi/src/test_utils.rs rename to payjoin-ffi/src/test_utils/mod.rs index 962d91b1d..8c73cf470 100644 --- a/payjoin-ffi/src/test_utils.rs +++ b/payjoin-ffi/src/test_utils/mod.rs @@ -2,81 +2,23 @@ use std::io; use std::sync::Arc; use lazy_static::lazy_static; -use payjoin_test_utils::corepc_node::AddressType; use payjoin_test_utils::{ - corepc_node, EXAMPLE_URL, INVALID_PSBT, ORIGINAL_PSBT, PAYJOIN_PROPOSAL, - PAYJOIN_PROPOSAL_WITH_SENDER_INFO, QUERY_PARAMS, RECEIVER_INPUT_CONTRIBUTION, + EXAMPLE_URL, INVALID_PSBT, ORIGINAL_PSBT, PAYJOIN_PROPOSAL, PAYJOIN_PROPOSAL_WITH_SENDER_INFO, + QUERY_PARAMS, RECEIVER_INPUT_CONTRIBUTION, }; -use serde_json::Value; use tokio::runtime::Runtime; use tokio::sync::Mutex; +#[cfg(feature = "_test-utils-bitcoind")] +mod bitcoind; +#[cfg(feature = "_test-utils-bitcoind")] +pub use bitcoind::*; + lazy_static! { static ref RUNTIME: Arc> = Arc::new(std::sync::Mutex::new(Runtime::new().expect("Failed to create Tokio runtime"))); } -#[derive(uniffi::Object)] -pub struct BitcoindEnv { - pub bitcoind: Arc, - pub receiver: Arc, - pub sender: Arc, -} - -#[uniffi::export] -impl BitcoindEnv { - pub fn get_receiver(&self) -> Arc { self.receiver.clone() } - - pub fn get_sender(&self) -> Arc { self.sender.clone() } - - pub fn get_bitcoind(&self) -> Arc { self.bitcoind.clone() } -} - -#[derive(uniffi::Object)] -pub struct BitcoindInstance { - _inner: corepc_node::Node, -} - -#[derive(uniffi::Object)] -pub struct RpcClient { - inner: corepc_node::Client, -} - -#[uniffi::export] -impl RpcClient { - pub fn call(&self, method: String, params: Vec>) -> Result { - let parsed_params: Vec = params - .into_iter() - .map(|param| match param { - Some(p) => serde_json::from_str(&p).unwrap_or(Value::String(p)), - None => Value::Null, - }) - .collect(); - - let result = self - .inner - .call::(&method, &parsed_params) - .map_err(|e| FfiError::new(format!("RPC call failed: {e}")))?; - - serde_json::to_string(&result) - .map_err(|e| FfiError::new(format!("Serialization error: {e}"))) - } -} - -#[derive(Debug, thiserror::Error, PartialEq, Eq, uniffi::Error)] -pub enum FfiError { - #[error("Init error: {0}")] - InitError(String), - #[error("Rpc error: {0}")] - RpcError(String), - #[error("{0}")] - Message(String), -} - -impl FfiError { - pub fn new(msg: impl Into) -> Self { FfiError::Message(msg.into()) } -} - #[derive(Debug, thiserror::Error, uniffi::Object)] #[error(transparent)] pub struct BoxSendSyncError(#[from] payjoin_test_utils::BoxSendSyncError); @@ -177,21 +119,6 @@ impl TestServices { } } -#[uniffi::export] -pub fn init_bitcoind_sender_receiver() -> Result, FfiError> { - let (bitcoind, receiver, sender) = payjoin_test_utils::init_bitcoind_sender_receiver( - Some(AddressType::Bech32), - Some(AddressType::Bech32), - ) - .map_err(|e| FfiError::InitError(e.to_string()))?; - - Ok(Arc::new(BitcoindEnv { - bitcoind: Arc::new(BitcoindInstance { _inner: bitcoind }), - receiver: Arc::new(RpcClient { inner: receiver }), - sender: Arc::new(RpcClient { inner: sender }), - })) -} - #[uniffi::export] pub fn example_url() -> String { EXAMPLE_URL.to_string() }