Skip to content

Commit a3e131d

Browse files
committed
feat: all address derivations
1 parent 4a4250a commit a3e131d

18 files changed

+330
-157
lines changed

cw_bitcoin/lib/bitcoin_address_record.dart

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:convert';
22

33
import 'package:bitcoin_base/bitcoin_base.dart';
4+
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
45

56
abstract class BaseBitcoinAddressRecord {
67
BaseBitcoinAddressRecord(
@@ -63,11 +64,13 @@ abstract class BaseBitcoinAddressRecord {
6364

6465
class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
6566
final BitcoinDerivationInfo derivationInfo;
67+
final CWBitcoinDerivationType derivationType;
6668

6769
BitcoinAddressRecord(
6870
super.address, {
6971
required super.index,
7072
required this.derivationInfo,
73+
required this.derivationType,
7174
super.isHidden,
7275
super.isChange = false,
7376
super.txCount = 0,
@@ -94,6 +97,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
9497
derivationInfo: BitcoinDerivationInfo.fromJSON(
9598
decoded['derivationInfo'] as Map<String, dynamic>,
9699
),
100+
derivationType: CWBitcoinDerivationType.values[decoded['derivationType'] as int],
97101
isHidden: decoded['isHidden'] as bool? ?? false,
98102
isChange: decoded['isChange'] as bool? ?? false,
99103
isUsed: decoded['isUsed'] as bool? ?? false,
@@ -115,6 +119,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
115119
'address': address,
116120
'index': index,
117121
'derivationInfo': derivationInfo.toJSON(),
122+
'derivationType': derivationType.index,
118123
'isHidden': isHidden,
119124
'isChange': isChange,
120125
'isUsed': isUsed,

cw_bitcoin/lib/bitcoin_wallet.dart

+87-18
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ import 'dart:async';
22
import 'dart:convert';
33
import 'dart:isolate';
44

5-
import 'package:bip39/bip39.dart' as bip39;
65
import 'package:bitcoin_base/bitcoin_base.dart';
76
import 'package:blockchain_utils/blockchain_utils.dart';
87
import 'package:cw_bitcoin/bitcoin_address_record.dart';
9-
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
108
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
119
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
1210
import 'package:cw_bitcoin/bitcoin_unspent.dart';
1311
import 'package:cw_bitcoin/electrum_transaction_info.dart';
14-
// import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
12+
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
1513
import 'package:cw_core/encryption_file_utils.dart';
1614
import 'package:cw_bitcoin/electrum_derivations.dart';
1715
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
@@ -60,6 +58,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
6058
int initialSilentAddressIndex = 0,
6159
bool? alwaysScan,
6260
required bool mempoolAPIEnabled,
61+
super.hdWallets,
6362
}) : super(
6463
mnemonic: mnemonic,
6564
passphrase: passphrase,
@@ -88,9 +87,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
8887
initialChangeAddressIndex: initialChangeAddressIndex,
8988
initialSilentAddresses: initialSilentAddresses,
9089
initialSilentAddressIndex: initialSilentAddressIndex,
91-
bip32: bip32,
9290
network: networkParam ?? network,
9391
isHardwareWallet: walletInfo.isHardwareWallet,
92+
hdWallets: hdWallets,
9493
);
9594

9695
autorun((_) {
@@ -116,15 +115,49 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
116115
required bool mempoolAPIEnabled,
117116
}) async {
118117
late List<int> seedBytes;
118+
final Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets = {};
119119

120-
switch (walletInfo.derivationInfo?.derivationType) {
121-
case DerivationType.bip39:
120+
for (final derivation in walletInfo.derivations ?? <DerivationInfo>[]) {
121+
if (derivation.derivationType == DerivationType.bip39) {
122122
seedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
123+
hdWallets[CWBitcoinDerivationType.bip39] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
124+
hdWallets[CWBitcoinDerivationType.old] = hdWallets[CWBitcoinDerivationType.bip39]!;
125+
126+
try {
127+
hdWallets[CWBitcoinDerivationType.old] = Bip32Slip10Secp256k1.fromSeed(
128+
seedBytes,
129+
ElectrumWalletBase.getKeyNetVersion(network ?? BitcoinNetwork.mainnet),
130+
).derivePath(
131+
_hardenedDerivationPath(derivation.derivationPath ?? electrum_path),
132+
) as Bip32Slip10Secp256k1;
133+
} catch (e) {
134+
print("bip39 seed error: $e");
135+
}
123136
break;
124-
case DerivationType.electrum:
125-
default:
126-
seedBytes = ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase);
137+
} else {
138+
try {
139+
seedBytes = ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase);
140+
hdWallets[CWBitcoinDerivationType.electrum] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
141+
} catch (e) {
142+
print("electrum_v2 seed error: $e");
143+
144+
try {
145+
seedBytes = ElectrumV1SeedGenerator(mnemonic).generate();
146+
hdWallets[CWBitcoinDerivationType.electrum] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
147+
} catch (e) {
148+
print("electrum_v1 seed error: $e");
149+
}
150+
}
151+
152+
try {
153+
hdWallets[CWBitcoinDerivationType.old] = Bip32Slip10Secp256k1.fromSeed(
154+
seedBytes,
155+
).derivePath(
156+
_hardenedDerivationPath(derivation.derivationPath ?? electrum_path),
157+
) as Bip32Slip10Secp256k1;
158+
} catch (_) {}
127159
break;
160+
}
128161
}
129162

130163
return BitcoinWallet(
@@ -144,6 +177,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
144177
addressPageType: addressPageType,
145178
networkParam: network,
146179
mempoolAPIEnabled: mempoolAPIEnabled,
180+
hdWallets: hdWallets,
147181
);
148182
}
149183

@@ -200,21 +234,52 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
200234
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
201235

202236
List<int>? seedBytes = null;
237+
final Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets = {};
203238
final mnemonic = keysData.mnemonic;
204239
final passphrase = keysData.passphrase;
205240

206241
if (mnemonic != null) {
207-
switch (walletInfo.derivationInfo!.derivationType) {
208-
case DerivationType.electrum:
209-
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
242+
for (final derivation in walletInfo.derivations ?? <DerivationInfo>[]) {
243+
if (derivation.derivationType == DerivationType.bip39) {
244+
seedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
245+
hdWallets[CWBitcoinDerivationType.bip39] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
246+
hdWallets[CWBitcoinDerivationType.old] = hdWallets[CWBitcoinDerivationType.bip39]!;
247+
248+
try {
249+
hdWallets[CWBitcoinDerivationType.old] = Bip32Slip10Secp256k1.fromSeed(
250+
seedBytes,
251+
ElectrumWalletBase.getKeyNetVersion(network),
252+
).derivePath(
253+
_hardenedDerivationPath(derivation.derivationPath ?? electrum_path),
254+
) as Bip32Slip10Secp256k1;
255+
} catch (e) {
256+
print("bip39 seed error: $e");
257+
}
210258
break;
211-
case DerivationType.bip39:
212-
default:
213-
seedBytes = await bip39.mnemonicToSeed(
214-
mnemonic,
215-
passphrase: passphrase ?? '',
216-
);
259+
} else {
260+
try {
261+
seedBytes = ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase);
262+
hdWallets[CWBitcoinDerivationType.electrum] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
263+
} catch (e) {
264+
print("electrum_v2 seed error: $e");
265+
266+
try {
267+
seedBytes = ElectrumV1SeedGenerator(mnemonic).generate();
268+
hdWallets[CWBitcoinDerivationType.electrum] =
269+
Bip32Slip10Secp256k1.fromSeed(seedBytes);
270+
} catch (e) {
271+
print("electrum_v1 seed error: $e");
272+
}
273+
}
274+
275+
try {
276+
hdWallets[CWBitcoinDerivationType.old] =
277+
Bip32Slip10Secp256k1.fromSeed(seedBytes!).derivePath(
278+
_hardenedDerivationPath(derivation.derivationPath ?? electrum_path),
279+
) as Bip32Slip10Secp256k1;
280+
} catch (_) {}
217281
break;
282+
}
218283
}
219284
}
220285

@@ -237,6 +302,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
237302
networkParam: network,
238303
alwaysScan: alwaysScan,
239304
mempoolAPIEnabled: mempoolAPIEnabled,
305+
hdWallets: hdWallets,
240306
);
241307
}
242308

