Skip to content

Commit e02dc68

Browse files
committed
Refactor derivation functions to use NetworkNodeInfo and simplify parameters
1 parent d65d3de commit e02dc68

File tree

10 files changed

+86
-56
lines changed

10 files changed

+86
-56
lines changed

lib/src/crypto/network_type.dart

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ sealed class UTXONetworkType extends NetworkType {
7878

7979
typedef BIP32Prefixes = ({int private, int public});
8080

81+
class NetworkNodeInfo {
82+
final int wif;
83+
final BIP32Prefixes keyPrefixes;
84+
85+
const NetworkNodeInfo({
86+
required this.wif,
87+
required this.keyPrefixes,
88+
});
89+
}
90+
8191
class NetworkBIP {
8292
final int wif;
8393

@@ -98,19 +108,43 @@ class NetworkBIP {
98108
DOGE_NETWORK_BIP,
99109
];
100110

101-
static (BIP32Prefixes, NetworkBIP)? findPrefixesFromVersion(int version) {
111+
static NetworkNodeInfo? findPrefixesFromVersion(int version) {
102112
return defaults
103113
.map((e) {
104114
final prefixes = e.fromVersion(version);
105115
if (prefixes != null) {
106-
return (prefixes, e);
116+
return NetworkNodeInfo(
117+
wif: e.wif,
118+
keyPrefixes: prefixes,
119+
);
107120
}
108121
return null;
109122
})
110123
.nonNulls
111124
.firstOrNull;
112125
}
113126

127+
NetworkNodeInfo getForPurpose(HDWalletPurpose purpose) {
128+
switch (purpose) {
129+
case HDWalletPurpose.NO_STRUCTURE:
130+
case HDWalletPurpose.BIP44:
131+
return NetworkNodeInfo(
132+
wif: wif,
133+
keyPrefixes: bip32,
134+
);
135+
case HDWalletPurpose.BIP84:
136+
return NetworkNodeInfo(
137+
wif: wif,
138+
keyPrefixes: bip84,
139+
);
140+
case HDWalletPurpose.BIP49:
141+
return NetworkNodeInfo(
142+
wif: wif,
143+
keyPrefixes: bip49,
144+
);
145+
}
146+
}
147+
114148
const NetworkBIP({
115149
required this.bip32,
116150
required this.bip49,

lib/src/crypto/utxo/utils/derivation.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,28 @@ HDNode deriveMasterNodeFromSeed({
4242
required HDWalletPath walletPath,
4343
required UTXONetworkType networkType,
4444
}) {
45-
final parentNode = HDNode.fromSeed(seed, network: networkType.networkBIP);
45+
final masterNode = HDNode.fromSeed(
46+
seed,
47+
network: networkType.networkBIP.getForPurpose(walletPath.purpose),
48+
);
49+
4650
final derivationPath = switch (walletPath.basePath) {
4751
"m/44'/2'" => walletPath.account0Path,
4852
_ => walletPath.purpose.string,
4953
};
5054
final node =
51-
parentNode.derivePath(derivationPath); // TODO: Use base Path with Account
55+
masterNode.derivePath(derivationPath); // TODO: Use base Path with Account
5256

5357
return node;
5458
}
5559

5660
HDNode deriveMasterNodeFromExtendedKey(
5761
String ePubKey, {
58-
UTXONetworkType? networkType,
62+
NetworkNodeInfo? nodeNetworkInfo,
5963
}) {
6064
return HDNode.fromExtendedKey(
6165
ePubKey,
62-
network: networkType?.networkBIP,
66+
network: nodeNetworkInfo,
6367
);
6468
}
6569

@@ -121,8 +125,9 @@ HDNode deriveChildNodeFromPath({
121125

122126
extension on HDNode {
123127
String toBase58wkCompatibility(int parentFingerprint, int depth) {
124-
final version =
125-
(!isNeutered) ? network!.bip32.private : network!.bip32.public;
128+
final version = !isNeutered
129+
? network!.keyPrefixes.private
130+
: network!.keyPrefixes.public;
126131
Uint8List buffer = new Uint8List(78);
127132
ByteData bytes = buffer.buffer.asByteData();
128133
bytes.setUint32(0, version);

lib/src/crypto/utxo/utxo_analyzer.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,17 @@ Future<UTXOTxInfo> fetchUTXOTransactionsFromEpubKey({
204204
/// Search for Receive and Change Addresses
205205
///
206206
207+
final nodeNetworkInfo = networkType.networkBIP.getForPurpose(purpose);
208+
207209
final masterNode = await isolateManager.executeTask(
208210
IsolateTask(
209211
task: (arg) {
210212
return deriveMasterNodeFromExtendedKey(
211213
arg.$1,
212-
networkType: arg.$2,
214+
nodeNetworkInfo: arg.$2,
213215
);
214216
},
215-
argument: (ePubKey, networkType),
217+
argument: (ePubKey, nodeNetworkInfo),
216218
),
217219
);
218220

lib/src/wallet/hd_node.dart

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const UINT32_MAX = 4294967295; // 2^32 - 1
1515
/// Inspired by https://github.com/dart-bitcoin/
1616
///
1717
class HDNode {
18-
final NetworkBIP? network;
18+
final NetworkNodeInfo? network;
1919
final Uint8List _q;
2020
final Uint8List? _p;
2121
final Uint8List chainCode;
@@ -72,7 +72,7 @@ class HDNode {
7272
String? extendedPrivateKey({
7373
int? version,
7474
}) {
75-
version ??= network?.bip32.private;
75+
version ??= network?.keyPrefixes.private;
7676
if (version == null) {
7777
throw ArgumentError("Missing version");
7878
}
@@ -100,7 +100,7 @@ class HDNode {
100100
}
101101

102102
String extendedPublicKey({int? version}) {
103-
version ??= network?.bip32.public;
103+
version ??= network?.keyPrefixes.public;
104104
if (version == null) {
105105
throw ArgumentError("Missing version");
106106
}
@@ -214,7 +214,7 @@ class HDNode {
214214
required int depth,
215215
required int index,
216216
required int parentFingerprint,
217-
NetworkBIP? network,
217+
NetworkNodeInfo? network,
218218
}) {
219219
if (IL.length != 32) {
220220
throw ArgumentError("IL should be 32 bytes");
@@ -254,7 +254,7 @@ class HDNode {
254254
required int depth,
255255
required int index,
256256
required int parentFingerprint,
257-
NetworkBIP? network,
257+
NetworkNodeInfo? network,
258258
}) {
259259
if (ecurve.isPoint(publicKey) == false) {
260260
throw ArgumentError("Point is not on the curve");
@@ -284,7 +284,7 @@ class HDNode {
284284

285285
factory HDNode.fromSeed(
286286
Uint8List seed, {
287-
NetworkBIP? network,
287+
NetworkNodeInfo? network,
288288
}) {
289289
if (seed.length < 16) {
290290
throw new ArgumentError("Seed should be at least 128 bits");
@@ -311,7 +311,7 @@ class HDNode {
311311
///
312312
factory HDNode.fromExtendedKey(
313313
String key, {
314-
NetworkBIP? network,
314+
NetworkNodeInfo? network,
315315
}) {
316316
final buffer = base58CheckDecodeWithVersion(key);
317317
if (buffer.length != 78) {
@@ -322,23 +322,20 @@ class HDNode {
322322

323323
final version = bytes.getUint32(0);
324324

325-
final BIP32Prefixes? networkPrefixes;
326-
327325
/// Check if the version is valid if network is provided
328-
if (network != null) {
329-
networkPrefixes = network.fromVersion(version);
330-
if (networkPrefixes == null) {
331-
throw ArgumentError("Invalid version for given network");
332-
}
333-
} else {
326+
if (network == null) {
334327
final result = NetworkBIP.findPrefixesFromVersion(version);
335328

336329
if (result == null) {
337330
throw ArgumentError("No NetworkBIP found for $version");
338331
}
339332

340-
network = result.$2;
341-
networkPrefixes = result.$1;
333+
network = result;
334+
} else {
335+
if (version != network.keyPrefixes.private &&
336+
version != network.keyPrefixes.public) {
337+
throw ArgumentError("Invalid version for given network");
338+
}
342339
}
343340

344341
final depth = bytes.getUint8(4);
@@ -356,7 +353,7 @@ class HDNode {
356353

357354
final chainCode = buffer.sublist(13, 45);
358355

359-
final isPrivate = networkPrefixes.private == version;
356+
final isPrivate = network.keyPrefixes.private == version;
360357

361358
if (isPrivate) {
362359
if (bytes.getUint8(45) != 0) {

test/ci/bip32/bip32-dart_test.dart

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:hex/hex.dart';
55
import 'package:test/test.dart';
66
import 'package:walletkit_dart/src/crypto/network_type.dart';
77
import 'package:walletkit_dart/src/domain/constants.dart';
8+
import 'package:walletkit_dart/src/domain/entities/hd_wallet_type.dart';
89
import 'dart:io';
910
import 'dart:convert';
1011

@@ -31,19 +32,20 @@ void main() {
3132
if (ff['network'] == 'litecoin') {
3233
network = LTC_NETWORK_BIP;
3334
}
34-
var hdPrv = HDNode.fromExtendedKey(ff['base58Priv'], network: network);
35+
var hdPrv = HDNode.fromExtendedKey(ff['base58Priv']);
3536
test('works for private key -> HD wallet', () {
3637
verify(hdPrv, true, ff, network);
3738
});
3839

39-
var hdPub = HDNode.fromExtendedKey(ff['base58'], network: network);
40+
var hdPub = HDNode.fromExtendedKey(ff['base58']);
4041
test('works for public key -> HD wallet', () {
4142
verify(hdPub, false, ff, network);
4243
});
4344

4445
if (ff['seed'] != null) {
4546
var seed = HEX.decode(ff['seed']);
46-
var hdSeed = HDNode.fromSeed(seed as Uint8List, network: network);
47+
var hdSeed = HDNode.fromSeed(seed as Uint8List,
48+
network: network.getForPurpose(HDWalletPurpose.BIP44));
4749
test('works for seed -> HD wallet', () {
4850
verify(hdSeed, true, ff, network);
4951
});
@@ -59,7 +61,8 @@ void main() {
5961
network = LTC_NETWORK_BIP;
6062
HDNode? hd;
6163
try {
62-
hd = HDNode.fromExtendedKey(f['string'], network: network);
64+
hd = HDNode.fromExtendedKey(f['string'],
65+
network: network.getForPurpose(HDWalletPurpose.BIP44));
6366
} catch (err) {
6467
if (err is FormatException) {
6568
expect(err.message, f['exception']);
@@ -154,7 +157,6 @@ void main() {
154157
depth: 0,
155158
index: 0,
156159
parentFingerprint: 0,
157-
network: BITCOIN_NETWORK_BIP,
158160
);
159161
} catch (err) {
160162
expect((err as ArgumentError).message, "IL should be 32 bytes");
@@ -168,7 +170,6 @@ void main() {
168170
depth: 0,
169171
index: 0,
170172
parentFingerprint: 0,
171-
network: BITCOIN_NETWORK_BIP,
172173
);
173174
} catch (err) {
174175
expect((err as ArgumentError).message, "IL should be a private key");
@@ -203,8 +204,9 @@ void main() {
203204
(fixtures['invalid']['fromSeed'] as List<dynamic>).forEach((f) {
204205
var hd;
205206
try {
206-
hd = HDNode.fromSeed(HEX.decode(f['seed']) as Uint8List,
207-
network: BITCOIN_NETWORK_BIP);
207+
hd = HDNode.fromSeed(
208+
HEX.decode(f['seed']) as Uint8List,
209+
);
208210
} catch (err) {
209211
expect((err as ArgumentError).message, f['exception']);
210212
} finally {
@@ -219,7 +221,7 @@ void main() {
219221
String sigStr =
220222
"9636ee2fac31b795a308856b821ebe297dda7b28220fb46ea1fbbd7285977cc04c82b734956246a0f15a9698f03f546d8d96fe006c8e7bd2256ca7c8229e6f5c";
221223
Uint8List signature = HEX.decode(sigStr) as Uint8List;
222-
HDNode node = HDNode.fromSeed(seed, network: BITCOIN_NETWORK_BIP);
224+
HDNode node = HDNode.fromSeed(seed);
223225
expect(HEX.encode(node.sign(hash)), sigStr);
224226
expect(node.verify(hash, signature), true);
225227
expect(node.verify(seed, signature), false);

test/ci/derivation/derivation_path_test.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@ void main() {
4343
walletPath: bitcoinNSHDPath,
4444
seed: helloSeed,
4545
);
46-
final node_xpub_ns = deriveMasterNodeFromExtendedKey(
47-
helloXpub,
48-
networkType: networkType,
49-
);
46+
final node_xpub_ns = deriveMasterNodeFromExtendedKey(helloXpub);
5047

5148
expect(node_seed_ns.neutered().extendedPublicKey(), helloXpub);
5249

test/ci/derivation/derive_addresses_test.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ void main() {
2020

2121
final masterNode = deriveMasterNodeFromExtendedKey(
2222
ePubKey,
23-
networkType: t,
2423
);
2524
expect(
2625
deriveChildNode(
@@ -56,7 +55,6 @@ void main() {
5655
"xpub69QjKT4D1e67mdLGJDUq45wYdQZrVL7DATQ98dPPHiKwkKsrM1uyhbG4UazWemQvmi8GBV5UCpLGrFe8zUebtkw7Ew3bEKzv8bDPA2Difc8";
5756
final masterNode = deriveMasterNodeFromExtendedKey(
5857
ePubKey,
59-
networkType: t,
6058
);
6159
expect(
6260
deriveChildNode(
@@ -101,7 +99,6 @@ void main() {
10199
"xpub69QjKT4D1e67mdLGJDUq45wYdQZrVL7DATQ98dPPHiKwkKsrM1uyhbG4UazWemQvmi8GBV5UCpLGrFe8zUebtkw7Ew3bEKzv8bDPA2Difc8";
102100
final masterNode = deriveMasterNodeFromExtendedKey(
103101
ePubKey,
104-
networkType: t,
105102
);
106103
expect(
107104
deriveChildNode(
@@ -471,7 +468,6 @@ void main() {
471468

472469
final masterNode = deriveMasterNodeFromExtendedKey(
473470
wkDebugXPUB,
474-
networkType: t,
475471
);
476472
for (var i = 0; i < 10; i++) {
477473
final addresses = deriveChildNode(

test/ci/derivation/derive_extended_pubkey_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ void main() {
3030
var hdNode = deriveMasterNodeFromExtendedKey(btcPubKey);
3131

3232
expect(
33-
hdNode.network?.bip32.public,
33+
hdNode.network?.keyPrefixes.public,
3434
BitcoinNetwork.networkBIP.bip32.public,
3535
);
3636

@@ -40,7 +40,7 @@ void main() {
4040
hdNode = deriveMasterNodeFromExtendedKey(ltcPubKey);
4141

4242
expect(
43-
hdNode.network?.bip32.public,
43+
hdNode.network?.keyPrefixes.public,
4444
LitecoinNetwork.networkBIP.bip32.public,
4545
);
4646
});

0 commit comments

Comments
 (0)