-
Notifications
You must be signed in to change notification settings - Fork 492
/
HdPubKey.cs
179 lines (137 loc) · 5.03 KB
/
HdPubKey.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using NBitcoin;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using WalletWasabi.Bases;
using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Helpers;
using WalletWasabi.JsonConverters;
using WalletWasabi.Models;
namespace WalletWasabi.Blockchain.Keys;
[JsonObject(MemberSerialization.OptIn)]
public class HdPubKey : NotifyPropertyChangedBase, IEquatable<HdPubKey>
{
public const int DefaultHighAnonymitySet = int.MaxValue;
private readonly Lazy<Script> _p2pkScript;
private readonly Lazy<Script> _p2pkhScript;
private readonly Lazy<Script> _p2wpkhScript;
private readonly Lazy<Script> _p2shOverP2wpkhScript;
private readonly Lazy<Script> _p2Taproot;
private double _anonymitySet = DefaultHighAnonymitySet;
private Cluster _cluster;
public HdPubKey(PubKey pubKey, KeyPath fullKeyPath, LabelsArray labels, KeyState keyState)
{
PubKey = Guard.NotNull(nameof(pubKey), pubKey);
FullKeyPath = Guard.NotNull(nameof(fullKeyPath), fullKeyPath);
_cluster = new Cluster(this);
Labels = labels;
Cluster.UpdateLabels();
KeyState = keyState;
_p2pkScript = new Lazy<Script>(() => PubKey.ScriptPubKey, isThreadSafe: true);
_p2pkhScript = new Lazy<Script>(() => PubKey.GetScriptPubKey(ScriptPubKeyType.Legacy), isThreadSafe: true);
_p2wpkhScript = new Lazy<Script>(() => PubKey.GetScriptPubKey(ScriptPubKeyType.Segwit), isThreadSafe: true);
_p2shOverP2wpkhScript = new Lazy<Script>(() => PubKey.GetScriptPubKey(ScriptPubKeyType.SegwitP2SH), isThreadSafe: true);
_p2Taproot = new Lazy<Script>(() => PubKey.GetScriptPubKey(ScriptPubKeyType.TaprootBIP86), isThreadSafe: true);
PubKeyHash = PubKey.Hash;
HashCode = PubKeyHash.GetHashCode();
Index = (int)FullKeyPath.Indexes[4];
int change = (int)FullKeyPath.Indexes[3];
if (change == 0)
{
IsInternal = false;
}
else if (change == 1)
{
IsInternal = true;
}
else
{
throw new ArgumentException(nameof(FullKeyPath));
}
}
public Cluster Cluster
{
get => _cluster;
set => RaiseAndSetIfChanged(ref _cluster, value);
}
public HashSet<uint256> OutputAnonSetReasons { get; } = new();
public double AnonymitySet
{
get => _anonymitySet;
private set => RaiseAndSetIfChanged(ref _anonymitySet, value);
}
public HashSet<SmartCoin> Coins { get; } = new HashSet<SmartCoin>();
[JsonProperty(Order = 1)]
[JsonConverter(typeof(PubKeyJsonConverter))]
public PubKey PubKey { get; }
[JsonProperty(Order = 2)]
[JsonConverter(typeof(KeyPathJsonConverter))]
public KeyPath FullKeyPath { get; }
[JsonProperty(Order = 3, PropertyName = "Label")]
[JsonConverter(typeof(LabelsArrayJsonConverter))]
public LabelsArray Labels { get; private set; }
[JsonProperty(Order = 4)]
public KeyState KeyState { get; private set; }
/// <summary>Height of the block where all coins associated with the key were spent, or <c>null</c> if not yet spent.</summary>
/// <remarks>Value can be non-<c>null</c> only for <see cref="IsInternal">internal keys</see> as they should be used just once.</remarks>
public Height? LatestSpendingHeight { get; set; }
public Script P2pkScript => _p2pkScript.Value;
public Script P2pkhScript => _p2pkhScript.Value;
public Script P2wpkhScript => _p2wpkhScript.Value;
public Script P2shOverP2wpkhScript => _p2shOverP2wpkhScript.Value;
public Script P2Taproot => _p2Taproot.Value;
public KeyId PubKeyHash { get; }
public int Index { get; }
public bool IsInternal { get; }
private int HashCode { get; }
public void SetAnonymitySet(double anonset, uint256? outputAnonSetReason = null)
{
if (outputAnonSetReason is not null)
{
OutputAnonSetReasons.Add(outputAnonSetReason);
}
AnonymitySet = anonset;
}
public void SetLabel(LabelsArray labels, KeyManager? kmToFile = null)
{
if (Labels == labels)
{
return;
}
Labels = labels;
Cluster.UpdateLabels();
kmToFile?.ToFile();
}
public void SetKeyState(KeyState state, KeyManager? kmToFile = null)
{
if (KeyState == state)
{
return;
}
KeyState = state;
kmToFile?.ToFile();
}
public BitcoinPubKeyAddress GetP2pkhAddress(Network network) => (BitcoinPubKeyAddress)PubKey.GetAddress(ScriptPubKeyType.Legacy, network);
public BitcoinWitPubKeyAddress GetP2wpkhAddress(Network network) => (BitcoinWitPubKeyAddress)PubKey.GetAddress(ScriptPubKeyType.Segwit, network);
public BitcoinScriptAddress GetP2shOverP2wpkhAddress(Network network) => (BitcoinScriptAddress)PubKey.GetAddress(ScriptPubKeyType.SegwitP2SH, network);
public bool ContainsScript(Script scriptPubKey)
{
var scripts = new[]
{
P2pkScript,
P2pkhScript,
P2wpkhScript,
P2shOverP2wpkhScript,
P2Taproot
};
return scripts.Contains(scriptPubKey);
}
#region Equality
public override bool Equals(object? obj) => Equals(obj as HdPubKey);
public bool Equals(HdPubKey? other) => this == other;
public override int GetHashCode() => HashCode;
public static bool operator ==(HdPubKey? x, HdPubKey? y) => x?.PubKeyHash == y?.PubKeyHash;
public static bool operator !=(HdPubKey? x, HdPubKey? y) => !(x == y);
#endregion Equality
}