Skip to content

Commit bd97400

Browse files
committed
Introduce pjos=0
1 parent e2778ba commit bd97400

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

bip-0078.mediawiki

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Other than that, our proposal is very similar.
6464

6565
In a payjoin payment, the following steps happen:
6666

67-
* The receiver of the payment, presents a [[bip-021.mediawiki|BIP 21 URI]] to the sender with a parameter <code>pj</code> describing a payjoin endpoint.
67+
* The receiver of the payment, presents a [[bip-021.mediawiki|BIP 21 URI]] to the sender with a parameter <code>pj=</code> describing a payjoin endpoint.
6868
* The sender creates a signed, finalized PSBT with witness UTXO or previous transactions of the inputs. We call this PSBT the <code>original</code>.
6969
* The receiver replies back with a signed PSBT containing his own signed inputs/outputs and those of the sender. We call this PSBT <code>Payjoin proposal</code>.
7070
* The sender verifies the proposal, re-signs his inputs and broadcasts the transaction to the Bitcoin network. We call this transaction <code>Payjoin transaction</code>.
@@ -118,6 +118,12 @@ The payjoin proposal MAY:
118118
The payjoin proposal MUST NOT:
119119
* Shuffle the order of inputs or outputs, the additional outputs or additional inputs must be inserted at a random index.
120120
121+
===BIP21 payjoin parameters===
122+
123+
This proposal is defining the following new [[bip-021.mediawiki|BIP 21 URI]] parameters:
124+
* <code>pj=</code>: Represents an http(s) endpoint which the sender can POST the original PSBT.
125+
* <code>pjos=0</code>: Signal to the sender that they MUST disallow [[#output-substitution|payment output substitution]]. (See [[#unsecured-payjoin|Unsecured payjoin server]])
126+
121127
===<span id="optional-params"></span>Optional parameters===
122128

123129
When the payjoin sender posts the original PSBT to the receiver, he can optionally specify the following HTTP query string parameters:
@@ -242,7 +248,8 @@ The receiver needs to do some check on the original PSBT before proceeding:
242248
===Sender's payjoin proposal checklist===
243249

244250
The sender should check the payjoin proposal before signing it to prevent a malicious receiver from stealing money.
245-
251+
252+
* If the receiver's BIP21 signalled <code>pjos=0</code>, disable payment output substitution.
246253
* Verify that the transaction version, and the nLockTime are unchanged.
247254
* Check that the sender's inputs' sequence numbers are unchanged.
248255
* For each inputs in the proposal:
@@ -264,7 +271,7 @@ The sender should check the payjoin proposal before signing it to prevent a mali
264271
*** 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>.
265272
*** 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.
266273
*** 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)
267-
** If the output is the payment output and <code>disableoutputsubstitution=</code> is <code>false</code or not specified
274+
** If the output is the payment output and payment output substitution is disabled.
268275
*** Do not make any check
269276
** Else
270277
*** Make sure the output's value did not changed.
@@ -274,7 +281,7 @@ The sender should check the payjoin proposal before signing it to prevent a mali
274281
The sender must be careful to only sign the inputs that were present in the original PSBT and nothing else.
275282

276283
Note:
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>)
284+
* The sender must allow the receiver to add/remove or modify the receiver's own outputs (if [[#output-substitution|payment output substitution]], the payment's output should not be modified)
278285
* The sender should allow the receiver to not add any inputs. This is useful for the receiver to change the paymout output scriptPubKey type.
279286
* 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.
280287
@@ -335,6 +342,13 @@ On top of this the receiver can poison analysis by randomly faking a round amoun
335342
The receiver is free to change the output paying to himself.
336343
For example, if the sender's scriptPubKey type is P2WPKH while the receiver's payment output in the original PSBT is P2SH, then the receiver can substitute the payment output to be P2WPKH to match the sender's scriptPubKey type.
337344

345+
===<span id="unsecured-payjoin"></span>Unsecured payjoin server===
346+
347+
A receiver might run the payment server (generating the BIP21 invoice) on a different server than the payjoin server, which could be less trusted than the payment server.
348+
349+
In such case, the payment server can signal to the sender, via the BIP21 parameter <code>pjos=0</code>, that they MUST disallow [[#output-substitution|payment output substitution]].
350+
A compromised payjoin server could still the hot wallet outputs of the receiver, but would not be able to re-route payment to himself.
351+
338352
===Impacted heuristics===
339353

340354
Our proposal of payjoin is breaking the following blockchain heuristics:
@@ -382,7 +396,7 @@ The sender's software wallet can verify that the payjoin proposal is legitimate
382396
However, a hardware wallet can't verify that this is indeed the case. This means that the security guarantee of the hardware wallet is decreased. If the sender's software is compromised, the hardware wallet would sign two valid transactions, thus sending two payments.
383397

384398
Without payjoin, the maximum amount of money that could be lost by a compromised software is equal to one payment (via [[#output-substitution|payment output substitution]]).
385-
Note that the sender can opt out payment output substitution my using the optional parameter <code>disableoutputsubstitution=true</code>.
399+
Note that the sender can disallow [[#output-substitution|payment output substitution]] by using the optional parameter <code>disableoutputsubstitution=true</code>.
386400

387401
With payjoin, the maximum amount of money that can be lost is equal to two payments.
388402

@@ -412,6 +426,11 @@ public async Task<PSBT> RequestPayjoin(
412426
throw new InvalidOperationException("The original PSBT should not be finalized.");
413427
ScriptPubKeyType inputScriptType = wallet.ScriptPubKeyType();
414428
PSBTOutput feePSBTOutput = null;
429+
430+
bool allowOutputSubstitution = !optionalParameters.DisableOutputSubstitution;
431+
if (bip21.Parameters.Contains("pjos") && bip21.Parameters["pjos"] == "0")
432+
allowOutputSubstitution = false;
433+
415434
if (optionalParameters.AdditionalFeeOutputIndex != null && optionalParameters.MaxAdditionalFeeContribution != null)
416435
feePSBTOutput = signedPSBT.Outputs[optionalParameters.AdditionalFeeOutputIndex];
417436
Script paymentScriptPubKey = bip21.Address == null ? null : bip21.Address.ScriptPubKey;
@@ -536,7 +555,7 @@ public async Task<PSBT> RequestPayjoin(
536555
if (actualContribution > originalFeeRate * GetVirtualSize(inputScriptType) * additionalInputsCount)
537556
throw new PayjoinSenderException("The actual contribution is not only paying for additional inputs");
538557
}
539-
else if (!optionalParameters.DisableOutputSubstitution && output.OriginalTxOut.ScriptPubKey == paymentScriptPubKey)
558+
else if (allowOutputSubstitution && output.OriginalTxOut.ScriptPubKey == paymentScriptPubKey)
540559
{
541560
// That's the payment output, the receiver may have changed it.
542561
}
@@ -555,7 +574,7 @@ public async Task<PSBT> RequestPayjoin(
555574
if (originalOutputs.Count != 0)
556575
{
557576
// The payment output may have been substituted
558-
if (optionalParameters.DisableOutputSubstitution ||
577+
if (!allowOutputSubstitution ||
559578
originalOutputs.Count != 1 ||
560579
originalOutputs.Dequeue().OriginalTxOut.ScriptPubKey != paymentScriptPubKey)
561580
{

0 commit comments

Comments
 (0)