mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
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:
parent
8e4082d680
commit
fb33a6f23d
18 changed files with 433 additions and 144 deletions
|
@ -1,8 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/n2_node.dart';
|
||||
import 'package:cw_core/nano_account.dart';
|
||||
import 'package:cw_core/nano_account_info_response.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
|
@ -10,23 +14,20 @@ import 'package:cw_core/pending_transaction.dart';
|
|||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_priority.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_nano/file.dart';
|
||||
import 'package:cw_core/nano_account.dart';
|
||||
import 'package:cw_core/n2_node.dart';
|
||||
import 'package:cw_nano/nano_balance.dart';
|
||||
import 'package:cw_nano/nano_client.dart';
|
||||
import 'package:cw_nano/nano_transaction_credentials.dart';
|
||||
import 'package:cw_nano/nano_transaction_history.dart';
|
||||
import 'package:cw_nano/nano_transaction_info.dart';
|
||||
import 'package:cw_nano/nano_wallet_addresses.dart';
|
||||
import 'package:cw_nano/nano_wallet_keys.dart';
|
||||
import 'package:cw_nano/pending_nano_transaction.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'dart:async';
|
||||
import 'package:cw_nano/nano_wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
part 'nano_wallet.g.dart';
|
||||
|
@ -34,7 +35,8 @@ part 'nano_wallet.g.dart';
|
|||
class NanoWallet = NanoWalletBase with _$NanoWallet;
|
||||
|
||||
abstract class NanoWalletBase
|
||||
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
|
||||
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo>
|
||||
with Store, WalletKeysFile {
|
||||
NanoWalletBase({
|
||||
required WalletInfo walletInfo,
|
||||
required String mnemonic,
|
||||
|
@ -70,6 +72,7 @@ abstract class NanoWalletBase
|
|||
|
||||
String? _representativeAddress;
|
||||
int repScore = 100;
|
||||
|
||||
bool get isRepOk => repScore >= 90;
|
||||
|
||||
late final NanoClient _client;
|
||||
|
@ -128,14 +131,10 @@ abstract class NanoWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||
return 0; // always 0 :)
|
||||
}
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount) => 0; // always 0 :)
|
||||
|
||||
@override
|
||||
Future<void> changePassword(String password) {
|
||||
throw UnimplementedError("changePassword");
|
||||
}
|
||||
Future<void> changePassword(String password) => throw UnimplementedError("changePassword");
|
||||
|
||||
@override
|
||||
void close() {
|
||||
|
@ -170,9 +169,7 @@ abstract class NanoWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> connectToPowNode({required Node node}) async {
|
||||
_client.connectPow(node);
|
||||
}
|
||||
Future<void> connectToPowNode({required Node node}) async => _client.connectPow(node);
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
|
@ -296,9 +293,7 @@ abstract class NanoWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
NanoWalletKeys get keys {
|
||||
return NanoWalletKeys(seedKey: _hexSeed!);
|
||||
}
|
||||
NanoWalletKeys get keys => NanoWalletKeys(seedKey: _hexSeed!);
|
||||
|
||||
@override
|
||||
String? get privateKey => _privateKey!;
|
||||
|
@ -312,6 +307,11 @@ abstract class NanoWalletBase
|
|||
|
||||
@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());
|
||||
|
@ -323,6 +323,9 @@ abstract class NanoWalletBase
|
|||
|
||||
String get hexSeed => _hexSeed!;
|
||||
|
||||
@override
|
||||
WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, altMnemonic: hexSeed);
|
||||
|
||||
String get representative => _representativeAddress ?? "";
|
||||
|
||||
@action
|
||||
|
@ -358,8 +361,6 @@ abstract class NanoWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'seedKey': _hexSeed,
|
||||
'mnemonic': _mnemonic,
|
||||
|
@ -373,31 +374,47 @@ abstract class NanoWalletBase
|
|||
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;
|
||||
Map<String, dynamic>? data = null;
|
||||
try {
|
||||
final jsonSource = await read(path: path, password: password);
|
||||
|
||||
data = json.decode(jsonSource) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
if (!hasKeysFile) rethrow;
|
||||
}
|
||||
|
||||
final balance = NanoBalance.fromRawString(
|
||||
currentBalance: data['currentBalance'] as String? ?? "0",
|
||||
receivableBalance: data['receivableBalance'] as String? ?? "0",
|
||||
currentBalance: data?['currentBalance'] as String? ?? "0",
|
||||
receivableBalance: data?['receivableBalance'] as String? ?? "0",
|
||||
);
|
||||
|
||||
final WalletKeysData keysData;
|
||||
// Migrate wallet from the old scheme to then new .keys file scheme
|
||||
if (!hasKeysFile) {
|
||||
final mnemonic = data!['mnemonic'] as String;
|
||||
final isHexSeed = !mnemonic.contains(' ');
|
||||
|
||||
keysData = WalletKeysData(
|
||||
mnemonic: isHexSeed ? null : mnemonic, altMnemonic: isHexSeed ? mnemonic : null);
|
||||
} else {
|
||||
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
|
||||
}
|
||||
|
||||
DerivationType derivationType = DerivationType.nano;
|
||||
if (data['derivationType'] == "DerivationType.bip39") {
|
||||
if (data?['derivationType'] == "DerivationType.bip39") {
|
||||
derivationType = DerivationType.bip39;
|
||||
}
|
||||
|
||||
walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
|
||||
if (walletInfo.derivationInfo!.derivationType == null) {
|
||||
walletInfo.derivationInfo!.derivationType = derivationType;
|
||||
}
|
||||
walletInfo.derivationInfo!.derivationType ??= derivationType;
|
||||
|
||||
return NanoWallet(
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
mnemonic: keysData.mnemonic!,
|
||||
initialBalance: balance,
|
||||
);
|
||||
// init() should always be run after this!
|
||||
|
@ -435,7 +452,7 @@ abstract class NanoWalletBase
|
|||
_representativeAddress = await _client.getRepFromPrefs();
|
||||
throw Exception("Failed to get representative address $e");
|
||||
}
|
||||
|
||||
|
||||
repScore = await _client.getRepScore(_representativeAddress!);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
mnemonic: mnemonic,
|
||||
password: credentials.password!,
|
||||
);
|
||||
wallet.init();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue