Skip to content

Commit 6d4b491

Browse files
committed
Simplify sender's implementation, fix typos
1 parent 93c655a commit 6d4b491

File tree

1 file changed

+30
-20
lines changed

1 file changed

+30
-20
lines changed

bip-0078.mediawiki

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ The sender should check the payjoin proposal before signing it to prevent a mali
260260
** Verify that all of sender's inputs from the original PSBT are in the proposal.
261261
* For each outputs in the proposal:
262262
** Verify that no keypaths is in the PSBT output
263-
** If the output is the [[#fee-output|fee ouptut]]:
263+
** If the output is the [[#fee-output|fee output]]:
264264
*** The amount that was substracted from the output's value is less or equal to <code>maxadditionalfeecontribution</code>. Let's call this amount <code>actual contribution</code>.
265265
*** Make sure the actual contribution is only paying fee: The <code>actual contribution</code> is less or equals to the difference of absolute fee between the payjoin proposal and the original PSBT.
266266
*** Make sure the actual contribution is only paying for fee incurred by additional inputs: <code>actual contribution</code> is less or equals to <code>originalPSBTFeeRate * vsize(sender_input_type) * (count(original_psbt_inputs) - count(payjoin_proposal_inputs))</code>. (see [[#fee-output|Fee output]] section)
@@ -274,8 +274,8 @@ The sender should check the payjoin proposal before signing it to prevent a mali
274274
The sender must be careful to only sign the inputs that were present in the original PSBT and nothing else.
275275
276276
Note:
277-
* The sender must allow the receiver to add/remove or modify his own outputs (Except is explicitely disabled via the optional parameter <code>disableoutputsubstitution=</code>)
278-
* The sender should allow the receiver to not add any input. Useful for the receiver to change the paymout output scriptPubKey type.
277+
* The sender must allow the receiver to add/remove or modify the receiver's own outputs (Except if explicitly disabled via the optional parameter <code>disableoutputsubstitution=</code>)
278+
* The sender should allow the receiver to not add any inputs. This is useful for the receiver to change the paymout output scriptPubKey type.
279279
* If no input have been added, the sender's wallet implementation should accept the payjoin proposal, but not mark the transaction as an actual payjoin in the user interface.
280280
281281
Our method of checking the fee allows the receiver and the sender to batch payments in the payjoin transaction.
@@ -413,22 +413,20 @@ public async Task<PSBT> RequestPayjoin(
413413
PSBTOutput feePSBTOutput = null;
414414
if (optionalParameters.AdditionalFeeOutputIndex != null && optionalParameters.MaxAdditionalFeeContribution != null)
415415
feePSBTOutput = signedPSBT.Outputs[optionalParameters.AdditionalFeeOutputIndex];
416+
Script paymentScriptPubKey = bip21.Address == null ? null : bip21.Address.ScriptPubKey;
416417
decimal originalFee = signedPSBT.GetFee();
417418
PSBT originalPSBT = CreateOriginalPSBT(signedPSBT);
418419
Transaction originalGlobalTx = signedPSBT.GetGlobalTransaction();
419420
TxOut feeOutput = feePSBTOutput == null ? null : originalGlobalTx.Outputs[feePSBTOutput.Index];
420-
var ourInputs = new Queue<(TxIn OriginalTxIn, PSBTInput SignedPSBTInput)>();
421+
var originalInputs = new Queue<(TxIn OriginalTxIn, PSBTInput SignedPSBTInput)>();
421422
for (int i = 0; i < originalGlobalTx.Inputs.Count; i++)
422423
{
423-
ourInputs.Enqueue((originalGlobalTx.Inputs[i], signedPSBT.Inputs[i]));
424+
originalInputs.Enqueue((originalGlobalTx.Inputs[i], signedPSBT.Inputs[i]));
424425
}
425-
var ourOutputs = new Queue<(TxOut OriginalTxOut, PSBTOutput SignedPSBTOutput)>();
426+
var originalOutputs = new Queue<(TxOut OriginalTxOut, PSBTOutput SignedPSBTOutput)>();
426427
for (int i = 0; i < originalGlobalTx.Outputs.Count; i++)
427428
{
428-
if (optionalParameters.DisableOutputSubstitution ||
429-
bip21.Address == null ||
430-
signedPSBT.Outputs[i].ScriptPubKey != bip21.Address.ScriptPubKey)
431-
ourOutputs.Enqueue((originalGlobalTx.Outputs[i], signedPSBT.Outputs[i]));
429+
originalOutputs.Enqueue((originalGlobalTx.Outputs[i], signedPSBT.Outputs[i]));
432430
}
433431
endpoint = ApplyOptionalParameters(endpoint, optionalParameters);
434432
Log("original PSBT" + originalPSBT);
@@ -460,7 +458,7 @@ public async Task<PSBT> RequestPayjoin(
460458
if (proposedPSBTInput.PartialSigs.Count != 0)
461459
throw new PayjoinSenderException("The receiver added partial signatures to an input");
462460
PSBTInput proposedTxIn = proposalGlobalTx.Inputs.FindIndexedInput(proposedPSBTInput.PrevOut).TxIn;
463-
bool isOurInput = ourInputs.Count > 0 && ourInputs.Peek().OriginalTxIn.PrevOut == proposedPSBTInput.PrevOut;
461+
bool isOurInput = originalInputs.Count > 0 && originalInputs.Peek().OriginalTxIn.PrevOut == proposedPSBTInput.PrevOut;
464462
// If it is one of our input
465463
if (isOurInput)
466464
{
@@ -496,17 +494,17 @@ public async Task<PSBT> RequestPayjoin(
496494
if (proposedPSBTInput.NonWitnessUtxo == null && proposedPSBTInput.WitnessUtxo == null)
497495
throw new PayjoinSenderException("The receiver did not specify non_witness_utxo or witness_utxo for one of their inputs");
498496
sequences.Add(proposedTxIn.Sequence);
499-
// Verify that the payjoin proposal did not introduced mixed input's type.
497+
// Verify that the payjoin proposal did not introduced mixed inputs' type.
500498
if (inputScriptType != proposedPSBTInput.GetInputScriptPubKeyType())
501499
throw new PayjoinSenderException("Mixed input type detected in the proposal");
502500
}
503501
}
504502
505503
// Verify that all of sender's inputs from the original PSBT are in the proposal.
506-
if (ourInputs.Count != 0)
504+
if (originalInputs.Count != 0)
507505
throw new PayjoinSenderException("Some of our inputs are not included in the proposal");
508506
509-
// Verify that the payjoin proposal did not introduced mixed input's sequence.
507+
// Verify that the payjoin proposal did not introduced mixed inputs' sequence.
510508
if (sequences.Count != 1)
511509
throw new PayjoinSenderException("Mixed sequence detected in the proposal");
512510
@@ -516,10 +514,10 @@ public async Task<PSBT> RequestPayjoin(
516514
// Verify that no keypaths is in the PSBT output
517515
if (proposedPSBTOutput.HDKeyPaths.Count != 0)
518516
throw new PayjoinSenderException("The receiver added keypaths to an output");
519-
bool isOurOutput = ourOutputs.Count > 0 && ourOutputs.Peek().OriginalTxOut.ScriptPubKey == proposedPSBTOutput.ScriptPubKey;
520-
if (isOurOutput)
517+
bool isOriginalOutput = originalOutputs.Count > 0 && originalOutputs.Peek().OriginalTxOut.ScriptPubKey == proposedPSBTOutput.ScriptPubKey;
518+
if (isOriginalOutput)
521519
{
522-
var output = ourOutputs.Dequeue();
520+
var originalOutput = originalOutputs.Dequeue();
523521
if (output.OriginalTxOut == feeOutput)
524522
{
525523
var actualContribution = feeOutput.Value - proposedPSBTOutput.Value;
@@ -536,9 +534,13 @@ public async Task<PSBT> RequestPayjoin(
536534
if (actualContribution > originalFeeRate * GetVirtualSize(inputScriptType) * additionalInputsCount)
537535
throw new PayjoinSenderException("The actual contribution is not only paying for additional inputs");
538536
}
537+
else if (!optionalParameters.DisableOutputSubstitution && output.OriginalTxOut.ScriptPubKey == paymentScriptPubKey)
538+
{
539+
// That's the payment output, the receiver may have changed it.
540+
}
539541
else
540542
{
541-
if (output.OriginalTxOut.Value != proposedPSBTOutput.Value)
543+
if (originalOutput.OriginalTxOut.Value != proposedPSBTOutput.Value)
542544
throw new PayjoinSenderException("The receiver changed one of our outputs");
543545
}
544546
// We fill up information we had on the signed PSBT, so we can sign it.
@@ -548,8 +550,16 @@ public async Task<PSBT> RequestPayjoin(
548550
}
549551
}
550552
// Verify that all of sender's outputs from the original PSBT are in the proposal.
551-
if (ourOutputs.Count != 0)
552-
throw new PayjoinSenderException("Some of our outputs are not included in the proposal");
553+
if (originalOutputs.Count != 0)
554+
{
555+
// The payment output may have been substituted
556+
if (optionalParameters.DisableOutputSubstitution ||
557+
originalOutputs.Count != 1 ||
558+
originalOutputs.Dequeue().OriginalTxOut.ScriptPubKey != paymentScriptPubKey)
559+
{
560+
throw new PayjoinSenderException("Some of our outputs are not included in the proposal");
561+
}
562+
}
553563
554564
// After signing this proposal, we should check if minfeerate is respected.
555565
Log("payjoin proposal filled with sender's information" + proposal);

0 commit comments

Comments
 (0)