Merge pull request #1609 from NicolasDorier/pj3

Adapt PJ implementation to latest BIP
This commit is contained in:
Nicolas Dorier 2020-05-29 08:58:38 +09:00 committed by GitHub
commit 47baa219fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 14 additions and 24 deletions

View file

@ -127,6 +127,10 @@ namespace BTCPayServer.Payments.PayJoin
decimal minfeerate = -1.0m, decimal minfeerate = -1.0m,
int v = 1) int v = 1)
{ {
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
if (network == null)
return NotFound();
if (v != 1) if (v != 1)
{ {
return BadRequest(new JObject return BadRequest(new JObject
@ -137,11 +141,6 @@ namespace BTCPayServer.Payments.PayJoin
}); });
} }
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
if (network == null)
{
return BadRequest(CreatePayjoinError("invalid-network", "Incorrect network"));
}
await using var ctx = new PayjoinReceiverContext(_invoiceRepository, _explorerClientProvider.GetExplorerClient(network), _payJoinRepository); await using var ctx = new PayjoinReceiverContext(_invoiceRepository, _explorerClientProvider.GetExplorerClient(network), _payJoinRepository);
ObjectResult CreatePayjoinErrorAndLog(int httpCode, PayjoinReceiverWellknownErrors err, string debug) ObjectResult CreatePayjoinErrorAndLog(int httpCode, PayjoinReceiverWellknownErrors err, string debug)
{ {
@ -174,7 +173,7 @@ namespace BTCPayServer.Payments.PayJoin
if (PSBT.TryParse(rawBody, network.NBitcoinNetwork, out var psbt)) if (PSBT.TryParse(rawBody, network.NBitcoinNetwork, out var psbt))
{ {
if (!psbt.IsAllFinalized()) if (!psbt.IsAllFinalized())
return BadRequest(CreatePayjoinError("psbt-not-finalized", "The PSBT should be finalized")); return BadRequest(CreatePayjoinError("original-psbt-rejected", "The PSBT should be finalized"));
ctx.OriginalTransaction = psbt.ExtractTransaction(); ctx.OriginalTransaction = psbt.ExtractTransaction();
} }
// BTCPay Server implementation support a transaction instead of PSBT // BTCPay Server implementation support a transaction instead of PSBT
@ -182,7 +181,7 @@ namespace BTCPayServer.Payments.PayJoin
{ {
psbtFormat = false; psbtFormat = false;
if (!Transaction.TryParse(rawBody, network.NBitcoinNetwork, out var tx)) if (!Transaction.TryParse(rawBody, network.NBitcoinNetwork, out var tx))
return BadRequest(CreatePayjoinError("invalid-format", "invalid transaction or psbt")); return BadRequest(CreatePayjoinError("original-psbt-rejected", "invalid transaction or psbt"));
ctx.OriginalTransaction = tx; ctx.OriginalTransaction = tx;
psbt = PSBT.FromTransaction(tx, network.NBitcoinNetwork); psbt = PSBT.FromTransaction(tx, network.NBitcoinNetwork);
psbt = (await explorer.UpdatePSBTAsync(new UpdatePSBTRequest() { PSBT = psbt })).PSBT; psbt = (await explorer.UpdatePSBTAsync(new UpdatePSBTRequest() { PSBT = psbt })).PSBT;
@ -200,11 +199,11 @@ namespace BTCPayServer.Payments.PayJoin
var sendersInputType = psbt.GetInputsScriptPubKeyType(); var sendersInputType = psbt.GetInputsScriptPubKeyType();
if (psbt.CheckSanity() is var errors && errors.Count != 0) if (psbt.CheckSanity() is var errors && errors.Count != 0)
{ {
return BadRequest(CreatePayjoinError("insane-psbt", $"This PSBT is insane ({errors[0]})")); return BadRequest(CreatePayjoinError("original-psbt-rejected", $"This PSBT is insane ({errors[0]})"));
} }
if (!psbt.TryGetEstimatedFeeRate(out originalFeeRate)) if (!psbt.TryGetEstimatedFeeRate(out originalFeeRate))
{ {
return BadRequest(CreatePayjoinError("need-utxo-information", return BadRequest(CreatePayjoinError("original-psbt-rejected",
"You need to provide Witness UTXO information to the PSBT.")); "You need to provide Witness UTXO information to the PSBT."));
} }
@ -212,19 +211,19 @@ namespace BTCPayServer.Payments.PayJoin
// to leak global xpubs // to leak global xpubs
if (psbt.GlobalXPubs.Any()) if (psbt.GlobalXPubs.Any())
{ {
return BadRequest(CreatePayjoinError("leaking-data", return BadRequest(CreatePayjoinError("original-psbt-rejected",
"GlobalXPubs should not be included in the PSBT")); "GlobalXPubs should not be included in the PSBT"));
} }
if (psbt.Outputs.Any(o => o.HDKeyPaths.Count != 0) || psbt.Inputs.Any(o => o.HDKeyPaths.Count != 0)) if (psbt.Outputs.Any(o => o.HDKeyPaths.Count != 0) || psbt.Inputs.Any(o => o.HDKeyPaths.Count != 0))
{ {
return BadRequest(CreatePayjoinError("leaking-data", return BadRequest(CreatePayjoinError("original-psbt-rejected",
"Keypath information should not be included in the PSBT")); "Keypath information should not be included in the PSBT"));
} }
if (psbt.Inputs.Any(o => !o.IsFinalized())) if (psbt.Inputs.Any(o => !o.IsFinalized()))
{ {
return BadRequest(CreatePayjoinError("psbt-not-finalized", "The PSBT Should be finalized")); return BadRequest(CreatePayjoinError("original-psbt-rejected", "The PSBT Should be finalized"));
} }
//////////// ////////////
@ -232,7 +231,7 @@ namespace BTCPayServer.Payments.PayJoin
if (!mempool.Success) if (!mempool.Success)
{ {
ctx.DoNotBroadcast(); ctx.DoNotBroadcast();
return BadRequest(CreatePayjoinError("invalid-transaction", return BadRequest(CreatePayjoinError("original-psbt-rejected",
$"Provided transaction isn't mempool eligible {mempool.RPCCodeMessage}")); $"Provided transaction isn't mempool eligible {mempool.RPCCodeMessage}"));
} }
var enforcedLowR = ctx.OriginalTransaction.Inputs.All(IsLowR); var enforcedLowR = ctx.OriginalTransaction.Inputs.All(IsLowR);

View file

@ -293,28 +293,19 @@ namespace BTCPayServer.Services
public enum PayjoinReceiverWellknownErrors public enum PayjoinReceiverWellknownErrors
{ {
LeakingData,
PSBTNotFinalized,
Unavailable, Unavailable,
OutOfUTXOS,
NotEnoughMoney, NotEnoughMoney,
InsanePSBT,
VersionUnsupported, VersionUnsupported,
NeedUTXOInformation, OriginalPSBTRejected
InvalidTransaction
} }
public class PayjoinReceiverHelper public class PayjoinReceiverHelper
{ {
static IEnumerable<(PayjoinReceiverWellknownErrors EnumValue, string ErrorCode, string Message)> Get() static IEnumerable<(PayjoinReceiverWellknownErrors EnumValue, string ErrorCode, string Message)> Get()
{ {
yield return (PayjoinReceiverWellknownErrors.LeakingData, "leaking-data", "Key path information or GlobalXPubs should not be included in the original PSBT.");
yield return (PayjoinReceiverWellknownErrors.PSBTNotFinalized, "psbt-not-finalized", "The original PSBT must be finalized.");
yield return (PayjoinReceiverWellknownErrors.Unavailable, "unavailable", "The payjoin endpoint is not available for now."); yield return (PayjoinReceiverWellknownErrors.Unavailable, "unavailable", "The payjoin endpoint is not available for now.");
yield return (PayjoinReceiverWellknownErrors.NotEnoughMoney, "not-enough-money", "The receiver added some inputs but could not bump the fee of the payjoin proposal."); yield return (PayjoinReceiverWellknownErrors.NotEnoughMoney, "not-enough-money", "The receiver added some inputs but could not bump the fee of the payjoin proposal.");
yield return (PayjoinReceiverWellknownErrors.InsanePSBT, "insane-psbt", "Some consistency check on the PSBT failed.");
yield return (PayjoinReceiverWellknownErrors.VersionUnsupported, "version-unsupported", "This version of payjoin is not supported."); yield return (PayjoinReceiverWellknownErrors.VersionUnsupported, "version-unsupported", "This version of payjoin is not supported.");
yield return (PayjoinReceiverWellknownErrors.NeedUTXOInformation, "need-utxo-information", "The witness UTXO or non witness UTXO is missing."); yield return (PayjoinReceiverWellknownErrors.OriginalPSBTRejected, "original-psbt-rejected", "The receiver rejected the original PSBT.");
yield return (PayjoinReceiverWellknownErrors.InvalidTransaction, "invalid-transaction", "The original transaction is invalid for payjoin");
} }
public static string GetErrorCode(PayjoinReceiverWellknownErrors err) public static string GetErrorCode(PayjoinReceiverWellknownErrors err)
{ {