@@ -784,6 +850,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
784850
super.syncStatusReaction(syncStatus);
785851
}
786852
}
853+
854+
static String _hardenedDerivationPath(String derivationPath) =>
855+
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
787856
}
788857

789858
Future<void> startRefresh(ScanData scanData) async {

cw_bitcoin/lib/bitcoin_wallet_addresses.dart

+32-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:bitcoin_base/bitcoin_base.dart';
2+
import 'package:blockchain_utils/blockchain_utils.dart';
23
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
34
import 'package:cw_core/wallet_info.dart';
45
import 'package:mobx/mobx.dart';
@@ -10,9 +11,9 @@ class BitcoinWalletAddresses = BitcoinWalletAddressesBase with _$BitcoinWalletAd
1011
abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
1112
BitcoinWalletAddressesBase(
1213
WalletInfo walletInfo, {
13-
required super.bip32,
1414
required super.network,
1515
required super.isHardwareWallet,
16+
required super.hdWallets,
1617
super.initialAddresses,
1718
super.initialRegularAddressIndex,
1819
super.initialChangeAddressIndex,
@@ -36,44 +37,69 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
3637

3738
@override
3839
BitcoinBaseAddress generateAddress({
40+
required CWBitcoinDerivationType derivationType,
3941
required bool isChange,
4042
required int index,
4143
required BitcoinAddressType addressType,
4244
required BitcoinDerivationInfo derivationInfo,
4345
}) {
46+
final hdWallet = hdWallets[derivationType]!;
47+
48+
if (derivationType == CWBitcoinDerivationType.old) {
49+
final pub = hdWallet
50+
.childKey(Bip32KeyIndex(isChange ? 1 : 0))
51+
.childKey(Bip32KeyIndex(index))
52+
.publicKey;
53+
54+
switch (addressType) {
55+
case P2pkhAddressType.p2pkh:
56+
return ECPublic.fromBip32(pub).toP2pkhAddress();
57+
case SegwitAddresType.p2tr:
58+
return ECPublic.fromBip32(pub).toP2trAddress();
59+
case SegwitAddresType.p2wsh:
60+
return ECPublic.fromBip32(pub).toP2wshAddress();
61+
case P2shAddressType.p2wpkhInP2sh:
62+
return ECPublic.fromBip32(pub).toP2wpkhInP2sh();
63+
case SegwitAddresType.p2wpkh:
64+
return ECPublic.fromBip32(pub).toP2wpkhAddress();
65+
default:
66+
throw ArgumentError('Invalid address type');
67+
}
68+
}
69+
4470
switch (addressType) {
4571
case P2pkhAddressType.p2pkh:
4672
return P2pkhAddress.fromDerivation(
47-
bip32: bip32,
73+
bip32: hdWallet,
4874
derivationInfo: derivationInfo,
4975
isChange: isChange,
5076
index: index,
5177
);
5278
case SegwitAddresType.p2tr:
5379
return P2trAddress.fromDerivation(
54-
bip32: bip32,
80+
bip32: hdWallet,
5581
derivationInfo: derivationInfo,
5682
isChange: isChange,
5783
index: index,
5884
);
5985
case SegwitAddresType.p2wsh:
6086
return P2wshAddress.fromDerivation(
61-
bip32: bip32,
87+
bip32: hdWallet,
6288
derivationInfo: derivationInfo,
6389
isChange: isChange,
6490
index: index,
6591
);
6692
case P2shAddressType.p2wpkhInP2sh:
6793
return P2shAddress.fromDerivation(
68-
bip32: bip32,
94+
bip32: hdWallet,
6995
derivationInfo: derivationInfo,
7096
isChange: isChange,
7197
index: index,
7298
type: P2shAddressType.p2wpkhInP2sh,
7399
);
74100
case SegwitAddresType.p2wpkh:
75101
return P2wpkhAddress.fromDerivation(
76-
bip32: bip32,
102+
bip32: hdWallet,
77103
derivationInfo: derivationInfo,
78104
isChange: isChange,
79105
index: index,

cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
2626
required String name,
2727
required String password,
2828
required this.mnemonic,
29+
required super.derivations,
2930
WalletInfo? walletInfo,
3031
String? passphrase,
3132
}) : super(

0 commit comments

Comments
 (0)