diff --git a/NBXplorer.Client/Models/CreatePSBTRequest.cs b/NBXplorer.Client/Models/CreatePSBTRequest.cs
index 4ac58cc07..f6e380800 100644
--- a/NBXplorer.Client/Models/CreatePSBTRequest.cs
+++ b/NBXplorer.Client/Models/CreatePSBTRequest.cs
@@ -1,4 +1,4 @@
-using NBitcoin;
+using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
@@ -78,6 +78,11 @@ public class CreatePSBTRequest
/// Disabling the randomization of unspecified parameters to match the network's fingerprint distribution
///
public bool? DisableFingerprintRandomization { get; set; }
+
+ ///
+ /// Attempt setting non_witness_utxo for all inputs even if they are segwit.
+ ///
+ public bool AlwaysIncludeNonWitnessUTXO { get; set; }
}
public class PSBTRebaseKeyRules
{
diff --git a/NBXplorer.Client/Models/RescanRequest.cs b/NBXplorer.Client/Models/RescanRequest.cs
index 90fc61a6f..1f19d480a 100644
--- a/NBXplorer.Client/Models/RescanRequest.cs
+++ b/NBXplorer.Client/Models/RescanRequest.cs
@@ -1,4 +1,4 @@
-using NBitcoin;
+using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
diff --git a/NBXplorer.Client/Models/UpdatePSBTRequest.cs b/NBXplorer.Client/Models/UpdatePSBTRequest.cs
index c4db8ee71..a43fd422f 100644
--- a/NBXplorer.Client/Models/UpdatePSBTRequest.cs
+++ b/NBXplorer.Client/Models/UpdatePSBTRequest.cs
@@ -20,6 +20,11 @@ public class UpdatePSBTRequest
/// This transform (PubKey0, 0/0, accountFingerprint) by (PubKey0, m/49'/0'/0/0, masterFingerprint)
///
public List RebaseKeyPaths { get; set; }
+
+ ///
+ /// Attempt setting non_witness_utxo for all inputs even if they are segwit.
+ ///
+ public bool AlwaysIncludeNonWitnessUTXO { get; set; }
}
public class UpdatePSBTResponse
{
diff --git a/NBXplorer.Tests/UnitTest1.cs b/NBXplorer.Tests/UnitTest1.cs
index 3cafbdd41..951ad6628 100644
--- a/NBXplorer.Tests/UnitTest1.cs
+++ b/NBXplorer.Tests/UnitTest1.cs
@@ -842,6 +842,36 @@ private static void CanCreatePSBTCore(ServerTester tester, bool segwit)
});
Assert.True(psbt2.PSBT.TryGetEstimatedFeeRate(out var feeRate));
Assert.Equal(new FeeRate(1.0m), feeRate);
+
+ if (segwit)
+ {
+ // some PSBT signers are incompliant with spec and require the non_witness_utxo even for segwit inputs
+
+ Logs.Tester.LogInformation("Let's check that if we can create or update a psbt with non_witness_utxo filled even for segwit inputs");
+ psbt2 = tester.Client.CreatePSBT(userDerivationScheme, new CreatePSBTRequest()
+ {
+ Destinations =
+ {
+ new CreatePSBTDestination()
+ {
+ Destination = newAddress.Address,
+ Amount = Money.Coins(0.0001m)
+ }
+ },
+ FeePreference = new FeePreference()
+ {
+ FallbackFeeRate = new FeeRate(1.0m)
+ },
+ AlwaysIncludeNonWitnessUTXO = true
+ });
+
+ //in our case, we should have the tx to load this, but if someone restored the wallet and has a pruned node, this may not be set
+ foreach (var psbtInput in psbt2.PSBT.Inputs)
+ {
+ Assert.NotNull(psbtInput.NonWitnessUtxo);
+ }
+ }
+
}
[Fact]
diff --git a/NBXplorer/Controllers/MainController.PSBT.cs b/NBXplorer/Controllers/MainController.PSBT.cs
index 74a2c2e98..45a0799cc 100644
--- a/NBXplorer/Controllers/MainController.PSBT.cs
+++ b/NBXplorer/Controllers/MainController.PSBT.cs
@@ -281,7 +281,8 @@ public async Task CreatePSBT(
{
DerivationScheme = strategy,
PSBT = psbt,
- RebaseKeyPaths = request.RebaseKeyPaths
+ RebaseKeyPaths = request.RebaseKeyPaths,
+ AlwaysIncludeNonWitnessUTXO = request.AlwaysIncludeNonWitnessUTXO
};
await UpdatePSBTCore(update, network);
var resp = new CreatePSBTResponse()
@@ -323,8 +324,12 @@ private async Task UpdatePSBTCore(UpdatePSBTRequest update, NBXplorerNetwork net
await UpdateHDKeyPathsWitnessAndRedeem(update, repo);
}
- foreach (var input in update.PSBT.Inputs)
- input.TrySlimUTXO();
+ if (!update.AlwaysIncludeNonWitnessUTXO)
+ {
+ foreach (var input in update.PSBT.Inputs)
+ input.TrySlimUTXO();
+ }
+
HashSet rebased = new HashSet();
if (update.RebaseKeyPaths != null)
@@ -433,7 +438,7 @@ private async Task UpdateUTXO(UpdatePSBTRequest update, Repository repo, Bitcoin
{
AnnotatedTransactionCollection txs = null;
// First, we check for data in our history
- foreach (var input in update.PSBT.Inputs.Where(NeedUTXO))
+ foreach (var input in update.PSBT.Inputs.Where(psbtInput => update.AlwaysIncludeNonWitnessUTXO || NeedUTXO(psbtInput)))
{
txs = txs ?? await GetAnnotatedTransactions(repo, ChainProvider.GetChain(repo.Network), new DerivationSchemeTrackedSource(derivationScheme));
if (txs.GetByTxId(input.PrevOut.Hash) is AnnotatedTransaction tx)
@@ -453,7 +458,7 @@ private async Task UpdateUTXO(UpdatePSBTRequest update, Repository repo, Bitcoin
// then, we search data in the saved transactions
await Task.WhenAll(update.PSBT.Inputs
- .Where(NeedUTXO)
+ .Where(psbtInput => update.AlwaysIncludeNonWitnessUTXO || NeedUTXO(psbtInput))
.Select(async (input) =>
{
// If this is not segwit, or we are unsure of it, let's try to grab from our saved transactions
@@ -472,7 +477,7 @@ await Task.WhenAll(update.PSBT.Inputs
{
var batch = rpc.RPC.PrepareBatch();
var getTransactions = Task.WhenAll(update.PSBT.Inputs
- .Where(NeedUTXO)
+ .Where(psbtInput => update.AlwaysIncludeNonWitnessUTXO || NeedUTXO(psbtInput))
.Where(input => input.NonWitnessUtxo == null)
.Select(async input =>
{
diff --git a/docs/API.md b/docs/API.md
index 15cc57b1d..dd9d80ecf 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -953,7 +953,8 @@ Fields:
"accountKeyPath": "ab5ed9ab/49'/0'/0'"
}
],
- "disableFingerprintRandomization": false
+ "disableFingerprintRandomization": false,
+ "alwaysIncludeNonWitnessUTXO": false
}
```
@@ -982,6 +983,7 @@ Fields:
* `rebaseKeyPaths[].accountKey`: The account key to rebase
* `rebaseKeyPaths[].accountKeyPath`: The path from the root to the account key prefixed by the master public key fingerprint.
* `disableFingerprintRandomization`: Disable the randomization of default parameter's value to match the network's fingerprint distribution. (randomized default values are `version`, `timeLock`, `rbf`, `discourageFeeSniping`)
+* `alwaysIncludeNonWitnessUTXO`: Try to set the full transaction in `non_witness_utxo`, even for segwit inputs (default to `false`)
Response:
@@ -1026,6 +1028,7 @@ NBXplorer will take to complete as much information as it can about this PSBT.
* `rebaseKeyPaths`: Optional. Rebase the hdkey paths (if no rebase, the key paths are relative to the xpub that NBXplorer knows about), a rebase can transform (PubKey0, 0/0, accountFingerprint) by (PubKey0, m/49'/0'/0/0, masterFingerprint)
* `rebaseKeyPaths[].accountKey`: The account key to rebase
* `rebaseKeyPaths[].accountKeyPath`: The path from the root to the account key prefixed by the master public key fingerprint.
+* `alwaysIncludeNonWitnessUTXO`: Try to set the full transaction in `non_witness_utxo`, even for segwit inputs (default to `false`)
Response:
```json