Skip to content

Commit

Permalink
Allow attempting to set the full tx for segwit inputs (#266)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Kukks authored Jun 10, 2020
1 parent 8715d3d commit 81b7623
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 9 deletions.
7 changes: 6 additions & 1 deletion NBXplorer.Client/Models/CreatePSBTRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using NBitcoin;
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
Expand Down Expand Up @@ -78,6 +78,11 @@ public class CreatePSBTRequest
/// Disabling the randomization of unspecified parameters to match the network's fingerprint distribution
/// </summary>
public bool? DisableFingerprintRandomization { get; set; }

/// <summary>
/// Attempt setting non_witness_utxo for all inputs even if they are segwit.
/// </summary>
public bool AlwaysIncludeNonWitnessUTXO { get; set; }
}
public class PSBTRebaseKeyRules
{
Expand Down
2 changes: 1 addition & 1 deletion NBXplorer.Client/Models/RescanRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using NBitcoin;
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
Expand Down
5 changes: 5 additions & 0 deletions NBXplorer.Client/Models/UpdatePSBTRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class UpdatePSBTRequest
/// This transform (PubKey0, 0/0, accountFingerprint) by (PubKey0, m/49'/0'/0/0, masterFingerprint)
/// </summary>
public List<PSBTRebaseKeyRules> RebaseKeyPaths { get; set; }

/// <summary>
/// Attempt setting non_witness_utxo for all inputs even if they are segwit.
/// </summary>
public bool AlwaysIncludeNonWitnessUTXO { get; set; }
}
public class UpdatePSBTResponse
{
Expand Down
30 changes: 30 additions & 0 deletions NBXplorer.Tests/UnitTest1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
17 changes: 11 additions & 6 deletions NBXplorer/Controllers/MainController.PSBT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ public async Task<IActionResult> CreatePSBT(
{
DerivationScheme = strategy,
PSBT = psbt,
RebaseKeyPaths = request.RebaseKeyPaths
RebaseKeyPaths = request.RebaseKeyPaths,
AlwaysIncludeNonWitnessUTXO = request.AlwaysIncludeNonWitnessUTXO
};
await UpdatePSBTCore(update, network);
var resp = new CreatePSBTResponse()
Expand Down Expand Up @@ -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<PubKey> rebased = new HashSet<PubKey>();
if (update.RebaseKeyPaths != null)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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 =>
{
Expand Down
5 changes: 4 additions & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,8 @@ Fields:
"accountKeyPath": "ab5ed9ab/49'/0'/0'"
}
],
"disableFingerprintRandomization": false
"disableFingerprintRandomization": false,
"alwaysIncludeNonWitnessUTXO": false
}
```

Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 81b7623

Please sign in to comment.