From 161850150a5c7e19e3f45fac99bfb80cf7903654 Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 17 Apr 2020 10:46:29 +0200 Subject: [PATCH 1/5] fix p2sh detection input --- BTCPayServer/Services/PayjoinClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BTCPayServer/Services/PayjoinClient.cs b/BTCPayServer/Services/PayjoinClient.cs index 8915a6d24..3e617bdba 100644 --- a/BTCPayServer/Services/PayjoinClient.cs +++ b/BTCPayServer/Services/PayjoinClient.cs @@ -34,7 +34,7 @@ namespace BTCPayServer.Services if (i.WitnessUtxo.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)) return ScriptPubKeyType.Segwit; if (i.WitnessUtxo.ScriptPubKey.IsScriptType(ScriptType.P2SH) && - i.FinalScriptWitness.ToScript().IsScriptType(ScriptType.P2WPKH)) + i.FinalScriptWitness.GetSigner().ScriptPubKey.IsScriptType(ScriptType.P2WPKH)) return ScriptPubKeyType.SegwitP2SH; return null as ScriptPubKeyType?; } From b470ce2dad43aae27e7646fa50cd5f69cc9dadf7 Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 17 Apr 2020 10:46:43 +0200 Subject: [PATCH 2/5] fix p2sh test error codes --- BTCPayServer.Tests/PayJoinTests.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/BTCPayServer.Tests/PayJoinTests.cs b/BTCPayServer.Tests/PayJoinTests.cs index deb0b9ff3..68f0368ae 100644 --- a/BTCPayServer.Tests/PayJoinTests.cs +++ b/BTCPayServer.Tests/PayJoinTests.cs @@ -141,8 +141,14 @@ namespace BTCPayServer.Tests var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network); var clientShouldError = unsupportedFormats.Contains(senderAddressType); - var errorCode = ( unsupportedFormats.Contains( receiverAddressType) || receiverAddressType != senderAddressType)? "unsupported-inputs" : null; - + string errorCode = null; + if (unsupportedFormats.Contains(receiverAddressType)) + { + errorCode = "unsupported-inputs"; + }else if (receiverAddressType != senderAddressType) + { + errorCode = "out-of-utxos"; + } var invoice = receiverUser.BitPay.CreateInvoice(new Invoice() {Price = 50000, Currency = "sats", FullNotifications = true}); var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); From 514b6959071d14629bdb508b3f29bd54e9643822 Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 17 Apr 2020 11:44:19 +0200 Subject: [PATCH 3/5] fix coin addition for p2sh --- .../Payments/PayJoin/PayJoinEndpointController.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs index 29816498d..1cad6f693 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs @@ -22,6 +22,7 @@ using NicolasDorier.RateLimits; using Microsoft.Extensions.Logging; using NBXplorer.DerivationStrategy; using System.Diagnostics.CodeAnalysis; +using BTCPayServer.Logging; namespace BTCPayServer.Payments.PayJoin { @@ -384,8 +385,18 @@ namespace BTCPayServer.Payments.PayJoin Money ourFeeContribution = Money.Zero; // We need to adjust the fee to keep a constant fee rate var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder(); - txBuilder.AddCoins(psbt.Inputs.Select(i => i.GetCoin())); - txBuilder.AddCoins(selectedUTXOs.Select(o => o.Value.AsCoin())); + var coins = new List(); + if (sendersInputType != ScriptPubKeyType.SegwitP2SH) + { + coins.AddRange(psbt.Inputs.Select(i => i.GetCoin())); + coins.AddRange(selectedUTXOs.Select(o => o.Value.AsCoin())); + } + else + { + coins.AddRange(psbt.Inputs.Select(i =>i.GetCoin().ToScriptCoin(PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(i.FinalScriptSig).RedeemScript))); + coins.AddRange(selectedUTXOs.Select(pair => pair.Value.AsCoin(derivationSchemeSettings.AccountDerivation))); + } + txBuilder.AddCoins(coins); Money expectedFee = txBuilder.EstimateFees(newTx, originalFeeRate); Money actualFee = newTx.GetFee(txBuilder.FindSpentCoins(newTx)); Money additionalFee = expectedFee - actualFee; From 51db6175841d24b1fdabbfb3f4f6528bfd58dc60 Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 17 Apr 2020 11:55:24 +0200 Subject: [PATCH 4/5] fixes --- .../PayJoin/PayJoinEndpointController.cs | 5 ++- BTCPayServer/Services/PayjoinClient.cs | 33 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs index 1cad6f693..6efdc22d9 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs @@ -451,8 +451,11 @@ namespace BTCPayServer.Payments.PayJoin foreach (var selectedUtxo in selectedUTXOs.Select(o => o.Value)) { var signedInput = newPsbt.Inputs.FindIndexedInput(selectedUtxo.Outpoint); - signedInput.UpdateFromCoin(selectedUtxo.AsCoin()); + var coin = selectedUtxo.AsCoin(derivationSchemeSettings.AccountDerivation); + signedInput.UpdateFromCoin(coin); var privateKey = accountKey.Derive(selectedUtxo.KeyPath).PrivateKey; + //hack until UpdateFromCoin is fixed in NBitcoin when the coin is p2sh + signedInput.WitnessUtxo = coin.TxOut; signedInput.Sign(privateKey); signedInput.FinalizeInput(); newTx.Inputs[signedInput.Index].WitScript = newPsbt.Inputs[(int)signedInput.Index].FinalScriptWitness; diff --git a/BTCPayServer/Services/PayjoinClient.cs b/BTCPayServer/Services/PayjoinClient.cs index 3e617bdba..17e95dcc9 100644 --- a/BTCPayServer/Services/PayjoinClient.cs +++ b/BTCPayServer/Services/PayjoinClient.cs @@ -34,9 +34,9 @@ namespace BTCPayServer.Services if (i.WitnessUtxo.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)) return ScriptPubKeyType.Segwit; if (i.WitnessUtxo.ScriptPubKey.IsScriptType(ScriptType.P2SH) && - i.FinalScriptWitness.GetSigner().ScriptPubKey.IsScriptType(ScriptType.P2WPKH)) + PayToWitPubKeyHashTemplate.Instance.ExtractWitScriptParameters(i.FinalScriptWitness) is {}) return ScriptPubKeyType.SegwitP2SH; - return null as ScriptPubKeyType?; + return null; } } @@ -212,8 +212,33 @@ namespace BTCPayServer.Services if (sentAfter > sentBefore) { var overPaying = sentAfter - sentBefore; - if (!newPSBT.TryGetEstimatedFeeRate(out var newFeeRate) || !newPSBT.TryGetVirtualSize(out var newVirtualSize)) - throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); + + //hack until GetAllCoins is fixed in NBitcoin when the coin is p2sh (redeem needs to be loaded from RedeemScript and fallback to i.FinalScriptSig extraction) + int newVirtualSize = 0; + if (type == ScriptPubKeyType.SegwitP2SH) + { + if (!newPSBT.TryGetFee(out var fee)) + { + throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); + } + var transactionBuilder = originalTx.Network.CreateTransactionBuilder(); + transactionBuilder.AddCoins(newPSBT.Inputs.Select(i =>i.GetCoin().ToScriptCoin(i.RedeemScript??PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(i.FinalScriptSig).RedeemScript))); + try + { + newVirtualSize = transactionBuilder.EstimateSize(newPSBT.GetGlobalTransaction(), true); + } + catch + { + throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); + } + + } + else + { + if (!newPSBT.TryGetEstimatedFeeRate(out var newFeeRate) || !newPSBT.TryGetVirtualSize(out newVirtualSize)) + throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); + } + var additionalFee = newPSBT.GetFee() - originalFee; if (overPaying > additionalFee) throw new PayjoinSenderException("The payjoin receiver is sending more money to himself"); From 0077105a2db16d00fc6e9e3e2e85955604a03a22 Mon Sep 17 00:00:00 2001 From: Kukks Date: Sat, 18 Apr 2020 08:29:07 +0200 Subject: [PATCH 5/5] remove hacks --- .../BTCPayServer.Client.csproj | 2 +- .../PayJoin/PayJoinEndpointController.cs | 18 ++----------- BTCPayServer/Services/PayjoinClient.cs | 27 ++----------------- 3 files changed, 5 insertions(+), 42 deletions(-) diff --git a/BTCPayServer.Client/BTCPayServer.Client.csproj b/BTCPayServer.Client/BTCPayServer.Client.csproj index 035d01c9b..d1e7df665 100644 --- a/BTCPayServer.Client/BTCPayServer.Client.csproj +++ b/BTCPayServer.Client/BTCPayServer.Client.csproj @@ -5,7 +5,7 @@ - + diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs index 6efdc22d9..c79e15d7a 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs @@ -19,10 +19,8 @@ using NBXplorer; using NBXplorer.Models; using Newtonsoft.Json.Linq; using NicolasDorier.RateLimits; -using Microsoft.Extensions.Logging; using NBXplorer.DerivationStrategy; using System.Diagnostics.CodeAnalysis; -using BTCPayServer.Logging; namespace BTCPayServer.Payments.PayJoin { @@ -385,18 +383,8 @@ namespace BTCPayServer.Payments.PayJoin Money ourFeeContribution = Money.Zero; // We need to adjust the fee to keep a constant fee rate var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder(); - var coins = new List(); - if (sendersInputType != ScriptPubKeyType.SegwitP2SH) - { - coins.AddRange(psbt.Inputs.Select(i => i.GetCoin())); - coins.AddRange(selectedUTXOs.Select(o => o.Value.AsCoin())); - } - else - { - coins.AddRange(psbt.Inputs.Select(i =>i.GetCoin().ToScriptCoin(PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(i.FinalScriptSig).RedeemScript))); - coins.AddRange(selectedUTXOs.Select(pair => pair.Value.AsCoin(derivationSchemeSettings.AccountDerivation))); - } - txBuilder.AddCoins(coins); + txBuilder.AddCoins(psbt.Inputs.Select(i => i.GetSignableCoin())); + txBuilder.AddCoins(selectedUTXOs.Select(o => o.Value.AsCoin(derivationSchemeSettings.AccountDerivation))); Money expectedFee = txBuilder.EstimateFees(newTx, originalFeeRate); Money actualFee = newTx.GetFee(txBuilder.FindSpentCoins(newTx)); Money additionalFee = expectedFee - actualFee; @@ -454,8 +442,6 @@ namespace BTCPayServer.Payments.PayJoin var coin = selectedUtxo.AsCoin(derivationSchemeSettings.AccountDerivation); signedInput.UpdateFromCoin(coin); var privateKey = accountKey.Derive(selectedUtxo.KeyPath).PrivateKey; - //hack until UpdateFromCoin is fixed in NBitcoin when the coin is p2sh - signedInput.WitnessUtxo = coin.TxOut; signedInput.Sign(privateKey); signedInput.FinalizeInput(); newTx.Inputs[signedInput.Index].WitScript = newPsbt.Inputs[(int)signedInput.Index].FinalScriptWitness; diff --git a/BTCPayServer/Services/PayjoinClient.cs b/BTCPayServer/Services/PayjoinClient.cs index 17e95dcc9..964d91a80 100644 --- a/BTCPayServer/Services/PayjoinClient.cs +++ b/BTCPayServer/Services/PayjoinClient.cs @@ -213,31 +213,8 @@ namespace BTCPayServer.Services { var overPaying = sentAfter - sentBefore; - //hack until GetAllCoins is fixed in NBitcoin when the coin is p2sh (redeem needs to be loaded from RedeemScript and fallback to i.FinalScriptSig extraction) - int newVirtualSize = 0; - if (type == ScriptPubKeyType.SegwitP2SH) - { - if (!newPSBT.TryGetFee(out var fee)) - { - throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); - } - var transactionBuilder = originalTx.Network.CreateTransactionBuilder(); - transactionBuilder.AddCoins(newPSBT.Inputs.Select(i =>i.GetCoin().ToScriptCoin(i.RedeemScript??PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(i.FinalScriptSig).RedeemScript))); - try - { - newVirtualSize = transactionBuilder.EstimateSize(newPSBT.GetGlobalTransaction(), true); - } - catch - { - throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); - } - - } - else - { - if (!newPSBT.TryGetEstimatedFeeRate(out var newFeeRate) || !newPSBT.TryGetVirtualSize(out newVirtualSize)) - throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); - } + if (!newPSBT.TryGetEstimatedFeeRate(out var newFeeRate) || !newPSBT.TryGetVirtualSize(out var newVirtualSize)) + throw new PayjoinSenderException("The payjoin receiver did not included UTXO information to calculate fee correctly"); var additionalFee = newPSBT.GetFee() - originalFee; if (overPaying > additionalFee)