@@ -158,9 +158,11 @@ Future<String?> pollSender(Sender sender) async {
158158 V2PostContext postReqCtx;
159159 try {
160160 final result = await sender.extractV2 (ohttpProxyUrl: ohttpProxyUrl);
161+ print ('extracted v2' );
161162 postReq = result.$1;
162163 postReqCtx = result.$2;
163164 } catch (e) {
165+ print ('failed to extract v2. err: $e ' );
164166 try {
165167 final (req, v1Ctx) = await sender.extractV1 ();
166168 print ('Posting Original PSBT Payload request...' );
@@ -187,28 +189,39 @@ Future<String?> pollSender(Sender sender) async {
187189 },
188190 body: postReq.body,
189191 );
190- final getCtx = await postReqCtx.processResponse (
191- response: postRes.bodyBytes,
192- );
193- String ? proposalPsbt;
194- while (true ) {
195- final (getRequest, getReqCtx) = await getCtx.extractReq (
196- ohttpRelay: ohttpProxyUrl,
197- );
198- final getRes = await http.post (
199- Uri .parse (getRequest.url.asString ()),
200- headers: {
201- 'Content-Type' : getRequest.contentType,
202- },
203- body: getRequest.body,
204- );
205- proposalPsbt = await getCtx.processResponse (
206- response: getRes.bodyBytes,
207- ohttpCtx: getReqCtx,
192+ try {
193+ print ('got post response' );
194+ final getCtx = await postReqCtx.processResponse (
195+ response: postRes.bodyBytes,
208196 );
209- break ;
197+ print ('processed post response' );
198+ String ? proposalPsbt;
199+ while (true ) {
200+ print ('extracting get request' );
201+ final (getRequest, getReqCtx) = await getCtx.extractReq (
202+ ohttpRelay: ohttpProxyUrl,
203+ );
204+ print ('got get request' );
205+ final getRes = await http.post (
206+ Uri .parse (getRequest.url.asString ()),
207+ headers: {
208+ 'Content-Type' : getRequest.contentType,
209+ },
210+ body: getRequest.body,
211+ );
212+ print ('got get response' );
213+ proposalPsbt = await getCtx.processResponse (
214+ response: getRes.bodyBytes,
215+ ohttpCtx: getReqCtx,
216+ );
217+ print ('processed get response' );
218+ break ;
219+ }
220+ return proposalPsbt;
221+ } catch (e) {
222+ print ('err: $e ' );
223+ throw Exception ('Error occurred while polling sender' );
210224 }
211- return proposalPsbt;
212225}
213226
214227Future <bool > addressExistsInWallet (String address, bdk.Wallet bdkWallet) async {
@@ -343,24 +356,26 @@ Future<void> _isolateSender(List<dynamic> args) async {
343356
344357 // SIGN AND BROADCAST ---------------------------
345358 try {
359+ print ('signing' );
346360 final psbtStruct =
347361 await bdk.PartiallySignedTransaction .fromString (proposal! );
348362 await wallet.sign (
349363 psbt: psbtStruct,
350364 signOptions: const bdk.SignOptions (
351- trustWitnessUtxo: false ,
365+ trustWitnessUtxo: true ,
352366 allowAllSighashes: false ,
353367 removePartialSigs: true ,
354368 tryFinalize: true ,
355369 signWithTapInternalKey: false ,
356370 allowGrinding: true ,
357371 ),
358372 );
359-
373+ print ( 'signed' );
360374 final finalizedTx = psbtStruct.extractTx ();
361375 final signedPsbt = psbtStruct.toString ();
362376
363377 //Broadcast the transaction
378+ print ('broadcasting' );
364379 final broadcastedTx =
365380 await blockchain.broadcast (transaction: finalizedTx);
366381 print ('Broadcasted transaction: $broadcastedTx ' );
@@ -430,17 +445,22 @@ void _isolateReceiver(List<dynamic> args) async {
430445 while (unchecked_proposal == null ) {
431446 try {
432447 final (req, context) = await receiver.extractReq ();
448+ print ('making request' );
433449 final ohttpResponse = await http.post (
434450 Uri .parse (req.url.asString ()),
435451 headers: {
436452 'Content-Type' : req.contentType,
437453 },
438454 body: req.body,
439455 );
456+ print ('got unchecked response' );
440457 unchecked_proposal = await receiver.processRes (
441458 body: ohttpResponse.bodyBytes,
442459 ctx: context,
443460 );
461+ if (unchecked_proposal != null ) {
462+ break ;
463+ }
444464 } catch (e) {
445465 sendPort.send (
446466 Err (
@@ -452,24 +472,24 @@ void _isolateReceiver(List<dynamic> args) async {
452472 break ;
453473 }
454474 }
455- if (unchecked_proposal == null ) {
456- print ('FAILED TO GET PROPOSAL' );
457- }
458475 final payjoin_proposal = await processPayjoinProposal (
459476 unchecked_proposal! ,
460477 isTestnet,
461478 wallet,
462479 blockchain,
463480 );
481+ print ('payjoin proposal: $payjoin_proposal ' );
464482 try {
465483 final (postReq, ohttpCtx) = await payjoin_proposal.extractV2Req ();
484+ print ('extracted v2 req' );
466485 final postRes = await http.post (
467486 Uri .parse (postReq.url.asString ()),
468487 headers: {
469488 'Content-Type' : postReq.contentType,
470489 },
471490 body: postReq.body,
472491 );
492+ print ('processed res' );
473493 await payjoin_proposal.processRes (
474494 res: postRes.bodyBytes,
475495 ohttpContext: ohttpCtx,
@@ -498,92 +518,104 @@ Future<PayjoinProposal> processPayjoinProposal(
498518 final fallbackTx = await proposal.extractTxToScheduleBroadcast ();
499519 print ('fallback tx (broadcast this if payjoin fails): $fallbackTx ' );
500520
501- // Receive Check 1: can broadcast
502- final pj1 = await proposal.assumeInteractiveReceiver ();
503- // Receive Check 2: original PSBT has no receiver-owned inputs
504- final pj2 = await pj1.checkInputsNotOwned (
505- isOwned: (inputScript) async {
506- final address = await bdk.Address .fromScript (
507- script: bdk.ScriptBuf (bytes: inputScript),
508- network: isTestnet ? bdk.Network .testnet : bdk.Network .bitcoin,
509- );
510- return await addressExistsInWallet (address.toString (), wallet);
511- },
512- );
513- // Receive Check 3: sender inputs have not been seen before (prevent probing attacks)
514- final pj3 = await pj2.checkNoInputsSeenBefore (
515- isKnown: (input) {
516- // TODO: keep track of seen inputs in hive storage?
517- return false ;
518- },
519- );
521+ try {
522+ // Receive Check 1: can broadcast
523+ print ('check1' );
524+ final pj1 = await proposal.assumeInteractiveReceiver ();
525+ print ('check2' );
526+ // Receive Check 2: original PSBT has no receiver-owned inputs
527+ final pj2 = await pj1.checkInputsNotOwned (
528+ isOwned: (inputScript) async {
529+ final address = await bdk.Address .fromScript (
530+ script: bdk.ScriptBuf (bytes: inputScript),
531+ network: isTestnet ? bdk.Network .testnet : bdk.Network .bitcoin,
532+ );
533+ return await addressExistsInWallet (address.toString (), wallet);
534+ },
535+ );
536+ // Receive Check 3: sender inputs have not been seen before (prevent probing attacks)
537+ print ('check3' );
538+ final pj3 = await pj2.checkNoInputsSeenBefore (
539+ isKnown: (input) {
540+ // TODO: keep track of seen inputs in hive storage?
541+ return false ;
542+ },
543+ );
520544
521- // Identify receiver outputs
522- final pj4 = await pj3.identifyReceiverOutputs (
523- isReceiverOutput: (outputScript) async {
524- final address = await bdk.Address .fromScript (
525- script: bdk.ScriptBuf (bytes: outputScript),
526- network: isTestnet ? bdk.Network .testnet : bdk.Network .bitcoin,
527- );
528- return await addressExistsInWallet (address.toString (), wallet);
529- },
530- );
531- final pj5 = await pj4.commitOutputs ();
545+ // Identify receiver outputs
546+ print ('check4' );
547+ final pj4 = await pj3.identifyReceiverOutputs (
548+ isReceiverOutput: (outputScript) async {
549+ final address = await bdk.Address .fromScript (
550+ script: bdk.ScriptBuf (bytes: outputScript),
551+ network: isTestnet ? bdk.Network .testnet : bdk.Network .bitcoin,
552+ );
553+ return await addressExistsInWallet (address.toString (), wallet);
554+ },
555+ );
556+ final pj5 = await pj4.commitOutputs ();
532557
533- // Contribute receiver inputs
534- final utxos = await getSpendableUtxosFromBdkWallet (
535- wallet,
536- isTestnet ? bdk.Network .testnet : bdk.Network .bitcoin,
537- );
538- final inputs = await Future .wait (
539- utxos.map ((utxo) => inputPairFromUtxo (utxo, isTestnet)),
540- );
541- final selected_utxo = await pj5.tryPreservingPrivacy (
542- candidateInputs: inputs,
543- );
544- final pj6 = await pj5.contributeInputs (replacementInputs: [selected_utxo]);
545- final pj7 = await pj6.commitInputs ();
546-
547- // Finalize proposal
548- final payjoin_proposal = await pj7.finalizeProposal (
549- processPsbt: (String psbt) async {
550- // TODO: sign PSBT
551- final psbtStruct = await bdk.PartiallySignedTransaction .fromString (psbt);
552- await wallet.sign (
553- psbt: psbtStruct,
554- signOptions: const bdk.SignOptions (
555- trustWitnessUtxo: false ,
556- allowAllSighashes: false ,
557- removePartialSigs: true ,
558- tryFinalize: true ,
559- signWithTapInternalKey: false ,
560- allowGrinding: true ,
561- ),
562- );
563- return psbt;
564- },
565- maxFeeRateSatPerVb: BigInt .zero,
566- );
567- return payjoin_proposal;
558+ // Contribute receiver inputs
559+ print ('get spendable utxos' );
560+ final unspent = wallet.listUnspent ();
561+ final inputs = await Future .wait (
562+ unspent.map ((unspent) => inputPairFromUtxo (unspent, isTestnet)),
563+ );
564+ print ('selected utxo' );
565+ final selected_utxo = await pj5.tryPreservingPrivacy (
566+ candidateInputs: inputs,
567+ );
568+ print ('contribute inputs' );
569+ final pj6 = await pj5.contributeInputs (replacementInputs: [selected_utxo]);
570+ print ('commit inputs' );
571+ final pj7 = await pj6.commitInputs ();
572+
573+ // Finalize proposal
574+ print ('finalize proposal' );
575+ final payjoin_proposal = await pj7.finalizeProposal (
576+ processPsbt: (String psbt) async {
577+ print ('finalizeProposal psbt $psbt ' );
578+ // TODO: sign PSBT
579+ final psbtStruct =
580+ await bdk.PartiallySignedTransaction .fromString (psbt);
581+ print ('unsigned psbtStruct $psbtStruct ' );
582+ final signed = await wallet.sign (
583+ psbt: psbtStruct,
584+ signOptions: const bdk.SignOptions (
585+ trustWitnessUtxo: false ,
586+ allowAllSighashes: false ,
587+ removePartialSigs: true ,
588+ tryFinalize: true ,
589+ signWithTapInternalKey: true ,
590+ allowGrinding: true ,
591+ ),
592+ );
593+ print ('signed $signed ' );
594+ final signedPsbt = psbtStruct.toString ();
595+ print ('signedPsbt $signedPsbt ' );
596+ return signedPsbt;
597+ },
598+ maxFeeRateSatPerVb: BigInt .from (10000 ),
599+ );
600+ return payjoin_proposal;
601+ } catch (e) {
602+ print ('err: $e ' );
603+ throw Exception ('Error occurred while finalizing proposal' );
604+ }
568605}
569606
570- Future <InputPair > inputPairFromUtxo (UTXO utxo, bool isTestnet) async {
571- // TODO: this seems like a roundabout way of getting the script pubkey
572- final address = await bdk.Address .fromString (
573- s: utxo.address.address,
574- network: isTestnet ? bdk.Network .testnet : bdk.Network .bitcoin,
575- );
576- final spk = address.scriptPubkey ().bytes;
607+ Future <InputPair > inputPairFromUtxo (bdk.LocalUtxo utxo, bool isTestnet) async {
577608 final psbtin = PsbtInput (
609+ // We should be able to merge these bdk & payjoin rust-bitcoin types with bitcoin-ffi eventually
578610 witnessUtxo: TxOut (
579- value: BigInt . from ( utxo.value) ,
580- scriptPubkey: spk ,
611+ value: utxo.txout. value,
612+ scriptPubkey: utxo.txout.scriptPubkey.bytes ,
581613 ),
582614 // TODO: redeem script/witness script?
583615 );
584- // TODO: perhaps TxIn.default() should be exposed in payjoin_flutter api
585616 final txin = TxIn (
586- previousOutput: OutPoint (txid: utxo.txid, vout: utxo.txIndex),
617+ previousOutput:
618+ OutPoint (txid: utxo.outpoint.txid, vout: utxo.outpoint.vout),
587619 scriptSig: await Script .newInstance (rawOutputScript: []),
588620 sequence: 0xFFFFFFFF ,
589621 witness: [],
0 commit comments