Skip to content

Commit 326d9c5

Browse files
Fix: Taproot PSBT's outputs were not including internal key and taproot hd information (#495)
1 parent 8ea1467 commit 326d9c5

File tree

3 files changed

+37
-26
lines changed

3 files changed

+37
-26
lines changed

NBXplorer.Tests/UnitTest1.cs

+27-17
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,9 @@ public async Task CanCreatePSBT()
365365
Assert.NotNull(spendingPSBT.Inputs[0].WitnessUtxo);
366366
///////////////////////////
367367

368-
//CanCreatePSBTCore(tester, ScriptPubKeyType.SegwitP2SH);
369-
//CanCreatePSBTCore(tester, ScriptPubKeyType.Segwit);
370-
//CanCreatePSBTCore(tester, ScriptPubKeyType.Legacy);
368+
CanCreatePSBTCore(tester, ScriptPubKeyType.SegwitP2SH);
369+
CanCreatePSBTCore(tester, ScriptPubKeyType.Segwit);
370+
CanCreatePSBTCore(tester, ScriptPubKeyType.Legacy);
371371
CanCreatePSBTCore(tester, ScriptPubKeyType.TaprootBIP86);
372372

373373
// If we build a list of unconf transaction which is too long, the CreatePSBT should
@@ -471,6 +471,11 @@ private static void CanCreatePSBTCore(ServerTester tester, ScriptPubKeyType type
471471
Assert.Empty(input.HDKeyPaths);
472472
Assert.Single(input.HDTaprootKeyPaths);
473473
Assert.NotNull(input.TaprootInternalKey);
474+
475+
var output = Assert.Single(minimumInputs.PSBT.Outputs, o => o.ScriptPubKey == minimumInputs.ChangeAddress.ScriptPubKey);
476+
Assert.NotNull(output.TaprootInternalKey);
477+
Assert.Empty(output.HDKeyPaths);
478+
Assert.NotEmpty(output.HDTaprootKeyPaths);
474479
}
475480
Assert.Equal(2, spendAllOutpoints.PSBT.Inputs.Count);
476481
}
@@ -849,8 +854,8 @@ private static void CanCreatePSBTCore(ServerTester tester, ScriptPubKeyType type
849854
ReserveChangeAddress = false
850855
});
851856
Assert.Equal(3, psbt2.PSBT.Outputs.Count);
852-
Assert.Equal(2, psbt2.PSBT.Outputs.Where(o => o.HDKeyPaths.Any()).Count());
853-
Assert.Single(psbt2.PSBT.Outputs, o => o.HDKeyPaths.Any(h => h.Value.KeyPath == newAddress.KeyPath));
857+
858+
AssertHasOutput(type, newAddress.KeyPath, psbt2);
854859
foreach (var input in psbt2.PSBT.GetGlobalTransaction().Inputs)
855860
{
856861
Assert.Equal(Sequence.Final, input.Sequence);
@@ -914,8 +919,9 @@ private static void CanCreatePSBTCore(ServerTester tester, ScriptPubKeyType type
914919
Assert.Equal(new KeyPath("49'/0'"), globalXPub.KeyPath);
915920

916921
Assert.Equal(3, psbt2.PSBT.Outputs.Count);
917-
Assert.Equal(2, psbt2.PSBT.Outputs.Where(o => o.HDKeyPaths.Any()).Count());
918-
var selfchange = Assert.Single(psbt2.PSBT.Outputs, o => o.HDKeyPaths.Any(h => h.Key.GetAddress(type, tester.Network).ScriptPubKey == newAddress.ScriptPubKey));
922+
923+
var selfchange = AssertHasOutput(type, new KeyPath("49'/0'").Derive(newAddress.KeyPath), psbt2);
924+
919925
Assert.All(psbt2.PSBT.Inputs.Concat<PSBTCoin>(new[] { selfchange }).SelectMany(i => i.HDKeyPaths), i =>
920926
{
921927
Assert.Equal(rootHD, i.Value.MasterFingerprint);
@@ -1022,6 +1028,20 @@ private static void CanCreatePSBTCore(ServerTester tester, ScriptPubKeyType type
10221028
}
10231029
}
10241030

1031+
private static PSBTOutput AssertHasOutput(ScriptPubKeyType type, KeyPath keyPath, CreatePSBTResponse psbt2)
1032+
{
1033+
if (type == ScriptPubKeyType.TaprootBIP86)
1034+
{
1035+
Assert.Equal(2, psbt2.PSBT.Outputs.Where(o => o.HDTaprootKeyPaths.Any()).Count());
1036+
return Assert.Single(psbt2.PSBT.Outputs, o => o.HDTaprootKeyPaths.Any(h => h.Value.RootedKeyPath.KeyPath == keyPath));
1037+
}
1038+
else
1039+
{
1040+
Assert.Equal(2, psbt2.PSBT.Outputs.Where(o => o.HDKeyPaths.Any()).Count());
1041+
return Assert.Single(psbt2.PSBT.Outputs, o => o.HDKeyPaths.Any(h => h.Value.KeyPath == keyPath));
1042+
}
1043+
}
1044+
10251045
[TheoryWithTimeout]
10261046
[InlineData(true)]
10271047
[InlineData(false)]
@@ -2563,16 +2583,6 @@ public async Task CanTrackAddress()
25632583
}
25642584
}
25652585

2566-
[Fact]
2567-
public async Task Test()
2568-
{
2569-
var rpc = new RPCClient(new RPCCredentialString()
2570-
{
2571-
UserPassword = new NetworkCredential("dashrpc", "PQQgOzs1jN7q2SWQ6TpBNLm9j"),
2572-
}, "https://dash-testnet.nodes.m3t4c0.xyz", AltNetworkSets.Dash.Testnet);
2573-
var b1 = await rpc.GetBlockAsync(new uint256("000001f02c1623e0bb12b54ac505cefdfca3f0f664bf333fc73ae5eafe34b830"));
2574-
}
2575-
25762586
[FactWithTimeout]
25772587
public async Task CanTrack2()
25782588
{

NBXplorer/Controllers/MainController.PSBT.cs

+9-8
Original file line numberDiff line numberDiff line change
@@ -450,21 +450,22 @@ private async Task UpdatePSBTCore(UpdatePSBTRequest update, NBXplorerNetwork net
450450
// * HDTaprootKeyPaths is used instead of HDKeyPaths
451451
// * TaprootSighashType is explicitely set to default
452452
// * Fill up TaprootInternalKey
453-
foreach (var input in update.PSBT.Inputs)
453+
foreach (var c in update.PSBT.Inputs.OfType<PSBTCoin>().Concat(update.PSBT.Outputs))
454454
{
455-
input.TaprootSighashType = TaprootSigHash.Default;
456-
if (input.HDKeyPaths.Count != 1)
455+
if (c is PSBTInput input)
456+
input.TaprootSighashType = TaprootSigHash.Default;
457+
if (c.HDKeyPaths.Count != 1)
457458
continue;
458-
foreach (var keypath in input.HDKeyPaths)
459+
foreach (var keypath in c.HDKeyPaths)
459460
{
460461
var taprootPubKey = keypath.Key.GetTaprootFullPubKey();
461-
input.TaprootInternalKey = taprootPubKey.InternalKey;
462+
c.TaprootInternalKey = taprootPubKey.InternalKey;
462463
// Some consumers expect the internal key to be in the HDTaprootKeyPaths
463-
if (!TaprootPubKey.TryCreate(input.TaprootInternalKey.ToBytes(), out var pk))
464+
if (!TaprootPubKey.TryCreate(c.TaprootInternalKey.ToBytes(), out var pk))
464465
continue;
465-
input.HDTaprootKeyPaths.AddOrReplace(pk, new TaprootKeyPath(keypath.Value));
466+
c.HDTaprootKeyPaths.AddOrReplace(pk, new TaprootKeyPath(keypath.Value));
466467
}
467-
input.HDKeyPaths.Clear();
468+
c.HDKeyPaths.Clear();
468469
}
469470
}
470471
}

NBXplorer/NBXplorer.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<OutputType>Exe</OutputType>
44
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">net8.0</TargetFramework>
55
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">$(TargetFrameworkOverride)</TargetFramework>
6-
<Version>2.5.15</Version>
6+
<Version>2.5.16</Version>
77
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\NBXplorer.xml</DocumentationFile>
88
<NoWarn>1701;1702;1705;1591;CS1591</NoWarn>
99
<LangVersion>12</LangVersion>

0 commit comments

Comments
 (0)