Skip to content

Commit ae5487e

Browse files
Add taproot branch information to the derivation result (#1274)
1 parent af2226b commit ae5487e

File tree

3 files changed

+41
-26
lines changed

3 files changed

+41
-26
lines changed

NBitcoin.Tests/MiniscriptTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ public void CanMassDeriveMiniscripts(AddressIntent intent)
524524
var expected = $"multi(2,{a},{b},{c})";
525525
Assert.Equal(expected, derived.ToString());
526526

527-
var derivedKey = allMiniscripts[3].DerivedKeys[keys[0]];
527+
var derivedKey = allMiniscripts[3].DerivedKeys[0];
528528
Assert.Equal(new KeyPath($"{typeIdx[0]}/{15 + 3}"), derivedKey.KeyPath);
529529
Assert.Equal(a, ((Value.PubKeyValue)derivedKey.Pubkey).PubKey);
530530
}

NBitcoin/WalletPolicies/DerivationResult.cs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace NBitcoin.WalletPolicies;
1111

1212
public class DerivationResult
1313
{
14-
internal DerivationResult(Miniscript miniscript, Dictionary<HDKeyNode, Derivation> derivations)
14+
internal DerivationResult(Miniscript miniscript, List<Derivation> derivations)
1515
{
1616
Miniscript = miniscript;
1717
DerivedKeys = derivations;
@@ -21,20 +21,19 @@ internal DerivationResult(Miniscript miniscript, Dictionary<HDKeyNode, Derivatio
2121
/// <summary>
2222
/// The derived keys. The values are either <see cref="Value.PubKeyValue"/> or a <see cref="Value.TaprootPubKeyValue"/>.
2323
/// </summary>
24-
public Dictionary<HDKeyNode, Derivation> DerivedKeys { get; }
24+
public List<Derivation> DerivedKeys { get; }
2525
}
2626

27-
public class Derivation
28-
{
29-
public Derivation(KeyPath keyPath, Value pubkey)
30-
{
31-
KeyPath = keyPath;
32-
Pubkey = pubkey;
33-
}
34-
public KeyPath KeyPath { get; }
35-
/// <summary>
36-
/// The derived key. This is either a <see cref="Value.PubKeyValue"/> or a <see cref="Value.TaprootPubKeyValue"/>.
37-
/// </summary>
38-
public Value Pubkey { get; }
39-
}
27+
/// <summary>
28+
/// Represents a derivation that has been made from a <see cref="HDKeyNode"/>.
29+
/// </summary>
30+
/// <param name="KeyPath">The derived key path.</param>
31+
/// <param name="Pubkey">The derived public key (either <see cref="Value.PubKeyValue"/> or <see cref="Value.TaprootPubKeyValue"/>).</param>
32+
/// <param name="TaprootBranch">The taproot branch in which this has been derived, if any.</param>
33+
/// <param name="Source">The source node that generated this key.</param>
34+
public record Derivation(
35+
KeyPath KeyPath,
36+
Value Pubkey,
37+
TaprootBranchNode? TaprootBranch,
38+
HDKeyNode Source);
4039
#endif

NBitcoin/WalletPolicies/Visitors/DeriveVisitor.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,22 @@ public DerivationResult[] Derive(MiniscriptNode node, Network network)
3232
}
3333

3434
internal static readonly byte[] BIP0328CC = Encoders.Hex.DecodeData("868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965");
35+
Stack<TaprootBranchNode> _TaprootBranches = new();
36+
TaprootBranchNode? TaprootBranch => _TaprootBranches.Count > 0 ? _TaprootBranches.Peek() : null;
3537
public override MiniscriptNode Visit(MiniscriptNode node)
3638
{
39+
if (node is TaprootBranchNode b)
40+
{
41+
_TaprootBranches.Push(b);
42+
try
43+
{
44+
return base.Visit(b);
45+
}
46+
finally
47+
{
48+
_TaprootBranches.Pop();
49+
}
50+
}
3751
if (node is MultipathNode { Target: MusigNode })
3852
{
3953
var wasNestedMusig = _nestedMusig;
@@ -53,10 +67,10 @@ public override MiniscriptNode Visit(MiniscriptNode node)
5367
}
5468
if (node is MiniscriptNode.MultipathNode mki && mki.CanDerive(Intent))
5569
{
56-
if (mki.Target is HDKeyNode { Key: var pk } xpub)
70+
if (mki.Target is HDKeyNode xpub)
5771
{
58-
var value = GetPublicKey(mki, pk);
59-
_Derivations.TryAdd(xpub, value);
72+
var value = GetPublicKey(mki, xpub.Key, xpub);
73+
_Derivations.Add(new(value.KeyPath, value.Pubkey, TaprootBranch, xpub));
6074
node = value.Pubkey;
6175
}
6276
else if (mki.Target is MusigNode musig)
@@ -69,19 +83,21 @@ public override MiniscriptNode Visit(MiniscriptNode node)
6983
return node;
7084
}
7185

72-
private Derivation GetPublicKey(MiniscriptNode.MultipathNode mki, IHDKey k)
86+
private (KeyPath KeyPath, Value Pubkey) GetPublicKey(MiniscriptNode.MultipathNode mki, IHDKey k, HDKeyNode? source = null)
7387
{
7488
var type = mki.GetTypeIndex(Intent);
7589
k = DeriveIntent(k, type);
7690
k = k.Derive((uint)idx);
7791
var keyType = _nestedMusig ? KeyType.Classic : KeyType;
78-
return new Derivation(new KeyPath([(uint)type, (uint)idx]), keyType switch
79-
{
80-
KeyType.Taproot => MiniscriptNode.Create(k.GetPublicKey().TaprootPubKey),
81-
_ => MiniscriptNode.Create(k.GetPublicKey())
82-
});
92+
return (
93+
new KeyPath([(uint)type, (uint)idx]),
94+
keyType switch
95+
{
96+
KeyType.Taproot => MiniscriptNode.Create(k.GetPublicKey().TaprootPubKey),
97+
_ => MiniscriptNode.Create(k.GetPublicKey())
98+
});
8399
}
84-
Dictionary<HDKeyNode, Derivation> _Derivations = new();
100+
List<Derivation> _Derivations = new();
85101

86102
public bool _nestedMusig = false;
87103

0 commit comments

Comments
 (0)