From 2a80763021f963cc4bbcc5757bd1da73b81b64c0 Mon Sep 17 00:00:00 2001 From: kumulynja Date: Mon, 24 Jun 2024 17:11:14 +0200 Subject: [PATCH] use assumeInteractiveReceiver + add comments for non-interactive use --- example/lib/payjoin_library.dart | 40 +++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/example/lib/payjoin_library.dart b/example/lib/payjoin_library.dart index d8a6a14..8b37344 100644 --- a/example/lib/payjoin_library.dart +++ b/example/lib/payjoin_library.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:flutter/cupertino.dart'; import 'package:payjoin_flutter/common.dart' as common; import 'package:payjoin_flutter/receive/v1.dart' as v1; +import 'package:payjoin_flutter/receive/v2.dart'; import 'package:payjoin_flutter/send.dart' as send; import 'package:payjoin_flutter/uri.dart' as pj_uri; @@ -33,19 +34,42 @@ class PayJoinLibrary { }); final unchecked = await v1.UncheckedProposal.fromRequest( body: body.toList(), query: '', headers: headers); - final provisionalProposal = await handleUnckedProposal(unchecked, isOwned); + final provisionalProposal = + await handleUncheckedProposal(unchecked, isOwned); return provisionalProposal; } - Future handleUnckedProposal( + Future handleUncheckedProposal( v1.UncheckedProposal uncheckedProposal, Future Function(Uint8List) isOwned) async { - // in a payment processor where the sender could go offline, this is where you schedule to broadcast the original_tx - var _ = await uncheckedProposal.extractTxToScheduleBroadcast(); - final inputsOwned = await uncheckedProposal.checkBroadcastSuitability( - canBroadcast: (e) async { - return true; - }); + // A consumer wallet has some manual interaction to initiate a payjoin, it + // is not a server that can receive a lot of requests without the user + // being aware of it. Therefore we say a consumer wallet app is an + // interactive receiver and an automatic payment processor is + // non-interactive. + // + // The way to check a proposal for these cases are different: + // - For an interactive receiver, you can just call + // `assumeInteractiveReceiver` as used here in the example code. + // - For a non-interactive receiver, you would extract the original tx + // with `extractTxToScheduleBroadcast` and check if it can be + // broadcasted in `checkBroadcastSuitability`. This way, if the sender + // doesn't complete the payjoin, you can still broadcast the original + // tx and get your funds. This protects against sender maliciousness of + // probing your utxo set amongst other things. + + final inputsOwned = await uncheckedProposal.assumeInteractiveReceiver(); + /* + // Non-interactive receiver example code: + final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast(); + final inputsOwned = await uncheckedProposal.checkBroadcastSuitability( + canBroadcast: (e) async { + // Here you would check if the original tx is a valid tx that pays you + // and that can be broadcasted. + return true; + }); + */ + // Receive Check 2: receiver can't sign for proposal inputs final mixedInputScripts = await inputsOwned.checkInputsNotOwned(isOwned: isOwned);