Skip to content

Commit 0c4d23b

Browse files
test: fetch ohttp keys (#1354)
Remove io module from payjoin-ffi and implement fetch_ohttp_keys natively in C#
1 parent 1b1c687 commit 0c4d23b

9 files changed

Lines changed: 82 additions & 68 deletions

File tree

payjoin-ffi/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ license = "MIT OR Apache-2.0"
66
exclude = ["tests"]
77

88
[features]
9-
default = ["io"]
9+
default = []
1010
csharp = ["dep:uniffi-bindgen-cs"]
1111
dart = ["dep:uniffi-dart"]
12-
io = ["payjoin/io"]
13-
_test-utils = ["payjoin-test-utils", "tokio", "io"]
12+
_test-utils = ["payjoin-test-utils", "tokio"]
1413
_manual-tls = ["payjoin/_manual-tls"]
1514
wasm_js = ["getrandom/js"]
1615

payjoin-ffi/csharp/IntegrationTests.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
using Payjoin.Http;
12
using System.Text.Json;
2-
using uniffi.payjoin;
3+
using Payjoin;
34
using Xunit;
45

56
namespace Payjoin.Tests
@@ -323,6 +324,21 @@ public ValueTask DisposeAsync()
323324
return ValueTask.CompletedTask;
324325
}
325326

327+
[Fact]
328+
public async Task FetchAndDecodeOhttpKeysViaRelayProxy()
329+
{
330+
var cancellationToken = TestContext.Current.CancellationToken;
331+
332+
_services!.WaitForServicesReady();
333+
var ohttpRelay = _services.OhttpRelayUrl();
334+
var directory = _services.DirectoryUrl();
335+
var cert = _services.Cert();
336+
337+
using var keys = await OhttpKeysClient.GetOhttpKeysAsync(new System.Uri(ohttpRelay), new System.Uri(directory), cert, cancellationToken);
338+
339+
Assert.NotNull(keys);
340+
}
341+
326342
[Fact]
327343
public void TestFfiValidation()
328344
{
@@ -395,7 +411,7 @@ public void TestFfiValidation()
395411
);
396412
new InputPair(txin, psbtIn, new PlainWeight(0));
397413
});
398-
414+
399415
var directory = _services!.DirectoryUrl();
400416
_services.WaitForServicesReady();
401417
var ohttpKeys = _services.FetchOhttpKeys();
@@ -432,9 +448,9 @@ public async Task TestIntegrationV2ToV2()
432448
Assert.Skip($"test-utils are not available: {ex.GetType().Name}: {ex.Message}");
433449
}
434450

435-
_ = _env.GetBitcoind();
436-
var receiver = _env.GetReceiver();
437-
var sender = _env.GetSender();
451+
using var bitcoind = _env.GetBitcoind();
452+
using var receiver = _env.GetReceiver();
453+
using var sender = _env.GetSender();
438454

439455
var receiverAddressJson = RpcCall(receiver, "getnewaddress");
440456
var receiverAddress = JsonSerializer.Deserialize<string>(receiverAddressJson)!;

