dev
dart 214 lines 7.54 KB
Raw
1 import 'dart:io';
2 import 'package:bitcoin_base/bitcoin_base.dart';
3 import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
4 import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
5 import 'package:cw_core/encryption_file_utils.dart';
6 import 'package:cw_core/unspent_coins_info.dart';
7 import 'package:hive/hive.dart';
8 import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
9 import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
10 import 'package:cw_bitcoin/litecoin_wallet.dart';
11 import 'package:cw_core/wallet_service.dart';
12 import 'package:cw_core/pathForWallet.dart';
13 import 'package:cw_core/wallet_type.dart';
14 import 'package:cw_core/wallet_info.dart';
15 import 'package:bip39/bip39.dart' as bip39;
16 import 'package:path_provider/path_provider.dart';
17
18 class LitecoinWalletService extends WalletService<
19 BitcoinNewWalletCredentials,
20 BitcoinRestoreWalletFromSeedCredentials,
21 LitecoinWalletFromKeysCredentials,
22 BitcoinRestoreWalletFromHardware> {
23 LitecoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
24
25 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
26 final bool isDirect;
27
28 @override
29 WalletType getType() => WalletType.litecoin;
30
31 @override
32 Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
33 final String mnemonic;
34 switch (credentials.derivationInfo?.derivationType) {
35 case DerivationType.bip39:
36 final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
37
38 mnemonic = credentials.mnemonic ?? await MnemonicBip39.generate(strength: strength);
39 break;
40 case DerivationType.electrum:
41 default:
42 mnemonic = await generateElectrumMnemonic();
43 break;
44 }
45
46 final wallet = await LitecoinWalletBase.create(
47 mnemonic: mnemonic,
48 password: credentials.password!,
49 passphrase: credentials.passphrase,
50 walletInfo: credentials.walletInfo!,
51 derivationInfo:
52 credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
53 unspentCoinsInfo: unspentCoinsInfoSource,
54 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
55 );
56 await wallet.save();
57 await wallet.init();
58
59 return wallet;
60 }
61
62 @override
63 Future<bool> isWalletExit(String name) async =>
64 File(await pathForWallet(name: name, type: getType())).existsSync();
65
66 @override
67 Future<LitecoinWallet> openWallet(String name, String password) async {
68 final walletInfo = await WalletInfo.get(name, getType());
69 if (walletInfo == null) {
70 throw Exception('Wallet not found');
71 }
72
73 try {
74 final wallet = await LitecoinWalletBase.open(
75 password: password,
76 name: name,
77 walletInfo: walletInfo,
78 unspentCoinsInfo: unspentCoinsInfoSource,
79 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
80 );
81 await wallet.init();
82 saveBackup(name);
83 return wallet;
84 } catch (_) {
85 await restoreWalletFilesFromBackup(name);
86 final wallet = await LitecoinWalletBase.open(
87 password: password,
88 name: name,
89 walletInfo: walletInfo,
90 unspentCoinsInfo: unspentCoinsInfoSource,
91 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
92 );
93 await wallet.init();
94 return wallet;
95 }
96 }
97
98 @override
99 Future<void> remove(String wallet) async {
100 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
101 final walletInfo = await WalletInfo.get(wallet, getType());
102 if (walletInfo == null) {
103 throw Exception('Wallet not found');
104 }
105 await WalletInfo.delete(walletInfo);
106
107 // if there are no more litecoin wallets left, cleanup the neutrino db and other files created by mwebd:
108 if ((await WalletInfo.selectList('type = ?', [WalletType.litecoin.index])).isEmpty) {
109 final appDirPath = (await getApplicationSupportDirectory()).path;
110 File neturinoDb = File('$appDirPath/neutrino.db');
111 File blockHeaders = File('$appDirPath/block_headers.bin');
112 File regFilterHeaders = File('$appDirPath/reg_filter_headers.bin');
113 File mwebdLogs = File('$appDirPath/logs/debug.log');
114 if (neturinoDb.existsSync()) {
115 neturinoDb.deleteSync();
116 }
117 if (blockHeaders.existsSync()) {
118 blockHeaders.deleteSync();
119 }
120 if (regFilterHeaders.existsSync()) {
121 regFilterHeaders.deleteSync();
122 }
123 if (mwebdLogs.existsSync()) {
124 mwebdLogs.deleteSync();
125 }
126 }
127
128 final unspentCoinsToDelete = unspentCoinsInfoSource.values
129 .where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
130 .toList();
131
132 final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
133
134 if (keysToDelete.isNotEmpty) {
135 await unspentCoinsInfoSource.deleteAll(keysToDelete);
136 }
137 }
138
139 @override
140 Future<void> rename(String currentName, String password, String newName) async {
141 if (currentName == newName) return;
142
143 await LitecoinWalletBase.copyMwebBox(fromName: currentName, toName: newName);
144 await super.rename(currentName, password, newName);
145 await LitecoinWalletBase.deleteMwebBox(currentName);
146 }
147
148 @override
149 Future<LitecoinWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
150 {bool? isTestnet}) async {
151 final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
152 credentials.walletInfo?.network = network.value;
153 final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
154 derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
155 await derivationInfo.save();
156 credentials.walletInfo!.save();
157
158 final wallet = await LitecoinWallet(
159 password: credentials.password!,
160 xpub: credentials.hwAccountData.xpub,
161 walletInfo: credentials.walletInfo!,
162 derivationInfo: derivationInfo,
163 unspentCoinsInfo: unspentCoinsInfoSource,
164 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
165 );
166 await wallet.save();
167 await wallet.init();
168 return wallet;
169 }
170
171 @override
172 Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials,
173 {bool? isTestnet}) async {
174 final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
175 credentials.walletInfo?.network = network.value;
176
177 final wallet = await LitecoinWallet(
178 password: credentials.password!,
179 xpub: credentials.xpub,
180 scanSecretOverride: credentials.scanSecret,
181 spendPubkeyOverride: credentials.spendPubkey,
182 walletInfo: credentials.walletInfo!,
183 derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
184 unspentCoinsInfo: unspentCoinsInfoSource,
185 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
186 );
187
188 await wallet.save();
189 await wallet.init();
190 return wallet;
191 }
192
193 @override
194 Future<LitecoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
195 {bool? isTestnet}) async {
196 if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
197 throw LitecoinMnemonicIsIncorrectException();
198 }
199
200 final wallet = await LitecoinWalletBase.create(
201 password: credentials.password!,
202 passphrase: credentials.passphrase,
203 mnemonic: credentials.mnemonic,
204 walletInfo: credentials.walletInfo!,
205 derivationInfo:
206 credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
207 unspentCoinsInfo: unspentCoinsInfoSource,
208 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
209 );
210 await wallet.save();
211 await wallet.init();
212 return wallet;
213 }
214 }