Skip to content

Commit 81b7623

Browse files
authored
Allow attempting to set the full tx for segwit inputs (#266)
* Allow attempting to set the full tx for segwit inputs An new option to allow *attempting* to set `non_witness_utxo` for segwit inputs on PSBT create/update. * rename and fix text
1 parent 8715d3d commit 81b7623

File tree

6 files changed

+57
-9
lines changed

6 files changed

+57
-9
lines changed

NBXplorer.Client/Models/CreatePSBTRequest.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using NBitcoin;
1+
using NBitcoin;
22
using System;
33
using System.Collections.Generic;
44
using System.Text;
@@ -78,6 +78,11 @@ public class CreatePSBTRequest
7878
/// Disabling the randomization of unspecified parameters to match the network's fingerprint distribution
7979
/// </summary>
8080
public bool? DisableFingerprintRandomization { get; set; }
81+
82+
/// <summary>
83+
/// Attempt setting non_witness_utxo for all inputs even if they are segwit.
84+
/// </summary>
85+
public bool AlwaysIncludeNonWitnessUTXO { get; set; }
8186
}
8287
public class PSBTRebaseKeyRules
8388
{

NBXplorer.Client/Models/RescanRequest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using NBitcoin;
1+
using NBitcoin;
22
using System;
33
using System.Collections.Generic;
44
using System.Text;

NBXplorer.Client/Models/UpdatePSBTRequest.cs

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ public class UpdatePSBTRequest
2020
/// This transform (PubKey0, 0/0, accountFingerprint) by (PubKey0, m/49'/0'/0/0, masterFingerprint)
2121
/// </summary>
2222
public List<PSBTRebaseKeyRules> RebaseKeyPaths { get; set; }
23+
24+
/// <summary>
25+
/// Attempt setting non_witness_utxo for all inputs even if they are segwit.
26+
/// </summary>
27+
public bool AlwaysIncludeNonWitnessUTXO { get; set; }
2328
}
2429
public class UpdatePSBTResponse
2530
{

NBXplorer.Tests/UnitTest1.cs

+30
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,36 @@ private static void CanCreatePSBTCore(ServerTester tester, bool segwit)
842842
});
843843
Assert.True(psbt2.PSBT.TryGetEstimatedFeeRate(out var feeRate));
844844
Assert.Equal(new FeeRate(1.0m), feeRate);
845+
846+
if (segwit)
847+
{
848+
// some PSBT signers are incompliant with spec and require the non_witness_utxo even for segwit inputs
849+
850+
Logs.Tester.LogInformation("Let's check that if we can create or update a psbt with non_witness_utxo filled even for segwit inputs");
851+
psbt2 = tester.Client.CreatePSBT(userDerivationScheme, new CreatePSBTRequest()
852+
{
853+
Destinations =
854+
{
855+
new CreatePSBTDestination()
856+
{
857+
Destination = newAddress.Address,
858+
Amount = Money.Coins(0.0001m)
859+
}
860+
},
861+
FeePreference = new FeePreference()
862+
{
863+
FallbackFeeRate = new FeeRate(1.0m)
864+
},
865+
AlwaysIncludeNonWitnessUTXO = true
866+
});
867+
868+
//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
869+
foreach (var psbtInput in psbt2.PSBT.Inputs)
870+
{
871+
Assert.NotNull(psbtInput.NonWitnessUtxo);
872+
}
873+
}
874+
845875
}
846876

847877
[Fact]

NBXplorer/Controllers/MainController.PSBT.cs

