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); + } +}