Cw 688 avoid wallet file corruption (#1582)

* CW-688 Store Seed and keys in .keys file

* CW-688 Open wallet from keys in .keys file and migrate wallets using the old file

* CW-688 Open wallet from keys in .keys file and migrate wallets using the old file

* CW-688 Restore .keys file from .keys.backup

* CW-688 Restore .keys file from .keys.backup

* CW-688 Move saving .keys files into the save function instead of the service

* CW-688 Handle corrupt wallets

* CW-688 Handle corrupt wallets

* CW-688 Remove code duplication

* CW-688 Reduce cache dependency

* wrap any file reading/writing function with try/catch [skip ci]

---------

Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>
This commit is contained in:
Omar Hatem 2024-08-09 23:15:30 +03:00 committed by GitHub
parent 8e4082d680
commit fb33a6f23d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 433 additions and 144 deletions

View file

@ -16,6 +16,7 @@ import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_tron/default_tron_tokens.dart';
import 'package:cw_tron/file.dart';
@ -37,7 +38,8 @@ part 'tron_wallet.g.dart';
class TronWallet = TronWalletBase with _$TronWallet;
abstract class TronWalletBase
extends WalletBase<TronBalance, TronTransactionHistory, TronTransactionInfo> with Store {
extends WalletBase<TronBalance, TronTransactionHistory, TronTransactionInfo>
with Store, WalletKeysFile {
TronWalletBase({
required WalletInfo walletInfo,
String? mnemonic,
@ -124,18 +126,36 @@ abstract class TronWalletBase
required String password,
required WalletInfo walletInfo,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String?;
final privateKey = data['private_key'] as String?;
final balance = TronBalance.fromJSON(data['balance'] as String) ?? TronBalance(BigInt.zero);
Map<String, dynamic>? data;
try {
final jsonSource = await read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
if (!hasKeysFile) rethrow;
}
final balance = TronBalance.fromJSON(data?['balance'] as String) ?? TronBalance(BigInt.zero);
final WalletKeysData keysData;
// Migrate wallet from the old scheme to then new .keys file scheme
if (!hasKeysFile) {
final mnemonic = data!['mnemonic'] as String?;
final privateKey = data['private_key'] as String?;
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
}
return TronWallet(
walletInfo: walletInfo,
password: password,
mnemonic: mnemonic,
privateKey: privateKey,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
initialBalance: balance,
);
}
@ -163,9 +183,7 @@ abstract class TronWalletBase
}) async {
assert(mnemonic != null || privateKey != null);
if (privateKey != null) {
return TronPrivateKey(privateKey);
}
if (privateKey != null) return TronPrivateKey(privateKey);
final seed = bip39.mnemonicToSeed(mnemonic!);
@ -181,14 +199,10 @@ abstract class TronWalletBase
int calculateEstimatedFee(TransactionPriority priority, int? amount) => 0;
@override
Future<void> changePassword(String password) {
throw UnimplementedError("changePassword");
}
Future<void> changePassword(String password) => throw UnimplementedError("changePassword");
@override
void close() {
_transactionsUpdateTimer?.cancel();
}
void close() => _transactionsUpdateTimer?.cancel();
@action
@override
@ -406,12 +420,15 @@ abstract class TronWalletBase
Object get keys => throw UnimplementedError("keys");
@override
Future<void> rescan({required int height}) {
throw UnimplementedError("rescan");
}
Future<void> rescan({required int height}) => throw UnimplementedError("rescan");
@override
Future<void> save() async {
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
await saveKeysFile(_password);
saveKeysFile(_password, true);
}
await walletAddresses.updateAddressesInBox();
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
@ -424,7 +441,8 @@ abstract class TronWalletBase
@override
String get privateKey => _tronPrivateKey.toHex();
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
@override
WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, privateKey: privateKey);
String toJSON() => json.encode({
'mnemonic': _mnemonic,
@ -512,7 +530,7 @@ abstract class TronWalletBase
@override
Future<void> renameWalletFiles(String newWalletName) async {
String transactionHistoryFileNameForWallet = 'tron_transactions.json';
const transactionHistoryFileNameForWallet = 'tron_transactions.json';
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
final currentWalletFile = File(currentWalletPath);
@ -550,9 +568,7 @@ abstract class TronWalletBase
Future<String> signMessage(String message, {String? address}) async =>
_tronPrivateKey.signPersonalMessage(ascii.encode(message));
String getTronBase58AddressFromHex(String hexAddress) {
return TronAddress(hexAddress).toAddress();
}
String getTronBase58AddressFromHex(String hexAddress) => TronAddress(hexAddress).toAddress();
void updateScanProviderUsageState(bool isEnabled) {
if (isEnabled) {

View file

@ -1,6 +1,7 @@
import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:collection/collection.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_history.dart';
@ -14,7 +15,6 @@ import 'package:cw_tron/tron_exception.dart';
import 'package:cw_tron/tron_wallet.dart';
import 'package:cw_tron/tron_wallet_creation_credentials.dart';
import 'package:hive/hive.dart';
import 'package:collection/collection.dart';
class TronWalletService extends WalletService<
TronNewWalletCredentials,
@ -153,7 +153,8 @@ class TronWalletService extends WalletService<
}
@override
Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>> restoreFromHardwareWallet(TronNewWalletCredentials credentials) {
Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>>
restoreFromHardwareWallet(TronNewWalletCredentials credentials) {
// TODO: implement restoreFromHardwareWallet
throw UnimplementedError();
}