+11-6
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,8 @@ public async Task<IActionResult> CreatePSBT(
281281
{
282282
DerivationScheme = strategy,
283283
PSBT = psbt,
284-
RebaseKeyPaths = request.RebaseKeyPaths
284+
RebaseKeyPaths = request.RebaseKeyPaths,
285+
AlwaysIncludeNonWitnessUTXO = request.AlwaysIncludeNonWitnessUTXO
285286
};
286287
await UpdatePSBTCore(update, network);
287288
var resp = new CreatePSBTResponse()
@@ -323,8 +324,12 @@ private async Task UpdatePSBTCore(UpdatePSBTRequest update, NBXplorerNetwork net
323324
await UpdateHDKeyPathsWitnessAndRedeem(update, repo);
324325
}
325326

326-
foreach (var input in update.PSBT.Inputs)
327-
input.TrySlimUTXO();
327+
if (!update.AlwaysIncludeNonWitnessUTXO)
328+
{
329+
foreach (var input in update.PSBT.Inputs)
330+
input.TrySlimUTXO();
331+
}
332+
328333

329334
HashSet<PubKey> rebased = new HashSet<PubKey>();
330335
if (update.RebaseKeyPaths != null)
@@ -433,7 +438,7 @@ private async Task UpdateUTXO(UpdatePSBTRequest update, Repository repo, Bitcoin
433438
{
434439
AnnotatedTransactionCollection txs = null;
435440
// First, we check for data in our history
436-
foreach (var input in update.PSBT.Inputs.Where(NeedUTXO))
441+
foreach (var input in update.PSBT.Inputs.Where(psbtInput => update.AlwaysIncludeNonWitnessUTXO || NeedUTXO(psbtInput)))
437442
{
438443
txs = txs ?? await GetAnnotatedTransactions(repo, ChainProvider.GetChain(repo.Network), new DerivationSchemeTrackedSource(derivationScheme));
439444
if (txs.GetByTxId(input.PrevOut.Hash) is AnnotatedTransaction tx)
@@ -453,7 +458,7 @@ private async Task UpdateUTXO(UpdatePSBTRequest update, Repository repo, Bitcoin
453458

454459
// then, we search data in the saved transactions
455460
await Task.WhenAll(update.PSBT.Inputs
456-
.Where(NeedUTXO)
461+
.Where(psbtInput => update.AlwaysIncludeNonWitnessUTXO || NeedUTXO(psbtInput))
457462
.Select(async (input) =>
458463
{
459464
// 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
472477
{
473478
var batch = rpc.RPC.PrepareBatch();
474479
var getTransactions = Task.WhenAll(update.PSBT.Inputs
475-
.Where(NeedUTXO)
480+
.Where(psbtInput => update.AlwaysIncludeNonWitnessUTXO || NeedUTXO(psbtInput))
476481
.Where(input => input.NonWitnessUtxo == null)
477482
.Select(async input =>
478483
{

docs/API.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,8 @@ Fields:
953953
"accountKeyPath": "ab5ed9ab/49'/0'/0'"
954954
}
955955
],
956-
"disableFingerprintRandomization": false
956+
"disableFingerprintRandomization": false,
957+
"alwaysIncludeNonWitnessUTXO": false
957958
}
958959
```
959960

@@ -982,6 +983,7 @@ Fields:
982983
* `rebaseKeyPaths[].accountKey`: The account key to rebase
983984
* `rebaseKeyPaths[].accountKeyPath`: The path from the root to the account key prefixed by the master public key fingerprint.
984985
* `disableFingerprintRandomization`: Disable the randomization of default parameter's value to match the network's fingerprint distribution. (randomized default values are `version`, `timeLock`, `rbf`, `discourageFeeSniping`)
986+
* `alwaysIncludeNonWitnessUTXO`: Try to set the full transaction in `non_witness_utxo`, even for segwit inputs (default to `false`)
985987

986988
Response:
987989

@@ -1026,6 +1028,7 @@ NBXplorer will take to complete as much information as it can about this PSBT.
10261028
* `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)
10271029
* `rebaseKeyPaths[].accountKey`: The account key to rebase
10281030
* `rebaseKeyPaths[].accountKeyPath`: The path from the root to the account key prefixed by the master public key fingerprint.
1031+
* `alwaysIncludeNonWitnessUTXO`: Try to set the full transaction in `non_witness_utxo`, even for segwit inputs (default to `false`)
10291032

10301033
Response:
10311034
```json

0 commit comments

Comments
 (0)