Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 2 additions & 3 deletions payjoin-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ license = "MIT OR Apache-2.0"
exclude = ["tests"]

[features]
default = ["io"]
default = []
csharp = ["dep:uniffi-bindgen-cs"]
dart = ["dep:uniffi-dart"]
io = ["payjoin/io"]
_test-utils = ["payjoin-test-utils", "tokio", "io"]
_test-utils = ["payjoin-test-utils", "tokio"]
_manual-tls = ["payjoin/_manual-tls"]
wasm_js = ["getrandom/js"]

Expand Down
2 changes: 1 addition & 1 deletion payjoin-ffi/csharp/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Generated bindings
src/*.cs
src/payjoin.cs

# Build outputs
bin/
Expand Down
24 changes: 20 additions & 4 deletions payjoin-ffi/csharp/IntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Payjoin.Http;
using System.Text.Json;
using uniffi.payjoin;
Comment thread
spacebear21 marked this conversation as resolved.
Outdated
using Xunit;
Expand Down Expand Up @@ -323,6 +324,21 @@ public ValueTask DisposeAsync()
return ValueTask.CompletedTask;
}

[Fact]
public async Task FetchAndDecodeOhttpKeysViaRelayProxy()
{
var cancellationToken = TestContext.Current.CancellationToken;

_services!.WaitForServicesReady();
var ohttpRelay = _services.OhttpRelayUrl();
var directory = _services.DirectoryUrl();
var cert = _services.Cert();

using var keys = await OhttpKeysClient.GetOhttpKeysAsync(new System.Uri(ohttpRelay), new System.Uri(directory), cert, cancellationToken);

Assert.NotNull(keys);
}

[Fact]
public void TestFfiValidation()
{
Expand Down Expand Up @@ -395,7 +411,7 @@ public void TestFfiValidation()
);
new InputPair(txin, psbtIn, new PlainWeight(0));
});

var directory = _services!.DirectoryUrl();
_services.WaitForServicesReady();
var ohttpKeys = _services.FetchOhttpKeys();
Expand Down Expand Up @@ -432,9 +448,9 @@ public async Task TestIntegrationV2ToV2()
Assert.Skip($"test-utils are not available: {ex.GetType().Name}: {ex.Message}");
}

_ = _env.GetBitcoind();
var receiver = _env.GetReceiver();
var sender = _env.GetSender();
using var bitcoind = _env.GetBitcoind();
using var receiver = _env.GetReceiver();
using var sender = _env.GetSender();

var receiverAddressJson = RpcCall(receiver, "getnewaddress");
var receiverAddress = JsonSerializer.Deserialize<string>(receiverAddressJson)!;
Expand Down
44 changes: 44 additions & 0 deletions payjoin-ffi/csharp/Payjoin.Http.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using uniffi.payjoin;

namespace Payjoin.Http
{
internal static class OhttpKeysClient
{
/// <summary>
/// Fetches the OHTTP keys from the specified payjoin directory via proxy.
/// </summary>
/// <param name="ohttpRelayUrl">
/// The HTTP CONNECT method proxy to request the OHTTP keys from a payjoin directory.
/// Proxying requests for OHTTP keys ensures a client IP address is never revealed to
/// the payjoin directory.
/// </param>
/// <param name="directoryUrl">
/// The payjoin directory from which to fetch the OHTTP keys. This directory stores
/// and forwards payjoin client payloads.
/// </param>
/// <param name="certificate">The DER-encoded certificate to use for local HTTPS connections.</param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
/// <returns>The decoded <see cref="OhttpKeys"/> from the payjoin directory.</returns>
internal static async Task<OhttpKeys> GetOhttpKeysAsync(System.Uri ohttpRelayUrl, System.Uri directoryUrl, byte[] certificate, CancellationToken cancellationToken = default)
{
var keysUrl = new System.Uri(directoryUrl, "/.well-known/ohttp-gateway");

using var handler = new HttpClientHandler
{
Proxy = new System.Net.WebProxy(ohttpRelayUrl),
UseProxy = true,
ServerCertificateCustomValidationCallback = (_, serverCert, _, _) => serverCert != null && serverCert.GetRawCertData().SequenceEqual(certificate)
};

using var client = new HttpClient(handler);
using var request = new HttpRequestMessage(HttpMethod.Get, keysUrl);
request.Headers.Accept.ParseAdd("application/ohttp-keys");

using var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();

var ohttpKeysBytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
return OhttpKeys.Decode(ohttpKeysBytes);
}
}
}
2 changes: 2 additions & 0 deletions payjoin-ffi/csharp/Payjoin.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
<tags>bitcoin payjoin bip78 ffi csharp dotnet</tags>
<contentFiles>
<files include="cs/any/payjoin.cs" buildAction="Compile" copyToOutput="false" flatten="true" />
<files include="cs/any/Payjoin.Http.cs" buildAction="Compile" copyToOutput="false" flatten="true" />
</contentFiles>
</metadata>

<files>
<file src="README.md" target="README.md" />
<file src="src/payjoin.cs" target="contentFiles/cs/any/payjoin.cs" />
<file src="Payjoin.Http.cs" target="contentFiles/cs/any/Payjoin.Http.cs" />
<file src="lib/*" target="runtimes/any/native" />
</files>
</package>
55 changes: 0 additions & 55 deletions payjoin-ffi/src/io.rs

This file was deleted.

2 changes: 0 additions & 2 deletions payjoin-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#![crate_name = "payjoin_ffi"]

pub mod error;
#[cfg(feature = "io")]
pub mod io;
pub mod ohttp;
pub mod output_substitution;
pub mod receive;
Expand Down
10 changes: 8 additions & 2 deletions payjoin-ffi/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,16 @@ impl TestServices {
})
}

pub fn fetch_ohttp_keys(&self) -> Result<crate::OhttpKeys, crate::io::IoError> {
pub fn fetch_ohttp_keys(&self) -> Result<crate::OhttpKeys, BoxSendSyncError> {
let runtime = RUNTIME.lock().expect("Lock should not be poisoned");
runtime.block_on(async {
self.0.lock().await.fetch_ohttp_keys().await.map_err(Into::into).map(Into::into)
self.0
.lock()
.await
.fetch_ohttp_keys()
.await
.map(Into::into)
.map_err(|e| payjoin_test_utils::BoxSendSyncError::from(e).into())
})
}
}
Expand Down
Loading