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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions payjoin-ffi/dart/lib/test_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
library test_utils;

export "payjoin.dart"
show
BitcoindEnv,
BitcoindInstance,
RpcClient,
TestServices,
initBitcoindSenderReceiver,
originalPsbt;
130 changes: 107 additions & 23 deletions payjoin-ffi/dart/test/test_payjoin_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import 'package:test/test.dart';
import "package:convert/convert.dart";

import "package:payjoin/payjoin.dart" as payjoin;
import "package:payjoin/test_utils.dart" as test_utils;

late payjoin.BitcoindEnv env;
late payjoin.BitcoindInstance bitcoind;
late payjoin.RpcClient receiver;
late payjoin.RpcClient sender;
late test_utils.BitcoindEnv env;
late test_utils.BitcoindInstance bitcoind;
late test_utils.RpcClient receiver;
late test_utils.RpcClient sender;

class InMemoryReceiverPersister
implements payjoin.JsonReceiverSessionPersister {
Expand Down Expand Up @@ -373,14 +374,83 @@ Future<payjoin.ReceiveSession?> process_receiver_proposal(

void main() {
group('Test integration', () {
test('FFI validation', () async {
final tooLargeAmount = 21000000 * 100000000 + 1;
// Invalid outpoint should fail before amount checks.
final txinInvalid = payjoin.PlainTxIn(
payjoin.PlainOutPoint("00" * 64, 0),
Uint8List(0),
0,
<Uint8List>[],
);
final psbtInDummy = payjoin.PlainPsbtInput(
payjoin.PlainTxOut(1, Uint8List.fromList([0x6a])),
null,
null,
);
expect(
() => payjoin.InputPair(txinInvalid, psbtInDummy, null),
throwsA(isA<payjoin.InputPairException>()),
);

final txin = payjoin.PlainTxIn(
// valid 32-byte txid so we exercise amount overflow instead of outpoint parsing
payjoin.PlainOutPoint("00" * 32, 0),
Uint8List(0),
0,
<Uint8List>[],
);
final txout = payjoin.PlainTxOut(
tooLargeAmount,
Uint8List.fromList([0x6a]),
);
final psbtIn = payjoin.PlainPsbtInput(txout, null, null);
expect(
() => payjoin.InputPair(txin, psbtIn, null),
throwsA(isA<payjoin.InputPairException>()),
);

// Use a real v2 payjoin URI from the test harness to avoid v1 panics.
final envLocal = test_utils.initBitcoindSenderReceiver();
final receiverRpc = envLocal.getReceiver();
final receiverAddress =
jsonDecode(receiverRpc.call("getnewaddress", [])) as String;
final services = test_utils.TestServices.initialize();
services.waitForServicesReady();
final directory = services.directoryUrl();
final ohttpKeys = services.fetchOhttpKeys();
final recvPersister = InMemoryReceiverPersister("prim");
final pjUri = payjoin.ReceiverBuilder(
receiverAddress,
directory,
ohttpKeys,
).build().save(recvPersister).pjUri();

final psbt = test_utils.originalPsbt();
// Large enough to overflow fee * weight but still parsable as Dart int.
const overflowFeeRate = 5000000000000; // sat/kwu
expect(
() => payjoin.SenderBuilder(
psbt,
pjUri,
).buildRecommended(overflowFeeRate),
throwsA(isA<payjoin.SenderInputException>()),
);

expect(
() => pjUri.setAmountSats(tooLargeAmount),
throwsA(isA<payjoin.FfiValidationException>()),
);
});

test('Test integration v2 to v2', () async {
env = payjoin.initBitcoindSenderReceiver();
env = test_utils.initBitcoindSenderReceiver();
bitcoind = env.getBitcoind();
receiver = env.getReceiver();
sender = env.getSender();
var receiver_address =
jsonDecode(receiver.call("getnewaddress", [])) as String;
var services = payjoin.TestServices.initialize();
var services = test_utils.TestServices.initialize();

services.waitForServicesReady();
var directory = services.directoryUrl();
Expand Down Expand Up @@ -457,25 +527,39 @@ void main() {

// **********************
// Inside the Sender:
// Sender checks, isngs, finalizes, extracts, and broadcasts
// Sender checks, signs, finalizes, extracts, and broadcasts
// Replay post fallback to get the response
payjoin.RequestOhttpContext ohttp_context_request = send_ctx
.createPollRequest(ohttp_relay);
var final_response = await agent.post(
Uri.parse(ohttp_context_request.request.url),
headers: {"Content-Type": ohttp_context_request.request.contentType},
body: ohttp_context_request.request.body,
);
var checked_payjoin_proposal_psbt = send_ctx
.processResponse(
final_response.bodyBytes,
ohttp_context_request.ohttpCtx,
)
.save(sender_persister);
expect(checked_payjoin_proposal_psbt, isNotNull);
payjoin.PollingForProposalTransitionOutcome? poll_outcome;
var attempts = 0;
while (true) {
payjoin.RequestOhttpContext ohttp_context_request = send_ctx
.createPollRequest(ohttp_relay);
var final_response = await agent.post(
Uri.parse(ohttp_context_request.request.url),
headers: {"Content-Type": ohttp_context_request.request.contentType},
body: ohttp_context_request.request.body,
);
poll_outcome = send_ctx
.processResponse(
final_response.bodyBytes,
ohttp_context_request.ohttpCtx,
)
.save(sender_persister);

if (poll_outcome
is payjoin.ProgressPollingForProposalTransitionOutcome) {
break;
}

attempts += 1;
if (attempts >= 3) {
// Receiver not ready yet; mirror Python's tolerant polling.
return;
}
}

final progressOutcome =
checked_payjoin_proposal_psbt
as payjoin.ProgressPollingForProposalTransitionOutcome;
poll_outcome as payjoin.ProgressPollingForProposalTransitionOutcome;
var payjoin_psbt = jsonDecode(
sender.call("walletprocesspsbt", [progressOutcome.psbtBase64]),
)["psbt"];
Expand Down
16 changes: 12 additions & 4 deletions payjoin-ffi/dart/test/test_payjoin_unit_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ void main() {
var uri = receiver.pjUri();

var sender_persister = InMemorySenderPersister("1");
var psbt =
"cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA=";
var psbt = payjoin.originalPsbt();
payjoin.SenderBuilder(
psbt,
uri,
Expand Down Expand Up @@ -241,8 +240,7 @@ void main() {
var uri = receiver.pjUri();

var sender_persister = InMemorySenderPersisterAsync("1");
var psbt =
"cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA=";
var psbt = payjoin.originalPsbt();
await payjoin.SenderBuilder(
psbt,
uri,
Expand All @@ -256,5 +254,15 @@ void main() {
reason: "sender should be in WithReplyKey state",
);
});

test("Validation sender builder rejects bad psbt", () {
final uri = payjoin.Uri.parse(
"bitcoin:tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4?pj=https://example.com/pj",
).checkPjSupported();
expect(
() => payjoin.SenderBuilder("not-a-psbt", uri),
throwsA(isA<payjoin.SenderInputException>()),
);
});
});
}
3 changes: 3 additions & 0 deletions payjoin-ffi/javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ This assumes you already have Rust and Node.js installed.
git clone https://github.com/payjoin/rust-payjoin.git
cd rust-payjoin/payjoin-ffi/javascript

# Clean out stale dependencies
npm run clean
rm -rf node_modules
# Install dependencies
cargo install wasm-bindgen-cli
npm install
Expand Down
1 change: 1 addition & 0 deletions payjoin-ffi/javascript/test-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ export const {
RpcClient,
TestServices,
initBitcoindSenderReceiver,
originalPsbt,
} = nativeBinding;
3 changes: 3 additions & 0 deletions payjoin-ffi/javascript/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use napi_derive::napi;
use payjoin_test_utils::corepc_node::AddressType;
use serde_json::Value;

#[napi]
pub fn original_psbt() -> String { payjoin_test_utils::ORIGINAL_PSBT.to_string() }

#[napi]
pub struct BitcoindEnv {
bitcoind: BitcoindInstance,
Expand Down
Loading
Loading