payjoin-ffi/csharp/Payjoin.Http.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Payjoin;
2+
3+
namespace Payjoin.Http
4+
{
5+
internal static class OhttpKeysClient
6+
{
7+
/// <summary>
8+
/// Fetches the OHTTP keys from the specified payjoin directory via proxy.
9+
/// </summary>
10+
/// <param name="ohttpRelayUrl">
11+
/// The HTTP CONNECT method proxy to request the OHTTP keys from a payjoin directory.
12+
/// Proxying requests for OHTTP keys ensures a client IP address is never revealed to
13+
/// the payjoin directory.
14+
/// </param>
15+
/// <param name="directoryUrl">
16+
/// The payjoin directory from which to fetch the OHTTP keys. This directory stores
17+
/// and forwards payjoin client payloads.
18+
/// </param>
19+
/// <param name="certificate">The DER-encoded certificate to use for local HTTPS connections.</param>
20+
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
21+
/// <returns>The decoded <see cref="OhttpKeys"/> from the payjoin directory.</returns>
22+
internal static async Task<OhttpKeys> GetOhttpKeysAsync(System.Uri ohttpRelayUrl, System.Uri directoryUrl, byte[] certificate, CancellationToken cancellationToken = default)
23+
{
24+
var keysUrl = new System.Uri(directoryUrl, "/.well-known/ohttp-gateway");
25+
26+
using var handler = new HttpClientHandler
27+
{
28+
Proxy = new System.Net.WebProxy(ohttpRelayUrl),
29+
UseProxy = true,
30+
ServerCertificateCustomValidationCallback = (_, serverCert, _, _) => serverCert != null && serverCert.GetRawCertData().SequenceEqual(certificate)
31+
};
32+
33+
using var client = new HttpClient(handler);
34+
using var request = new HttpRequestMessage(HttpMethod.Get, keysUrl);
35+
request.Headers.Accept.ParseAdd("application/ohttp-keys");
36+
37+
using var response = await client.SendAsync(request, cancellationToken);
38+
response.EnsureSuccessStatusCode();
39+
40+
var ohttpKeysBytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
41+
return OhttpKeys.Decode(ohttpKeysBytes);
42+
}
43+
}
44+
}

payjoin-ffi/csharp/Payjoin.nuspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
<tags>bitcoin payjoin bip78 ffi csharp dotnet</tags>
1616
<contentFiles>
1717
<files include="cs/any/payjoin.cs" buildAction="Compile" copyToOutput="false" flatten="true" />
18+
<files include="cs/any/Payjoin.Http.cs" buildAction="Compile" copyToOutput="false" flatten="true" />
1819
</contentFiles>
1920
</metadata>
2021

2122
<files>
2223
<file src="README.md" target="README.md" />
2324
<file src="src/payjoin.cs" target="contentFiles/cs/any/payjoin.cs" />
25+
<file src="Payjoin.Http.cs" target="contentFiles/cs/any/Payjoin.Http.cs" />
2426
<file src="lib/*" target="runtimes/any/native" />
2527
</files>
2628
</package>

payjoin-ffi/csharp/UnitTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Xunit;
2-
using uniffi.payjoin;
2+
using Payjoin;
33

44
namespace Payjoin.Tests;
55

payjoin-ffi/src/io.rs

Lines changed: 0 additions & 55 deletions
This file was deleted.

payjoin-ffi/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#![crate_name = "payjoin_ffi"]
22

33
pub mod error;
4-
#[cfg(feature = "io")]
5-
pub mod io;
64
pub mod ohttp;
75
pub mod output_substitution;
86
pub mod receive;

payjoin-ffi/src/test_utils.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,16 @@ impl TestServices {
163163
})
164164
}
165165

166-
pub fn fetch_ohttp_keys(&self) -> Result<crate::OhttpKeys, crate::io::IoError> {
166+
pub fn fetch_ohttp_keys(&self) -> Result<crate::OhttpKeys, BoxSendSyncError> {
167167
let runtime = RUNTIME.lock().expect("Lock should not be poisoned");
168168
runtime.block_on(async {
169-
self.0.lock().await.fetch_ohttp_keys().await.map_err(Into::into).map(Into::into)
169+
self.0
170+
.lock()
171+
.await
172+
.fetch_ohttp_keys()
173+
.await
174+
.map(Into::into)
175+
.map_err(|e| payjoin_test_utils::BoxSendSyncError::from(e).into())
170176
})
171177
}
172178
}

payjoin-ffi/uniffi.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ cdylib_name = "payjoin_ffi"
1111
[bindings.dart]
1212
cdylib_name = "payjoin_ffi"
1313
package_name = "payjoin"
14+
15+
[bindings.csharp]
16+
namespace = "Payjoin"
17+
cdylib_name = "payjoin_ffi"

0 commit comments

Comments
 (0)