From 7e8fbbd48891a391c8ca938b45c5693e142c884c Mon Sep 17 00:00:00 2001 From: chavic Date: Tue, 8 Sep 2026 12:46:56 +0200 Subject: [PATCH] Share fixtures across language binding tests Keep common PSBT and OHTTP vectors in one source so Rust and the Python, Dart, JavaScript, and C# tests cannot drift independently. Preserve the existing PSBT constants and document the fixed OHTTP vector separately from keys used for request decryption. Seed Dart's native wrapper from the maintained lockfile so test builds retain dependency versions compatible with the MSRV. --- flake.nix | 1 + payjoin-ffi/csharp/IntegrationTests.cs | 2 +- payjoin-ffi/csharp/Payjoin.Tests.csproj | 6 ++ payjoin-ffi/csharp/TestVectors.cs | 12 +++ payjoin-ffi/csharp/UnitTests.cs | 74 +++++------------- payjoin-ffi/dart/contrib/test.sh | 6 +- .../dart/test/fetch_ohttp_keys_http_test.dart | 18 +---- payjoin-ffi/dart/test/fixtures.dart | 19 +++++ .../test/test_payjoin_integration_test.dart | 3 +- .../dart/test/test_payjoin_unit_test.dart | 76 ++++--------------- payjoin-ffi/javascript/test/fixtures.ts | 19 +++++ .../javascript/test/integration.test.ts | 3 +- payjoin-ffi/javascript/test/unit.test.ts | 14 +--- payjoin-ffi/python/test/fixtures.py | 6 ++ .../test/test_payjoin_integration_test.py | 3 +- .../python/test/test_payjoin_unit_test.py | 63 ++++----------- payjoin-test-utils/fixtures/README.md | 32 ++++++++ .../fixtures/invalid-psbt.base64 | 1 + payjoin-test-utils/fixtures/ohttp-keys.hex | 1 + .../fixtures/original-psbt.base64 | 1 + .../payjoin-proposal-with-sender-info.base64 | 1 + .../fixtures/payjoin-proposal.base64 | 1 + .../receiver-input-contribution.base64 | 1 + payjoin-test-utils/src/lib.rs | 12 +-- payjoin-test-utils/src/v2.rs | 15 ++++ 25 files changed, 184 insertions(+), 206 deletions(-) create mode 100644 payjoin-ffi/csharp/TestVectors.cs create mode 100644 payjoin-ffi/dart/test/fixtures.dart create mode 100644 payjoin-ffi/javascript/test/fixtures.ts create mode 100644 payjoin-ffi/python/test/fixtures.py create mode 100644 payjoin-test-utils/fixtures/README.md create mode 100644 payjoin-test-utils/fixtures/invalid-psbt.base64 create mode 100644 payjoin-test-utils/fixtures/ohttp-keys.hex create mode 100644 payjoin-test-utils/fixtures/original-psbt.base64 create mode 100644 payjoin-test-utils/fixtures/payjoin-proposal-with-sender-info.base64 create mode 100644 payjoin-test-utils/fixtures/payjoin-proposal.base64 create mode 100644 payjoin-test-utils/fixtures/receiver-input-contribution.base64 diff --git a/flake.nix b/flake.nix index 981a9f0cd..7d2fb5f08 100644 --- a/flake.nix +++ b/flake.nix @@ -115,6 +115,7 @@ (builtins.match ".*nginx.conf.template$" path != null) || (builtins.match ".*\\.mmdb$" path != null) || (builtins.match ".*\\.html$" path != null) + || (builtins.match ".*/payjoin-test-utils/fixtures/.*" path != null) || (craneLibVersions.msrv.filterCargoSources path type); name = "source"; }; diff --git a/payjoin-ffi/csharp/IntegrationTests.cs b/payjoin-ffi/csharp/IntegrationTests.cs index 545a876ad..eb94f6dc9 100644 --- a/payjoin-ffi/csharp/IntegrationTests.cs +++ b/payjoin-ffi/csharp/IntegrationTests.cs @@ -590,7 +590,7 @@ public void TestFfiValidation() using var receiver = receiveTransition.Save(recvPersister); using var pjUri = receiver.PjUri(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; var feeRateOutOfRange = Assert.Throws(() => { diff --git a/payjoin-ffi/csharp/Payjoin.Tests.csproj b/payjoin-ffi/csharp/Payjoin.Tests.csproj index 93025616d..fa7bf0797 100644 --- a/payjoin-ffi/csharp/Payjoin.Tests.csproj +++ b/payjoin-ffi/csharp/Payjoin.Tests.csproj @@ -24,6 +24,12 @@ + + + + diff --git a/payjoin-ffi/csharp/TestVectors.cs b/payjoin-ffi/csharp/TestVectors.cs new file mode 100644 index 000000000..d309eb499 --- /dev/null +++ b/payjoin-ffi/csharp/TestVectors.cs @@ -0,0 +1,12 @@ +namespace Payjoin.Tests; + +internal static class TestVectors +{ + private static string Read(string name) => + File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "fixtures", name)); + + internal static readonly string OriginalPsbt = Read("original-psbt.base64"); + private static readonly string OhttpKeysHex = Read("ohttp-keys.hex").Trim(); + + internal static byte[] OhttpKeys => Convert.FromHexString(OhttpKeysHex); +} diff --git a/payjoin-ffi/csharp/UnitTests.cs b/payjoin-ffi/csharp/UnitTests.cs index 655286fa5..0d3766bb7 100644 --- a/payjoin-ffi/csharp/UnitTests.cs +++ b/payjoin-ffi/csharp/UnitTests.cs @@ -44,24 +44,12 @@ public void ValidUrisWithDifferentAddressesAndEndpoints(string address, string p public class PersistenceTests { - private static readonly byte[] OhttpKeysData = new byte[] - { - 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, - 0x92, 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, - 0x0c, 0xed, 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, - 0xad, 0x2c, 0xc9, 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, - 0xe8, 0xd2, 0x8b, 0xfe, 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, - 0x4d, 0x31, 0xcd, 0x85, 0x1e, 0x8a, 0x65, 0x40, 0xe8, 0x6c, - 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, 0x09, 0x57, 0x00, 0x04, - 0x00, 0x01, 0x00, 0x03, - }; - [Fact] public void ReceiverPersistence() { var persister = new InMemoryReceiverPersister(); var address = "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var builder = new ReceiverBuilder(address, "https://example.com", ohttpKeys); var transition = builder.Build(); @@ -78,7 +66,7 @@ public void SenderPersistence() { var receiverPersister = new InMemoryReceiverPersister(); var address = "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var receiver = new ReceiverBuilder(address, "https://example.com", ohttpKeys) .Build() @@ -86,7 +74,7 @@ public void SenderPersistence() var uri = receiver.PjUri(); var senderPersister = new InMemorySenderPersister(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; var withReplyKey = new SenderBuilder(psbt, uri) .BuildRecommended(1000) @@ -103,7 +91,7 @@ public async Task ReceiverPersistenceAsync() { var persister = new InMemoryReceiverPersisterAsync(); var address = "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var builder = new ReceiverBuilder(address, "https://example.com", ohttpKeys); var transition = builder.Build(); @@ -120,7 +108,7 @@ public async Task SenderPersistenceAsync() { var receiverPersister = new InMemoryReceiverPersisterAsync(); var address = "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var receiver = await new ReceiverBuilder(address, "https://example.com", ohttpKeys) .Build() @@ -128,7 +116,7 @@ public async Task SenderPersistenceAsync() var uri = receiver.PjUri(); var senderPersister = new InMemorySenderPersisterAsync(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; var withReplyKey = await new SenderBuilder(psbt, uri) .BuildRecommended(1000) @@ -143,24 +131,12 @@ public async Task SenderPersistenceAsync() public class CancelTests { - private static readonly byte[] OhttpKeysData = new byte[] - { - 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, - 0x92, 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, - 0x0c, 0xed, 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, - 0xad, 0x2c, 0xc9, 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, - 0xe8, 0xd2, 0x8b, 0xfe, 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, - 0x4d, 0x31, 0xcd, 0x85, 0x1e, 0x8a, 0x65, 0x40, 0xe8, 0x6c, - 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, 0x09, 0x57, 0x00, 0x04, - 0x00, 0x01, 0x00, 0x03, - }; - [Fact] public void ReceiverCancelFromInitialized() { var persister = new InMemoryReceiverPersister(); var address = "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var initialized = new ReceiverBuilder(address, "https://example.com", ohttpKeys) .Build() @@ -179,7 +155,7 @@ public async Task ReceiverCancelFromInitializedAsync() { var persister = new InMemoryReceiverPersisterAsync(); var address = "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var initialized = await new ReceiverBuilder(address, "https://example.com", ohttpKeys) .Build() @@ -198,7 +174,7 @@ public void SenderCancelFromWithReplyKey() { var receiverPersister = new InMemoryReceiverPersister(); var address = "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var receiver = new ReceiverBuilder(address, "https://example.com", ohttpKeys) .Build() @@ -206,7 +182,7 @@ public void SenderCancelFromWithReplyKey() var uri = receiver.PjUri(); var senderPersister = new InMemorySenderPersister(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; var withReplyKey = new SenderBuilder(psbt, uri) .BuildRecommended(1000) .Save(senderPersister); @@ -228,7 +204,7 @@ public async Task SenderCancelFromWithReplyKeyAsync() { var receiverPersister = new InMemoryReceiverPersisterAsync(); var address = "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK"; - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var receiver = await new ReceiverBuilder(address, "https://example.com", ohttpKeys) .Build() @@ -236,7 +212,7 @@ public async Task SenderCancelFromWithReplyKeyAsync() var uri = receiver.PjUri(); var senderPersister = new InMemorySenderPersisterAsync(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; var withReplyKey = await new SenderBuilder(psbt, uri) .BuildRecommended(1000) .SaveAsync(senderPersister); @@ -256,21 +232,9 @@ public async Task SenderCancelFromWithReplyKeyAsync() public class ValidationTests { - private static readonly byte[] OhttpKeysData = new byte[] - { - 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, - 0x92, 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, - 0x0c, 0xed, 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, - 0xad, 0x2c, 0xc9, 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, - 0xe8, 0xd2, 0x8b, 0xfe, 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, - 0x4d, 0x31, 0xcd, 0x85, 0x1e, 0x8a, 0x65, 0x40, 0xe8, 0x6c, - 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, 0x09, 0x57, 0x00, 0x04, - 0x00, 0x01, 0x00, 0x03, - }; - private static PjUri CreateV2PjUri() { - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); var persister = new InMemoryReceiverPersister(); using var builder = new ReceiverBuilder("2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", "https://example.com", ohttpKeys); using var transition = builder.Build(); @@ -281,7 +245,7 @@ private static PjUri CreateV2PjUri() [Fact] public void ReceiverBuilderRejectsBadAddress() { - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); Assert.Throws(() => { @@ -322,7 +286,7 @@ public void SenderBuilderRejectsBadPsbt() [Fact] public void ReceiverBuilderRejectsAmountOverflow() { - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); using var builder = new ReceiverBuilder( "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", "https://example.com", @@ -337,7 +301,7 @@ public void ReceiverBuilderRejectsAmountOverflow() [Fact] public void ReceiverBuilderRejectsExpirationOverflow() { - var ohttpKeys = OhttpKeys.Decode(OhttpKeysData); + var ohttpKeys = OhttpKeys.Decode(TestVectors.OhttpKeys); using var builder = new ReceiverBuilder( "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", "https://example.com", @@ -353,7 +317,7 @@ public void ReceiverBuilderRejectsExpirationOverflow() public void SenderBuilderWithAdditionalFeeRejectsFeeContributionOverflow() { using var uri = CreateV2PjUri(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; using var builder = new SenderBuilder(psbt, uri); var ex = Assert.Throws(() => @@ -368,7 +332,7 @@ public void SenderBuilderWithAdditionalFeeRejectsFeeContributionOverflow() public void SenderBuilderWithAdditionalFeeRejectsFeeRateOverflow() { using var uri = CreateV2PjUri(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; using var builder = new SenderBuilder(psbt, uri); var ex = Assert.Throws(() => @@ -383,7 +347,7 @@ public void SenderBuilderWithAdditionalFeeRejectsFeeRateOverflow() public void SenderBuilderNonIncentivizingRejectsFeeRateOverflow() { using var uri = CreateV2PjUri(); - var psbt = PayjoinMethods.OriginalPsbt(); + var psbt = TestVectors.OriginalPsbt; using var builder = new SenderBuilder(psbt, uri); var ex = Assert.Throws(() => diff --git a/payjoin-ffi/dart/contrib/test.sh b/payjoin-ffi/dart/contrib/test.sh index 227491e50..53af2c0de 100755 --- a/payjoin-ffi/dart/contrib/test.sh +++ b/payjoin-ffi/dart/contrib/test.sh @@ -11,8 +11,10 @@ use_lockfile Cargo-recent.lock cd "$REPO_ROOT/payjoin-ffi/dart" -echo "==> Cleaning nested Cargo.lock..." -rm -f native/Cargo.lock +echo "==> Seeding native wrapper dependencies from the maintained lockfile..." +# The wrapper is a separate workspace. Preserve the tested dependency versions +# when Cargo adds its wrapper package during the native build hook. +cp "$REPO_ROOT/Cargo.lock" native/Cargo.lock echo "==> Generating FFI bindings..." bash ./scripts/generate_bindings.sh diff --git a/payjoin-ffi/dart/test/fetch_ohttp_keys_http_test.dart b/payjoin-ffi/dart/test/fetch_ohttp_keys_http_test.dart index 1f3a5e195..765d23ec7 100644 --- a/payjoin-ffi/dart/test/fetch_ohttp_keys_http_test.dart +++ b/payjoin-ffi/dart/test/fetch_ohttp_keys_http_test.dart @@ -8,6 +8,8 @@ import 'package:test/test.dart'; import 'package:payjoin/http.dart' as payjoin_http; import 'package:payjoin/payjoin.dart' as payjoin; +import 'fixtures.dart' as fixtures; + const _localhostCertPem = '-----BEGIN CERTIFICATE-----\n' 'MIIBmDCCAT+gAwIBAgIUZmuZcOJ7AKPKxmXUdl8mALQqLjkwCgYIKoZIzj0EAwIw\n' @@ -28,21 +30,7 @@ const _localhostKeyPem = '1f17kOSt4gPp9WH21EiioVJREvUvyraYsjEuXhbkvgdEm4+QpUKJQj3+\n' '-----END PRIVATE KEY-----'; -final _ohttpKeysBytes = Uint8List.fromList( - _hexToBytes( - '01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad' - '2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382' - 'af588d370957000400010003', - ), -); - -List _hexToBytes(String hex) { - final bytes = []; - for (var i = 0; i < hex.length; i += 2) { - bytes.add(int.parse(hex.substring(i, i + 2), radix: 16)); - } - return bytes; -} +final _ohttpKeysBytes = fixtures.ohttpKeys; Uint8List _localhostCertDer() => base64.decode( _localhostCertPem.replaceAll(RegExp(r'-----[^-]+-----|\s'), ''), diff --git a/payjoin-ffi/dart/test/fixtures.dart b/payjoin-ffi/dart/test/fixtures.dart new file mode 100644 index 000000000..f0350ca58 --- /dev/null +++ b/payjoin-ffi/dart/test/fixtures.dart @@ -0,0 +1,19 @@ +import 'dart:io'; +import 'dart:isolate'; +import 'dart:typed_data'; + +import 'package:convert/convert.dart'; + +// Resolve from the package source so fixture loading is independent of the +// test runner's working directory. These tests run from the repository checkout. +final _fixtures = Isolate.resolvePackageUriSync( + Uri.parse('package:payjoin/payjoin.dart'), +)!.resolve('../../../payjoin-test-utils/fixtures/'); + +final originalPsbt = File.fromUri(_fixtures.resolve('original-psbt.base64')) + .readAsStringSync(); +final _ohttpKeysHex = File.fromUri(_fixtures.resolve('ohttp-keys.hex')) + .readAsStringSync() + .trim(); + +Uint8List get ohttpKeys => Uint8List.fromList(hex.decode(_ohttpKeysHex)); diff --git a/payjoin-ffi/dart/test/test_payjoin_integration_test.dart b/payjoin-ffi/dart/test/test_payjoin_integration_test.dart index 0c9acab25..bf86434e6 100644 --- a/payjoin-ffi/dart/test/test_payjoin_integration_test.dart +++ b/payjoin-ffi/dart/test/test_payjoin_integration_test.dart @@ -9,6 +9,7 @@ import "package:payjoin/http.dart" as payjoin_http; import "package:payjoin/payjoin.dart" as payjoin; import "package:payjoin/test_utils.dart" as test_utils; +import "fixtures.dart" as fixtures; import "utils.dart"; late test_utils.BitcoindEnv env; @@ -477,7 +478,7 @@ void main() { ohttpKeys: ohttpKeys, ).build().save(persister: recvPersister).pjUri(); - final psbt = test_utils.originalPsbt(); + final psbt = fixtures.originalPsbt; // Large enough to overflow fee * weight but still parsable as Dart int. const overflowFeeRate = 5000000000000; // sat/kwu expect( diff --git a/payjoin-ffi/dart/test/test_payjoin_unit_test.dart b/payjoin-ffi/dart/test/test_payjoin_unit_test.dart index 31e8325b3..3cccc8424 100644 --- a/payjoin-ffi/dart/test/test_payjoin_unit_test.dart +++ b/payjoin-ffi/dart/test/test_payjoin_unit_test.dart @@ -1,9 +1,7 @@ -import 'dart:typed_data'; - -import 'package:convert/convert.dart'; import 'package:test/test.dart'; import "package:payjoin/payjoin.dart" as payjoin; +import "fixtures.dart" as fixtures; import "utils.dart"; void main() { @@ -64,13 +62,7 @@ void main() { payjoin.ReceiverBuilder( address: "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().save(persister: persister); final result = payjoin.replayReceiverEventLog(persister: persister); expect( @@ -85,18 +77,12 @@ void main() { var receiver = payjoin.ReceiverBuilder( address: "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().save(persister: receiver_persister); var uri = receiver.pjUri(); var sender_persister = InMemorySenderPersister(); - var psbt = payjoin.originalPsbt(); + var psbt = fixtures.originalPsbt; payjoin.SenderBuilder(psbt: psbt, uri: uri) .buildRecommended(minFeeRateSatPerKwu: 1000) .save(persister: sender_persister); @@ -117,13 +103,7 @@ void main() { var initialized = payjoin.ReceiverBuilder( address: "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().save(persister: persister); var cancelTransition = initialized.cancel(); var fallbackTx = cancelTransition.save(persister: persister); @@ -141,13 +121,7 @@ void main() { var initialized = await payjoin.ReceiverBuilder( address: "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().saveAsync(persister: persister); var cancelTransition = initialized.cancel(); var fallbackTx = await cancelTransition.saveAsync(persister: persister); @@ -169,18 +143,12 @@ void main() { var receiver = payjoin.ReceiverBuilder( address: "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().save(persister: receiver_persister); var uri = receiver.pjUri(); var sender_persister = InMemorySenderPersister(); - var psbt = payjoin.originalPsbt(); + var psbt = fixtures.originalPsbt; var withReplyKey = payjoin.SenderBuilder(psbt: psbt, uri: uri) .buildRecommended(minFeeRateSatPerKwu: 1000) .save(persister: sender_persister); @@ -212,18 +180,12 @@ void main() { var receiver = await payjoin.ReceiverBuilder( address: "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().saveAsync(persister: receiver_persister); var uri = receiver.pjUri(); var sender_persister = InMemorySenderPersisterAsync(); - var psbt = payjoin.originalPsbt(); + var psbt = fixtures.originalPsbt; var withReplyKey = await payjoin.SenderBuilder(psbt: psbt, uri: uri) .buildRecommended(minFeeRateSatPerKwu: 1000) .saveAsync(persister: sender_persister); @@ -259,13 +221,7 @@ void main() { await payjoin.ReceiverBuilder( address: "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().saveAsync(persister: persister); final result = await payjoin.replayReceiverEventLogAsync( persister: persister, @@ -282,18 +238,12 @@ void main() { var receiver = await payjoin.ReceiverBuilder( address: "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", directory: "https://example.com", - ohttpKeys: payjoin.OhttpKeys.decode( - bytes: Uint8List.fromList( - hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", - ), - ), - ), + ohttpKeys: payjoin.OhttpKeys.decode(bytes: fixtures.ohttpKeys), ).build().saveAsync(persister: receiver_persister); var uri = receiver.pjUri(); var sender_persister = InMemorySenderPersisterAsync(); - var psbt = payjoin.originalPsbt(); + var psbt = fixtures.originalPsbt; await payjoin.SenderBuilder(psbt: psbt, uri: uri) .buildRecommended(minFeeRateSatPerKwu: 1000) .saveAsync(persister: sender_persister); diff --git a/payjoin-ffi/javascript/test/fixtures.ts b/payjoin-ffi/javascript/test/fixtures.ts new file mode 100644 index 000000000..82ad7c2ec --- /dev/null +++ b/payjoin-ffi/javascript/test/fixtures.ts @@ -0,0 +1,19 @@ +import { readFileSync } from "node:fs"; + +const fixtures = new URL( + "../../../payjoin-test-utils/fixtures/", + import.meta.url, +); + +export const ORIGINAL_PSBT = readFileSync( + new URL("original-psbt.base64", fixtures), + "ascii", +); +// Copy into a Uint8Array so the ArrayBuffer contains exactly the fixture bytes, +// without the offset or extra capacity of Node's pooled Buffer. +export const OHTTP_KEYS = Uint8Array.from( + Buffer.from( + readFileSync(new URL("ohttp-keys.hex", fixtures), "ascii").trim(), + "hex", + ), +).buffer; diff --git a/payjoin-ffi/javascript/test/integration.test.ts b/payjoin-ffi/javascript/test/integration.test.ts index fb607b83f..ff5a975df 100644 --- a/payjoin-ffi/javascript/test/integration.test.ts +++ b/payjoin-ffi/javascript/test/integration.test.ts @@ -1,3 +1,4 @@ +import { ORIGINAL_PSBT } from "./fixtures.ts"; import * as testUtils from "../test-utils/index.js"; import assert from "assert"; import { readFileSync } from "node:fs"; @@ -507,7 +508,7 @@ function testFfiValidation(payjoin: PayjoinModule): void { const pjUri = payjoin.Uri.parse( "bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?amount=1&pj=https://example.com", ).checkPjSupported(); - const psbt = testUtils.originalPsbt(); + const psbt = ORIGINAL_PSBT; assert.throws(() => { new payjoin.SenderBuilder(psbt, pjUri).buildRecommended( 18446744073709551615n, diff --git a/payjoin-ffi/javascript/test/unit.test.ts b/payjoin-ffi/javascript/test/unit.test.ts index 89386421e..50cb4c81f 100644 --- a/payjoin-ffi/javascript/test/unit.test.ts +++ b/payjoin-ffi/javascript/test/unit.test.ts @@ -1,3 +1,4 @@ +import { ORIGINAL_PSBT, OHTTP_KEYS } from "./fixtures.ts"; import { describe, test, before } from "node:test"; import assert from "node:assert"; import { readFileSync } from "node:fs"; @@ -29,19 +30,6 @@ async function webUniffiInitAsync() { webPayjoinModule.default.initialize(); } -const OHTTP_KEYS = new Uint8Array([ - 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, 0x92, 0xa3, - 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, 0x0c, 0xed, 0x02, 0x18, - 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, 0xad, 0x2c, 0xc9, 0x1b, 0xb4, 0x83, - 0x82, 0x4f, 0xe2, 0xbe, 0xe8, 0xd2, 0x8b, 0xfe, 0x2e, 0xb2, 0xfc, 0x64, - 0x53, 0xbc, 0x4d, 0x31, 0xcd, 0x85, 0x1e, 0x8a, 0x65, 0x40, 0xe8, 0x6c, - 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, 0x09, 0x57, 0x00, 0x04, 0x00, 0x01, - 0x00, 0x03, -]).buffer; - -const ORIGINAL_PSBT = - "cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA="; - function runUnitTests(name: string, payjoin: typeof nodejsPayjoin) { describe(`[${name}] URI tests`, () => { test("URL encoded payjoin parameter", () => { diff --git a/payjoin-ffi/python/test/fixtures.py b/payjoin-ffi/python/test/fixtures.py new file mode 100644 index 000000000..5603fb38b --- /dev/null +++ b/payjoin-ffi/python/test/fixtures.py @@ -0,0 +1,6 @@ +from pathlib import Path + +_FIXTURES = Path(__file__).resolve().parents[3] / "payjoin-test-utils" / "fixtures" + +ORIGINAL_PSBT = (_FIXTURES / "original-psbt.base64").read_text(encoding="ascii") +OHTTP_KEYS = bytes.fromhex((_FIXTURES / "ohttp-keys.hex").read_text(encoding="ascii")) diff --git a/payjoin-ffi/python/test/test_payjoin_integration_test.py b/payjoin-ffi/python/test/test_payjoin_integration_test.py index b08364f28..3ae758bc7 100644 --- a/payjoin-ffi/python/test/test_payjoin_integration_test.py +++ b/payjoin-ffi/python/test/test_payjoin_integration_test.py @@ -15,6 +15,7 @@ 0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")) ) +from .fixtures import ORIGINAL_PSBT from .utils import InMemoryReceiverPersister, InMemorySenderPersister @@ -80,7 +81,7 @@ async def test_ffi_validation(self): ).pj_uri() with self.assertRaises(SenderInputError.FfiValidation) as ctx: - SenderBuilder(original_psbt(), pj_uri).build_recommended(2**64 - 1) + SenderBuilder(ORIGINAL_PSBT, pj_uri).build_recommended(2**64 - 1) self.assertIsInstance(ctx.exception[0], FfiValidationError.FeeRateOutOfRange) # PjUri rejects amount out of range. diff --git a/payjoin-ffi/python/test/test_payjoin_unit_test.py b/payjoin-ffi/python/test/test_payjoin_unit_test.py index 61b5c0780..d78eed493 100644 --- a/payjoin-ffi/python/test/test_payjoin_unit_test.py +++ b/payjoin-ffi/python/test/test_payjoin_unit_test.py @@ -1,6 +1,7 @@ import unittest from typing import cast import payjoin +from .fixtures import ORIGINAL_PSBT, OHTTP_KEYS from .utils import ( InMemoryReceiverPersister, InMemoryReceiverPersisterAsync, @@ -45,11 +46,7 @@ def test_receiver_persistence(self): payjoin.ReceiverBuilder( "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ).build().save(persister) result = payjoin.replay_receiver_event_log(persister) self.assertTrue(result.state().is_INITIALIZED()) @@ -63,11 +60,7 @@ def test_sender_persistence(self): payjoin.ReceiverBuilder( "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save(persister) @@ -75,7 +68,7 @@ def test_sender_persistence(self): uri = receiver.pj_uri() persister = InMemorySenderPersister() - psbt = payjoin.original_psbt() + psbt = ORIGINAL_PSBT with_reply_key = ( payjoin.SenderBuilder(psbt, uri).build_recommended(1000).save(persister) ) @@ -91,11 +84,7 @@ async def run_test(): payjoin.ReceiverBuilder( "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save_async(persister) @@ -117,11 +106,7 @@ async def run_test(): payjoin.ReceiverBuilder( "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save_async(persister) @@ -129,7 +114,7 @@ async def run_test(): uri = receiver.pj_uri() persister = InMemorySenderPersisterAsync() - psbt = payjoin.original_psbt() + psbt = ORIGINAL_PSBT with_reply_key = await ( payjoin.SenderBuilder(psbt, uri) .build_recommended(1000) @@ -146,11 +131,7 @@ def test_receiver_cancel(self): payjoin.ReceiverBuilder( "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save(persister) @@ -172,11 +153,7 @@ async def run_test(): payjoin.ReceiverBuilder( "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save_async(persister) @@ -198,11 +175,7 @@ def test_sender_cancel(self): payjoin.ReceiverBuilder( "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save(persister) @@ -210,7 +183,7 @@ def test_sender_cancel(self): uri = receiver.pj_uri() persister = InMemorySenderPersister() - psbt = payjoin.original_psbt() + psbt = ORIGINAL_PSBT with_reply_key = ( payjoin.SenderBuilder(psbt, uri).build_recommended(1000).save(persister) ) @@ -236,11 +209,7 @@ async def run_test(): payjoin.ReceiverBuilder( "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) .build() .save_async(persister) @@ -248,7 +217,7 @@ async def run_test(): uri = receiver.pj_uri() persister = InMemorySenderPersisterAsync() - psbt = payjoin.original_psbt() + psbt = ORIGINAL_PSBT with_reply_key = await ( payjoin.SenderBuilder(psbt, uri) .build_recommended(1000) @@ -273,11 +242,7 @@ def test_receiver_builder_rejects_bad_address(self): payjoin.ReceiverBuilder( "not-an-address", "https://example.com", - payjoin.OhttpKeys.decode( - bytes.fromhex( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" - ) - ), + payjoin.OhttpKeys.decode(OHTTP_KEYS), ) def test_input_pair_rejects_invalid_outpoint(self): diff --git a/payjoin-test-utils/fixtures/README.md b/payjoin-test-utils/fixtures/README.md new file mode 100644 index 000000000..e4afff811 --- /dev/null +++ b/payjoin-test-utils/fixtures/README.md @@ -0,0 +1,32 @@ +# Shared binding test vectors + +Rust and the Python, Dart, JavaScript, and C# binding tests read these files +from this repository. Update the fixture here instead of copying its contents +into a language test. Language-specific loaders belong in the test directories; +these files do not require new exports from the production bindings. + +The `.base64` files preserve the existing Rust test constants byte for byte, +including the intentionally invalid input. They have no trailing newline so +`include_str!` preserves the constants' values. + +| File | Origin and purpose | +| ------------------------------------------ | -------------------------------------------------------------------------- | +| `original-psbt.base64` | BIP 78 original PSBT, used to build sender sessions | +| `payjoin-proposal.base64` | BIP 78 proposal | +| `payjoin-proposal-with-sender-info.base64` | BIP 78 proposal with sender information | +| `receiver-input-contribution.base64` | Receiver input contribution from the BIP 78 vector | +| `invalid-psbt.base64` | Existing BIP 174 invalid input, preserved including its HTML entities | +| `ohttp-keys.hex` | Fixed public key configuration previously embedded in each binding's tests | + +The PSBT vectors originate from [BIP 78](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#test-vectors) +and [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#test-vectors). + +The OHTTP fixture exercises decoding and session construction. It is not a +snapshot of `ohttp_key_config_bytes()` and has no corresponding private key +in these tests. Tests that decrypt requests must continue to use the matching +`ohttp_key_config_bytes()` / `ohttp_server()` pair or live `TestServices` keys. + +Python and JavaScript resolve fixture paths relative to their test source. +Dart resolves them relative to the package source. C# copies the shared files +into its test output directory through `Payjoin.Tests.csproj`. Run the binding +suites through each language's `contrib/test.sh` from a repository checkout. diff --git a/payjoin-test-utils/fixtures/invalid-psbt.base64 b/payjoin-test-utils/fixtures/invalid-psbt.base64 new file mode 100644 index 000000000..1e99e0f0c --- /dev/null +++ b/payjoin-test-utils/fixtures/invalid-psbt.base64 @@ -0,0 +1 @@ +AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA== \ No newline at end of file diff --git a/payjoin-test-utils/fixtures/ohttp-keys.hex b/payjoin-test-utils/fixtures/ohttp-keys.hex new file mode 100644 index 000000000..f70926c03 --- /dev/null +++ b/payjoin-test-utils/fixtures/ohttp-keys.hex @@ -0,0 +1 @@ +01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003 diff --git a/payjoin-test-utils/fixtures/original-psbt.base64 b/payjoin-test-utils/fixtures/original-psbt.base64 new file mode 100644 index 000000000..c77ae5ccd --- /dev/null +++ b/payjoin-test-utils/fixtures/original-psbt.base64 @@ -0,0 +1 @@ +cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA= \ No newline at end of file diff --git a/payjoin-test-utils/fixtures/payjoin-proposal-with-sender-info.base64 b/payjoin-test-utils/fixtures/payjoin-proposal-with-sender-info.base64 new file mode 100644 index 000000000..6cfa244b9 --- /dev/null +++ b/payjoin-test-utils/fixtures/payjoin-proposal-with-sender-info.base64 @@ -0,0 +1 @@ +cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAQEgqBvXBQAAAAAXqRTeTh6QYcpZE1sDWtXm1HmQRUNU0IcBBBYAFMeKRXJTVYKNVlgHTdUmDV/LaYUwIgYDFZrAGqDVh1TEtNi300ntHt/PCzYrT2tVEGcjooWPhRYYSFzWUDEAAIABAACAAAAAgAEAAAAAAAAAAAEBIICEHgAAAAAAF6kUyPLL+cphRyyI5GTUazV0hF2R2NWHAQcXFgAUX4BmVeWSTJIEwtUb5TlPS/ntohABCGsCRzBEAiBnu3tA3yWlT0WBClsXXS9j69Bt+waCs9JcjWtNjtv7VgIge2VYAaBeLPDB6HGFlpqOENXMldsJezF9Gs5amvDQRDQBIQJl1jz1tBt8hNx2owTm+4Du4isx0pmdKNMNIjjaMHFfrQABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUIgICygvBWB5prpfx61y1HDAwo37kYP3YRJBvAjtunBAur3wYSFzWUDEAAIABAACAAAAAgAEAAAABAAAAAAA= \ No newline at end of file diff --git a/payjoin-test-utils/fixtures/payjoin-proposal.base64 b/payjoin-test-utils/fixtures/payjoin-proposal.base64 new file mode 100644 index 000000000..5f6008c03 --- /dev/null +++ b/payjoin-test-utils/fixtures/payjoin-proposal.base64 @@ -0,0 +1 @@ +cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAQEgqBvXBQAAAAAXqRTeTh6QYcpZE1sDWtXm1HmQRUNU0IcAAQEggIQeAAAAAAAXqRTI8sv5ymFHLIjkZNRrNXSEXZHY1YcBBxcWABRfgGZV5ZJMkgTC1RvlOU9L+e2iEAEIawJHMEQCIGe7e0DfJaVPRYEKWxddL2Pr0G37BoKz0lyNa02O2/tWAiB7ZVgBoF4s8MHocYWWmo4Q1cyV2wl7MX0azlqa8NBENAEhAmXWPPW0G3yE3HajBOb7gO7iKzHSmZ0o0w0iONowcV+tAAAA \ No newline at end of file diff --git a/payjoin-test-utils/fixtures/receiver-input-contribution.base64 b/payjoin-test-utils/fixtures/receiver-input-contribution.base64 new file mode 100644 index 000000000..5144d9e06 --- /dev/null +++ b/payjoin-test-utils/fixtures/receiver-input-contribution.base64 @@ -0,0 +1 @@ +cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAAEBIICEHgAAAAAAF6kUyPLL+cphRyyI5GTUazV0hF2R2NWHAQcXFgAUX4BmVeWSTJIEwtUb5TlPS/ntohABCGsCRzBEAiBnu3tA3yWlT0WBClsXXS9j69Bt+waCs9JcjWtNjtv7VgIge2VYAaBeLPDB6HGFlpqOENXMldsJezF9Gs5amvDQRDQBIQJl1jz1tBt8hNx2owTm+4Du4isx0pmdKNMNIjjaMHFfrQAAAA== \ No newline at end of file diff --git a/payjoin-test-utils/src/lib.rs b/payjoin-test-utils/src/lib.rs index a9579d7bd..ecb29a322 100644 --- a/payjoin-test-utils/src/lib.rs +++ b/payjoin-test-utils/src/lib.rs @@ -111,19 +111,21 @@ pub const MAX_ADDITIONAL_FEE_CONTRIBUTION: Amount = Amount::from_sat(182); pub const ADDITIONAL_FEE_OUTPUT_INDEX: usize = 0; /// From the BIP-78 test vector -pub const ORIGINAL_PSBT: &str = "cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA="; +pub const ORIGINAL_PSBT: &str = include_str!("../fixtures/original-psbt.base64"); /// From the BIP-174 test vector -pub const INVALID_PSBT: &str = "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA=="; +pub const INVALID_PSBT: &str = include_str!("../fixtures/invalid-psbt.base64"); /// From the BIP-78 test vector -pub const PAYJOIN_PROPOSAL: &str = "cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAQEgqBvXBQAAAAAXqRTeTh6QYcpZE1sDWtXm1HmQRUNU0IcAAQEggIQeAAAAAAAXqRTI8sv5ymFHLIjkZNRrNXSEXZHY1YcBBxcWABRfgGZV5ZJMkgTC1RvlOU9L+e2iEAEIawJHMEQCIGe7e0DfJaVPRYEKWxddL2Pr0G37BoKz0lyNa02O2/tWAiB7ZVgBoF4s8MHocYWWmo4Q1cyV2wl7MX0azlqa8NBENAEhAmXWPPW0G3yE3HajBOb7gO7iKzHSmZ0o0w0iONowcV+tAAAA"; +pub const PAYJOIN_PROPOSAL: &str = include_str!("../fixtures/payjoin-proposal.base64"); /// From the BIP-78 test vector -pub const PAYJOIN_PROPOSAL_WITH_SENDER_INFO: &str = "cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAQEgqBvXBQAAAAAXqRTeTh6QYcpZE1sDWtXm1HmQRUNU0IcBBBYAFMeKRXJTVYKNVlgHTdUmDV/LaYUwIgYDFZrAGqDVh1TEtNi300ntHt/PCzYrT2tVEGcjooWPhRYYSFzWUDEAAIABAACAAAAAgAEAAAAAAAAAAAEBIICEHgAAAAAAF6kUyPLL+cphRyyI5GTUazV0hF2R2NWHAQcXFgAUX4BmVeWSTJIEwtUb5TlPS/ntohABCGsCRzBEAiBnu3tA3yWlT0WBClsXXS9j69Bt+waCs9JcjWtNjtv7VgIge2VYAaBeLPDB6HGFlpqOENXMldsJezF9Gs5amvDQRDQBIQJl1jz1tBt8hNx2owTm+4Du4isx0pmdKNMNIjjaMHFfrQABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUIgICygvBWB5prpfx61y1HDAwo37kYP3YRJBvAjtunBAur3wYSFzWUDEAAIABAACAAAAAgAEAAAABAAAAAAA="; +pub const PAYJOIN_PROPOSAL_WITH_SENDER_INFO: &str = + include_str!("../fixtures/payjoin-proposal-with-sender-info.base64"); /// Input contribution for the receiver, from the BIP78 test vector -pub const RECEIVER_INPUT_CONTRIBUTION: &str = "cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAAEBIICEHgAAAAAAF6kUyPLL+cphRyyI5GTUazV0hF2R2NWHAQcXFgAUX4BmVeWSTJIEwtUb5TlPS/ntohABCGsCRzBEAiBnu3tA3yWlT0WBClsXXS9j69Bt+waCs9JcjWtNjtv7VgIge2VYAaBeLPDB6HGFlpqOENXMldsJezF9Gs5amvDQRDQBIQJl1jz1tBt8hNx2owTm+4Du4isx0pmdKNMNIjjaMHFfrQAAAA=="; +pub const RECEIVER_INPUT_CONTRIBUTION: &str = + include_str!("../fixtures/receiver-input-contribution.base64"); pub static PARSED_ORIGINAL_PSBT: Lazy = Lazy::new(|| Psbt::from_str(ORIGINAL_PSBT).expect("known psbt should parse")); diff --git a/payjoin-test-utils/src/v2.rs b/payjoin-test-utils/src/v2.rs index 835460150..7dc32f2db 100644 --- a/payjoin-test-utils/src/v2.rs +++ b/payjoin-test-utils/src/v2.rs @@ -244,3 +244,18 @@ pub fn ohttp_key_config_bytes() -> Vec { pub fn ohttp_server() -> ohttp::Server { ohttp::Server::new(test_key_config()).expect("valid ohttp server") } + +#[cfg(test)] +mod tests { + use bitcoin::hex::FromHex; + + use super::*; + + #[test] + fn shared_binding_ohttp_fixture_roundtrips() { + let bytes = Vec::from_hex(include_str!("../fixtures/ohttp-keys.hex").trim()) + .expect("valid hex fixture"); + let keys = OhttpKeys::decode(&bytes).expect("valid OHTTP key configuration"); + assert_eq!(keys.encode().expect("encodable OHTTP key configuration"), bytes); + } +}