Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion payjoin-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.142"
thiserror = "2.0.14"
tokio = { version = "1.47.1", features = ["full"], optional = true }
uniffi = { version = "0.30.0", features = ["cli"] }
uniffi = { version = "0.30.0", features = ["cli", "tokio"] }
uniffi-bindgen-cs = { git = "https://github.com/chavic/uniffi-bindgen-cs.git", rev = "878a3d269eacce64beadcd336ade0b7c8da09824", optional = true }
uniffi-dart = { git = "https://github.com/Uniffi-Dart/uniffi-dart.git", rev = "f830323", optional = true }
url = "2.5.4"
Expand Down
26 changes: 26 additions & 0 deletions payjoin-ffi/csharp/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,32 @@ public ValueTask DisposeAsync()
return ValueTask.CompletedTask;
}

/// <summary>
/// Regression test: <see cref="PayjoinMethods.FetchOhttpKeysWithCert"/> called from a plain .NET async
/// context should successfully return OHTTP keys.
///
/// Without the fix this test fails with:
/// PanicException: "there is no reactor running, must be called from the context of a Tokio 1.x runtime"
/// </summary>
[Fact]
public async Task FetchOhttpKeys_ShouldWorkFromNonTokioContext()
{
// Arrange: use TestServices' URLs and certificate so connectivity is guaranteed and
// the failure (if any) is purely about missing Tokio runtime, not TLS trust setup.
_services!.WaitForServicesReady();
var ohttpRelay = _services.OhttpRelayUrl();
var directory = _services.DirectoryUrl();
var cert = _services.Cert();

// Act: call the raw UniFFI async binding directly — NOT TestServices.FetchOhttpKeys(),
// which uses an internal block_on(RUNTIME) and therefore always has a Tokio context.
// PayjoinMethods.FetchOhttpKeysWithCert() has no such safety net.
var keys = await PayjoinMethods.FetchOhttpKeysWithCert(ohttpRelay, directory, cert);

// Assert
Assert.NotNull(keys);
}

[Fact]
public void TestFfiValidation()
{
Expand Down
6 changes: 3 additions & 3 deletions payjoin-ffi/csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@ 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,_manual-tls` so C# integration tests can use local HTTPS services with generated self-signed certificates. Override via `PAYJOIN_FFI_FEATURES`.

### Unix shells

```shell
export PAYJOIN_FFI_FEATURES=_test-utils # default behavior
export PAYJOIN_FFI_FEATURES=_test-utils,_manual-tls # default behavior
# export PAYJOIN_FFI_FEATURES="" # build without extra features
bash ./scripts/generate_bindings.sh
```

### PowerShell

```powershell
$env:PAYJOIN_FFI_FEATURES = "_test-utils" # default behavior
$env:PAYJOIN_FFI_FEATURES = "_test-utils,_manual-tls" # default behavior
# $env:PAYJOIN_FFI_FEATURES = "" # build without extra features
powershell -ExecutionPolicy Bypass -File .\scripts\generate_bindings.ps1
dotnet build
Expand Down
5 changes: 3 additions & 2 deletions payjoin-ffi/csharp/scripts/generate_bindings.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ 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"
# Include test utilities and manual TLS by default so local test services
# can fetch OHTTP keys over HTTPS with their generated self-signed cert.
$payjoinFfiFeatures = "_test-utils,_manual-tls"
}

if ($payjoinFfiFeatures) {
Expand Down
5 changes: 3 additions & 2 deletions payjoin-ffi/csharp/scripts/generate_bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ 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}
# Include test utilities and manual TLS by default so local test services
# can fetch OHTTP keys over HTTPS with their generated self-signed cert.
PAYJOIN_FFI_FEATURES=${PAYJOIN_FFI_FEATURES:-_test-utils,_manual-tls}
GENERATOR_FEATURES="csharp"
if [[ -n $PAYJOIN_FFI_FEATURES ]]; then
GENERATOR_FEATURES="$GENERATOR_FEATURES,$PAYJOIN_FFI_FEATURES"
Expand Down
3 changes: 2 additions & 1 deletion payjoin-ffi/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub mod error {
///
/// * `payjoin_directory`: The payjoin directory from which to fetch the ohttp keys. This
/// directory stores and forwards payjoin client payloads.
#[uniffi::export]
#[uniffi::export(async_runtime = "tokio")]
pub async fn fetch_ohttp_keys(
ohttp_relay: &str,
payjoin_directory: &str,
Expand All @@ -43,6 +43,7 @@ pub async fn fetch_ohttp_keys(
///
/// * `cert_der`: The DER-encoded certificate to use for local HTTPS connections.
#[cfg(feature = "_manual-tls")]
#[uniffi::export(async_runtime = "tokio")]
pub async fn fetch_ohttp_keys_with_cert(
ohttp_relay: &str,
payjoin_directory: &str,
Expand Down
Loading