Files
hash-wallet/cw_bitcoin/lib/litecoin_wallet_service.dart
77812016ac LCW-1144 cupcake litecoin (#2557)
* Add psbt rpcs

* Only build arm64

* Rebuild protos

* Update proto

* Add psbt create rpc

* Restore wallet stuff

* more stuff

* more stuff

* more stuff

* Fix open wallet

* Fix mweb enabled

* Start of buildPsbt

* Update proto

* Psbt add recipients

* fixes

* slightly better

* more fixes

* decode ur

* add more commit code

* Update proto

* use new proto

* Don't eat psbt rpc errors

* Use unix domain socket for mwebd

* add gitignore

* Undo build only arm64

* fix pure mweb txns

* fix mweb txids

* fix ci

* fix blank pegin address

* chore: update cw_mweb dependencies
chore: remove cw_core as dependency from cw_mweb

* fix: Ltub restore

* fix: address review crashes, fix non-mweb view only wallets

* minor fixes

---------

Co-authored-by: Hector Chu <hectorchu@gmail.com>
Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
2025-11-24 17:58:12 +01:00

231 lines
8.0 KiB
Dart

import 'dart:io';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:hive/hive.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_bitcoin/litecoin_wallet.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:collection/collection.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:path_provider/path_provider.dart';
class LitecoinWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
LitecoinWalletFromKeysCredentials,
BitcoinRestoreWalletFromHardware> {
LitecoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
@override
WalletType getType() => WalletType.litecoin;
@override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
final String mnemonic;
switch (credentials.derivationInfo?.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
mnemonic = credentials.mnemonic ?? await MnemonicBip39.generate(strength: strength);
break;
case DerivationType.electrum:
default:
mnemonic = await generateElectrumMnemonic();
break;
}
final wallet = await LitecoinWalletBase.create(
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!,
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
return wallet;
}
@override
Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync();
@override
Future<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await LitecoinWalletBase.open(
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
saveBackup(name);
return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await LitecoinWalletBase.open(
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
return wallet;
}
}
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
// if there are no more litecoin wallets left, cleanup the neutrino db and other files created by mwebd:
if ((await WalletInfo.selectList('type = ?', [WalletType.litecoin.index])).isEmpty) {
final appDirPath = (await getApplicationSupportDirectory()).path;
File neturinoDb = File('$appDirPath/neutrino.db');
File blockHeaders = File('$appDirPath/block_headers.bin');
File regFilterHeaders = File('$appDirPath/reg_filter_headers.bin');
File mwebdLogs = File('$appDirPath/logs/debug.log');
if (neturinoDb.existsSync()) {
neturinoDb.deleteSync();
}
if (blockHeaders.existsSync()) {
blockHeaders.deleteSync();
}
if (regFilterHeaders.existsSync()) {
regFilterHeaders.deleteSync();
}
if (mwebdLogs.existsSync()) {
mwebdLogs.deleteSync();
}
}
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
if (keysToDelete.isNotEmpty) {
await unspentCoinsInfoSource.deleteAll(keysToDelete);
}
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await LitecoinWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await newWalletInfo.save();
}
@override
Future<LitecoinWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
{bool? isTestnet}) async {
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationPath =
credentials.hwAccountData.derivationPath;
await derivationInfo.save();
credentials.walletInfo!.save();
final wallet = await LitecoinWallet(
password: credentials.password!,
xpub: credentials.hwAccountData.xpub,
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
return wallet;
}
@override
Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials,
{bool? isTestnet}) async {
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
final wallet = await LitecoinWallet(
password: credentials.password!,
xpub: credentials.xpub,
scanSecretOverride: credentials.scanSecret,
spendPubkeyOverride: credentials.spendPubkey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
return wallet;
}
@override
Future<LitecoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
throw LitecoinMnemonicIsIncorrectException();
}
final wallet = await LitecoinWalletBase.create(
password: credentials.password!,
passphrase: credentials.passphrase,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
return wallet;
}
}