Backup stuff.

This commit is contained in:
M 2021-01-15 19:41:30 +02:00
parent 9a79fcdc23
commit 47ceac2dd6
24 changed files with 459 additions and 253 deletions

View file

@ -14,128 +14,58 @@ import 'package:cake_wallet/entities/encrypt.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
class BackupService { class BackupService {
BackupService(this._flutterSecureStorage, this._authService, BackupService(this._flutterSecureStorage, this._walletInfoSource,
this._walletInfoSource, this._keyService, this._sharedPreferences) this._keyService, this._sharedPreferences)
: _cipher = chacha20Poly1305Aead; : _cipher = chacha20Poly1305Aead;
static const currentVersion = _v1;
static const _v1 = 1;
final Cipher _cipher; final Cipher _cipher;
final FlutterSecureStorage _flutterSecureStorage; final FlutterSecureStorage _flutterSecureStorage;
final SharedPreferences _sharedPreferences; final SharedPreferences _sharedPreferences;
final AuthService _authService;
final Box<WalletInfo> _walletInfoSource; final Box<WalletInfo> _walletInfoSource;
final KeyService _keyService; final KeyService _keyService;
Future<void> importBackup(Uint8List data, String password, Future<void> importBackup(Uint8List data, String password,
{@required String nonce}) async { {String nonce = secrets.backupSalt}) async {
final appDir = await getApplicationDocumentsDirectory(); final version = getVersion(data);
final decryptedData = await _decrypt(data, password, nonce); final backupBytes = data.toList()..removeAt(0);
final zip = ZipDecoder().decodeBytes(decryptedData); final backupData = Uint8List.fromList(backupBytes);
zip.files.forEach((file) { switch (version) {
final filename = file.name; case _v1:
await _importBackupV1(backupData, password, nonce: nonce);
if (file.isFile) { break;
final data = file.content as List<int>; default:
File('${appDir.path}/' + filename) break;
..createSync(recursive: true)
..writeAsBytesSync(data);
} else {
Directory('${appDir.path}/' + filename)..create(recursive: true);
}
print(filename);
});
await importKeychainDump(password, nonce: nonce);
await importPreferencesDump();
}
Future<void> importPreferencesDump() async {
final appDir = await getApplicationDocumentsDirectory();
final preferencesFile = File('${appDir.path}/~_preferences_dump');
if (!preferencesFile.existsSync()) {
return;
} }
final data =
json.decode(preferencesFile.readAsStringSync()) as Map<String, Object>;
print('data $data');
await _sharedPreferences.setString(PreferencesKey.currentWalletName,
data[PreferencesKey.currentWalletName] as String);
await _sharedPreferences.setInt(PreferencesKey.currentNodeIdKey,
data[PreferencesKey.currentNodeIdKey] as int);
await _sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey,
data[PreferencesKey.currentBalanceDisplayModeKey] as int);
await _sharedPreferences.setInt(PreferencesKey.currentWalletType,
data[PreferencesKey.currentWalletType] as int);
await _sharedPreferences.setString(PreferencesKey.currentFiatCurrencyKey,
data[PreferencesKey.currentFiatCurrencyKey] as String);
await _sharedPreferences.setBool(
PreferencesKey.shouldSaveRecipientAddressKey,
data[PreferencesKey.shouldSaveRecipientAddressKey] as bool);
await _sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKey,
data[PreferencesKey.currentTransactionPriorityKey] as int);
await _sharedPreferences.setBool(
PreferencesKey.allowBiometricalAuthenticationKey,
data[PreferencesKey.allowBiometricalAuthenticationKey] as bool);
await _sharedPreferences.setInt(
PreferencesKey.currentBitcoinElectrumSererIdKey,
data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int);
await _sharedPreferences.setInt(PreferencesKey.currentLanguageCode,
data[PreferencesKey.currentLanguageCode] as int);
await _sharedPreferences.setInt(PreferencesKey.displayActionListModeKey,
data[PreferencesKey.displayActionListModeKey] as int);
await _sharedPreferences.setInt(
'current_theme', data['current_theme'] as int);
await preferencesFile.delete();
}
Future<void> importKeychainDump(String password,
{@required String nonce}) async {
final appDir = await getApplicationDocumentsDirectory();
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData =
await _decrypt(keychainDumpFile.readAsBytesSync(), password, nonce);
final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData))
as Map<String, dynamic>;
final keychainWalletsInfo = keychainJSON['wallets'] as List;
final decodedPin = keychainJSON['pin'] as String;
final pinCodeKey = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
keychainWalletsInfo.forEach((dynamic rawInfo) async {
final info = rawInfo as Map<String, dynamic>;
await importWalletKeychainInfo(info);
});
await _flutterSecureStorage.write(
key: pinCodeKey, value: encodedPinCode(pin: decodedPin));
keychainDumpFile.deleteSync();
}
Future<void> importWalletKeychainInfo(Map<String, dynamic> info) async {
final name = info['name'] as String;
final password = info['password'] as String;
await _keyService.saveWalletPassword(walletName: name, password: password);
} }
Future<Uint8List> exportBackup(String password, Future<Uint8List> exportBackup(String password,
{@required String nonce}) async { {String nonce = secrets.backupSalt, int version = currentVersion}) async {
switch (version) {
case _v1:
return await _exportBackupV1(password, nonce: nonce);
default:
return null;
}
}
Future<Uint8List> _exportBackupV1(String password,
{String nonce = secrets.backupSalt}) async {
final zipEncoder = ZipFileEncoder(); final zipEncoder = ZipFileEncoder();
final appDir = await getApplicationDocumentsDirectory(); final appDir = await getApplicationDocumentsDirectory();
final now = DateTime.now(); final now = DateTime.now();
final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP'); final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP');
final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip'; final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip';
final fileEntities = appDir.listSync(recursive: false); final fileEntities = appDir.listSync(recursive: false);
final keychainDump = await exportKeychainDump(password, nonce: nonce); final keychainDump = await _exportKeychainDump(password, nonce: nonce);
final preferencesDump = await exportPreferencesJSON(); final preferencesDump = await _exportPreferencesJSON();
final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP'); final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP');
final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP'); final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP');
@ -165,12 +95,120 @@ class BackupService {
final content = File(archivePath).readAsBytesSync(); final content = File(archivePath).readAsBytesSync();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
final encryptedData = await _encrypt(content, password, nonce);
return await _encrypt(content, password, nonce); return setVersion(encryptedData, currentVersion);
} }
Future<Uint8List> exportKeychainDump(String password, Future<void> _importBackupV1(Uint8List data, String password,
{@required String nonce}) async { {@required String nonce}) async {
final appDir = await getApplicationDocumentsDirectory();
final decryptedData = await _decrypt(data, password, nonce);
final zip = ZipDecoder().decodeBytes(decryptedData);
zip.files.forEach((file) {
final filename = file.name;
if (file.isFile) {
final content = file.content as List<int>;
File('${appDir.path}/' + filename)
..createSync(recursive: true)
..writeAsBytesSync(content);
} else {
Directory('${appDir.path}/' + filename)..create(recursive: true);
}
});
await _importKeychainDump(password, nonce: nonce);
await _importPreferencesDump();
}
Future<void> _importPreferencesDump() async {
final appDir = await getApplicationDocumentsDirectory();
final preferencesFile = File('${appDir.path}/~_preferences_dump');
const defaultSettingsMigrationVersionKey = PreferencesKey.currentDefaultSettingsMigrationVersion;
if (!preferencesFile.existsSync()) {
return;
}
final data =
json.decode(preferencesFile.readAsStringSync()) as Map<String, Object>;
await _sharedPreferences.setString(PreferencesKey.currentWalletName,
data[PreferencesKey.currentWalletName] as String);
await _sharedPreferences.setInt(PreferencesKey.currentNodeIdKey,
data[PreferencesKey.currentNodeIdKey] as int);
await _sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey,
data[PreferencesKey.currentBalanceDisplayModeKey] as int);
await _sharedPreferences.setInt(PreferencesKey.currentWalletType,
data[PreferencesKey.currentWalletType] as int);
await _sharedPreferences.setString(PreferencesKey.currentFiatCurrencyKey,
data[PreferencesKey.currentFiatCurrencyKey] as String);
await _sharedPreferences.setBool(
PreferencesKey.shouldSaveRecipientAddressKey,
data[PreferencesKey.shouldSaveRecipientAddressKey] as bool);
await _sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKey,
data[PreferencesKey.currentTransactionPriorityKey] as int);
await _sharedPreferences.setBool(
PreferencesKey.allowBiometricalAuthenticationKey,
data[PreferencesKey.allowBiometricalAuthenticationKey] as bool);
await _sharedPreferences.setInt(
PreferencesKey.currentBitcoinElectrumSererIdKey,
data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int);
await _sharedPreferences.setInt(PreferencesKey.currentLanguageCode,
data[PreferencesKey.currentLanguageCode] as int);
await _sharedPreferences.setInt(PreferencesKey.displayActionListModeKey,
data[PreferencesKey.displayActionListModeKey] as int);
await _sharedPreferences.setInt(
'current_theme', data['current_theme'] as int);
await _sharedPreferences.setInt(defaultSettingsMigrationVersionKey,
data[defaultSettingsMigrationVersionKey] as int);
await preferencesFile.delete();
}
Future<void> _importKeychainDump(String password,
{@required String nonce,
String keychainSalt = secrets.backupKeychainSalt}) async {
final appDir = await getApplicationDocumentsDirectory();
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData = await _decrypt(
keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce);
final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData))
as Map<String, dynamic>;
final keychainWalletsInfo = keychainJSON['wallets'] as List;
final decodedPin = keychainJSON['pin'] as String;
final pinCodeKey = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final backupPasswordKey =
generateStoreKeyFor(key: SecretStoreKey.backupPassword);
final backupPassword = keychainJSON[backupPasswordKey] as String;
await _flutterSecureStorage.write(
key: backupPasswordKey, value: backupPassword);
keychainWalletsInfo.forEach((dynamic rawInfo) async {
final info = rawInfo as Map<String, dynamic>;
await importWalletKeychainInfo(info);
});
await _flutterSecureStorage.write(
key: pinCodeKey, value: encodedPinCode(pin: decodedPin));
keychainDumpFile.deleteSync();
}
Future<void> importWalletKeychainInfo(Map<String, dynamic> info) async {
final name = info['name'] as String;
final password = info['password'] as String;
await _keyService.saveWalletPassword(walletName: name, password: password);
}
Future<Uint8List> _exportKeychainDump(String password,
{@required String nonce,
String keychainSalt = secrets.backupKeychainSalt}) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final encodedPin = await _flutterSecureStorage.read(key: key); final encodedPin = await _flutterSecureStorage.read(key: key);
final decodedPin = decodedPinCode(pin: encodedPin); final decodedPin = decodedPinCode(pin: encodedPin);
@ -183,15 +221,25 @@ class BackupService {
await _keyService.getWalletPassword(walletName: walletInfo.name) await _keyService.getWalletPassword(walletName: walletInfo.name)
}; };
})); }));
final backupPasswordKey =
final data = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
utf8.encode(json.encode({'pin': decodedPin, 'wallets': wallets})); final backupPassword =
final encrypted = await _encrypt(Uint8List.fromList(data), password, nonce); await _flutterSecureStorage.read(key: backupPasswordKey);
final data = utf8.encode(json.encode({
'pin': decodedPin,
'wallets': wallets,
backupPasswordKey: backupPassword
}));
final encrypted = await _encrypt(
Uint8List.fromList(data), '$keychainSalt$password', nonce);
return encrypted; return encrypted;
} }
Future<String> exportPreferencesJSON() async { Future<String> _exportPreferencesJSON() async {
const defaultSettingsMigrationVersionKey =
'current_default_settings_migration_version';
final preferences = <String, Object>{ final preferences = <String, Object>{
PreferencesKey.currentWalletName: PreferencesKey.currentWalletName:
_sharedPreferences.getString(PreferencesKey.currentWalletName), _sharedPreferences.getString(PreferencesKey.currentWalletName),
@ -219,13 +267,22 @@ class BackupService {
_sharedPreferences.getString(PreferencesKey.currentLanguageCode), _sharedPreferences.getString(PreferencesKey.currentLanguageCode),
PreferencesKey.displayActionListModeKey: PreferencesKey.displayActionListModeKey:
_sharedPreferences.getInt(PreferencesKey.displayActionListModeKey), _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme) PreferencesKey.currentTheme:
// FIX-ME: Unnamed constant. _sharedPreferences.getInt(PreferencesKey.currentTheme),
defaultSettingsMigrationVersionKey:
_sharedPreferences.getInt(defaultSettingsMigrationVersionKey)
}; };
return json.encode(preferences); return json.encode(preferences);
} }
int getVersion(Uint8List data) => data.toList().first;
Uint8List setVersion(Uint8List data, int version) {
final bytes = data.toList()..insert(0, version);
return Uint8List.fromList(bytes);
}
Future<Uint8List> _encrypt( Future<Uint8List> _encrypt(
Uint8List data, String secretKeySource, String nonceBase64) async { Uint8List data, String secretKeySource, String nonceBase64) async {
final secretKeyHash = await sha256.hash(utf8.encode(secretKeySource)); final secretKeyHash = await sha256.hash(utf8.encode(secretKeySource));

View file

@ -1,5 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
import 'package:cake_wallet/core/backup.dart'; import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/core/wallet_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart'; import 'package:cake_wallet/entities/biometric_auth.dart';
import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact_record.dart';
@ -105,6 +105,15 @@ import 'package:cake_wallet/exchange/exchange_template.dart';
final getIt = GetIt.instance; final getIt = GetIt.instance;
var _isSetupFinished = false;
Box<WalletInfo> _walletInfoSource;
Box<Node> _nodeSource;
Box<Contact> _contactSource;
Box<Trade> _tradesSource;
Box<Template> _templates;
Box<ExchangeTemplate> _exchangeTemplates;
Box<TransactionDescription> _transactionDescriptionBox;
Future setup( Future setup(
{Box<WalletInfo> walletInfoSource, {Box<WalletInfo> walletInfoSource,
Box<Node> nodeSource, Box<Node> nodeSource,
@ -113,12 +122,26 @@ Future setup(
Box<Template> templates, Box<Template> templates,
Box<ExchangeTemplate> exchangeTemplates, Box<ExchangeTemplate> exchangeTemplates,
Box<TransactionDescription> transactionDescriptionBox}) async { Box<TransactionDescription> transactionDescriptionBox}) async {
getIt.registerSingletonAsync<SharedPreferences>( _walletInfoSource = walletInfoSource;
() => SharedPreferences.getInstance()); _nodeSource = nodeSource;
_contactSource = contactSource;
_tradesSource = tradesSource;
_templates = templates;
_exchangeTemplates = exchangeTemplates;
_transactionDescriptionBox = transactionDescriptionBox;
final settingsStore = await SettingsStoreBase.load(nodeSource: nodeSource); if (!_isSetupFinished) {
getIt.registerSingletonAsync<SharedPreferences>(
() => SharedPreferences.getInstance());
}
getIt.registerSingleton<Box<Node>>(nodeSource); final settingsStore = await SettingsStoreBase.load(nodeSource: _nodeSource);
if (_isSetupFinished) {
return;
}
getIt.registerFactory<Box<Node>>(() => _nodeSource);
getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage()); getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
getIt.registerSingleton(AuthenticationStore()); getIt.registerSingleton(AuthenticationStore());
@ -131,14 +154,14 @@ Future setup(
settingsStore: getIt.get<SettingsStore>(), settingsStore: getIt.get<SettingsStore>(),
nodeListStore: getIt.get<NodeListStore>())); nodeListStore: getIt.get<NodeListStore>()));
getIt.registerSingleton<TradesStore>(TradesStore( getIt.registerSingleton<TradesStore>(TradesStore(
tradesSource: tradesSource, settingsStore: getIt.get<SettingsStore>())); tradesSource: _tradesSource, settingsStore: getIt.get<SettingsStore>()));
getIt.registerSingleton<TradeFilterStore>(TradeFilterStore()); getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore()); getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
getIt.registerSingleton<FiatConversionStore>(FiatConversionStore()); getIt.registerSingleton<FiatConversionStore>(FiatConversionStore());
getIt.registerSingleton<SendTemplateStore>( getIt.registerSingleton<SendTemplateStore>(
SendTemplateStore(templateSource: templates)); SendTemplateStore(templateSource: _templates));
getIt.registerSingleton<ExchangeTemplateStore>( getIt.registerSingleton<ExchangeTemplateStore>(
ExchangeTemplateStore(templateSource: exchangeTemplates)); ExchangeTemplateStore(templateSource: _exchangeTemplates));
final secretStore = final secretStore =
await SecretStoreBase.load(getIt.get<FlutterSecureStorage>()); await SecretStoreBase.load(getIt.get<FlutterSecureStorage>());
@ -157,7 +180,7 @@ Future setup(
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) => getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
WalletNewVM(getIt.get<AppStore>(), WalletNewVM(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), walletInfoSource, getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
type: type)); type: type));
getIt getIt
@ -167,7 +190,7 @@ Future setup(
final mnemonic = args[2] as String; final mnemonic = args[2] as String;
return WalletRestorationFromSeedVM(getIt.get<AppStore>(), return WalletRestorationFromSeedVM(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), walletInfoSource, getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
type: type, language: language, seed: mnemonic); type: type, language: language, seed: mnemonic);
}); });
@ -177,7 +200,7 @@ Future setup(
final language = args[1] as String; final language = args[1] as String;
return WalletRestorationFromKeysVM(getIt.get<AppStore>(), return WalletRestorationFromKeysVM(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), walletInfoSource, getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
type: type, language: language); type: type, language: language);
}); });
@ -262,7 +285,7 @@ Future setup(
getIt.get<AppStore>().settingsStore, getIt.get<AppStore>().settingsStore,
getIt.get<SendTemplateStore>(), getIt.get<SendTemplateStore>(),
getIt.get<FiatConversionStore>(), getIt.get<FiatConversionStore>(),
transactionDescriptionBox)); _transactionDescriptionBox));
getIt.registerFactory( getIt.registerFactory(
() => SendPage(sendViewModel: getIt.get<SendViewModel>())); () => SendPage(sendViewModel: getIt.get<SendViewModel>()));
@ -271,7 +294,7 @@ Future setup(
() => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>())); () => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
getIt.registerFactory(() => WalletListViewModel( getIt.registerFactory(() => WalletListViewModel(
walletInfoSource, _walletInfoSource,
getIt.get<AppStore>(), getIt.get<AppStore>(),
getIt.get<KeyService>(), getIt.get<KeyService>(),
getIt.get<WalletNewVM>(param1: WalletType.monero))); getIt.get<WalletNewVM>(param1: WalletType.monero)));
@ -342,10 +365,10 @@ Future setup(
getIt.registerFactoryParam<ContactViewModel, ContactRecord, void>( getIt.registerFactoryParam<ContactViewModel, ContactRecord, void>(
(ContactRecord contact, _) => (ContactRecord contact, _) =>
ContactViewModel(contactSource, contact: contact)); ContactViewModel(_contactSource, contact: contact));
getIt.registerFactory( getIt.registerFactory(
() => ContactListViewModel(contactSource, walletInfoSource)); () => ContactListViewModel(_contactSource, _walletInfoSource));
getIt.registerFactoryParam<ContactListPage, bool, void>( getIt.registerFactoryParam<ContactListPage, bool, void>(
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(), (bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
@ -358,27 +381,27 @@ Future setup(
getIt.registerFactory(() { getIt.registerFactory(() {
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
return NodeListViewModel( return NodeListViewModel(
nodeSource, appStore.wallet, appStore.settingsStore); _nodeSource, appStore.wallet, appStore.settingsStore);
}); });
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>())); getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
getIt.registerFactory(() => getIt.registerFactory(() =>
NodeCreateOrEditViewModel(nodeSource, getIt.get<AppStore>().wallet)); NodeCreateOrEditViewModel(_nodeSource, getIt.get<AppStore>().wallet));
getIt.registerFactory( getIt.registerFactory(
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>())); () => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
getIt.registerFactory(() => ExchangeViewModel( getIt.registerFactory(() => ExchangeViewModel(
getIt.get<AppStore>().wallet, getIt.get<AppStore>().wallet,
tradesSource, _tradesSource,
getIt.get<ExchangeTemplateStore>(), getIt.get<ExchangeTemplateStore>(),
getIt.get<TradesStore>(), getIt.get<TradesStore>(),
getIt.get<AppStore>().settingsStore)); getIt.get<AppStore>().settingsStore));
getIt.registerFactory(() => ExchangeTradeViewModel( getIt.registerFactory(() => ExchangeTradeViewModel(
wallet: getIt.get<AppStore>().wallet, wallet: getIt.get<AppStore>().wallet,
trades: tradesSource, trades: _tradesSource,
tradesStore: getIt.get<TradesStore>(), tradesStore: getIt.get<TradesStore>(),
sendViewModel: getIt.get<SendViewModel>())); sendViewModel: getIt.get<SendViewModel>()));
@ -393,9 +416,9 @@ Future setup(
getIt.registerFactory( getIt.registerFactory(
() => ExchangeTemplatePage(getIt.get<ExchangeViewModel>())); () => ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
getIt.registerFactory(() => MoneroWalletService(walletInfoSource)); getIt.registerFactory(() => MoneroWalletService(_walletInfoSource));
getIt.registerFactory(() => BitcoinWalletService(walletInfoSource)); getIt.registerFactory(() => BitcoinWalletService(_walletInfoSource));
getIt.registerFactoryParam<WalletService, WalletType, void>( getIt.registerFactoryParam<WalletService, WalletType, void>(
(WalletType param1, __) { (WalletType param1, __) {
@ -428,7 +451,7 @@ Future setup(
getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>( getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>(
(type, _) => WalletRestoreViewModel(getIt.get<AppStore>(), (type, _) => WalletRestoreViewModel(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), walletInfoSource, getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
type: type)); type: type));
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) =>
@ -438,7 +461,7 @@ Future setup(
.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>( .registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
(TransactionInfo transactionInfo, _) => TransactionDetailsViewModel( (TransactionInfo transactionInfo, _) => TransactionDetailsViewModel(
transactionInfo: transactionInfo, transactionInfo: transactionInfo,
transactionDescriptionBox: transactionDescriptionBox, transactionDescriptionBox: _transactionDescriptionBox,
settingsStore: getIt.get<SettingsStore>())); settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>( getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
@ -455,12 +478,11 @@ Future setup(
(WalletType type, _) => PreSeedPage(type)); (WalletType type, _) => PreSeedPage(type));
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) => getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
TradeDetailsViewModel(tradeForDetails: trade, trades: tradesSource)); TradeDetailsViewModel(tradeForDetails: trade, trades: _tradesSource));
getIt.registerFactory(() => BackupService( getIt.registerFactory(() => BackupService(
getIt.get<FlutterSecureStorage>(), getIt.get<FlutterSecureStorage>(),
getIt.get<AuthService>(), _walletInfoSource,
walletInfoSource,
getIt.get<KeyService>(), getIt.get<KeyService>(),
getIt.get<SharedPreferences>())); getIt.get<SharedPreferences>()));
@ -476,8 +498,7 @@ Future setup(
getIt.registerFactory( getIt.registerFactory(
() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>())); () => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>()));
getIt.registerFactoryParam<RestoreOptionsPage, WalletType, void>( getIt.registerFactory(() => RestoreOptionsPage());
(WalletType type, _) => RestoreOptionsPage(type: type));
getIt.registerFactory( getIt.registerFactory(
() => RestoreFromBackupViewModel(getIt.get<BackupService>())); () => RestoreFromBackupViewModel(getIt.get<BackupService>()));
@ -487,4 +508,6 @@ Future setup(
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) => getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade))); TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
_isSetupFinished = true;
} }

View file

@ -5,11 +5,12 @@ import 'package:cake_wallet/utils/mobx.dart';
part 'contact.g.dart'; part 'contact.g.dart';
@HiveType(typeId: 0) @HiveType(typeId: Contact.typeId)
class Contact extends HiveObject with Keyable { class Contact extends HiveObject with Keyable {
Contact({@required this.name, @required this.address, CryptoCurrency type}) Contact({@required this.name, @required this.address, CryptoCurrency type})
: raw = type?.raw; : raw = type?.raw;
static const typeId = 0;
static const boxName = 'Contacts'; static const boxName = 'Contacts';
@HiveField(0) @HiveField(0)

View file

@ -1,9 +1,12 @@
import 'dart:io' show File, Platform; import 'dart:io' show File, Platform;
import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/pathForWallet.dart'; import 'package:cake_wallet/entities/pathForWallet.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/monero/monero_wallet_service.dart'; import 'package:cake_wallet/monero/monero_wallet_service.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
@ -17,10 +20,12 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/fs_migration.dart'; import 'package:cake_wallet/entities/fs_migration.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
Future defaultSettingsMigration( Future defaultSettingsMigration(
{@required int version, {@required int version,
@required SharedPreferences sharedPreferences, @required SharedPreferences sharedPreferences,
@required FlutterSecureStorage secureStorage,
@required Box<Node> nodes, @required Box<Node> nodes,
@required Box<WalletInfo> walletInfoSource, @required Box<WalletInfo> walletInfoSource,
@required Box<Trade> tradeSource, @required Box<Trade> tradeSource,
@ -29,9 +34,9 @@ Future defaultSettingsMigration(
await ios_migrate_v1(walletInfoSource, tradeSource, contactSource); await ios_migrate_v1(walletInfoSource, tradeSource, contactSource);
} }
final currentVersion = final currentVersion = sharedPreferences
sharedPreferences.getInt('current_default_settings_migration_version') ?? .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) ??
0; 0;
if (currentVersion >= version) { if (currentVersion >= version) {
return; return;
} }
@ -85,6 +90,10 @@ Future defaultSettingsMigration(
await updateDisplayModes(sharedPreferences); await updateDisplayModes(sharedPreferences);
break; break;
case 9:
await generateBackupPassword(secureStorage);
break;
default: default:
break; break;
} }
@ -230,5 +239,17 @@ Future<void> updateDisplayModes(SharedPreferences sharedPreferences) async {
final currentBalanceDisplayMode = final currentBalanceDisplayMode =
sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey); sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey);
final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2; final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2;
await sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode); await sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode);
}
Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async {
final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
if ((await secureStorage.read(key: key))?.isNotEmpty ?? false) {
return;
}
final password = encrypt.Key.fromSecureRandom(32).base16;
await secureStorage.write(key: key, value: password);
} }

View file

@ -8,7 +8,7 @@ import 'package:cake_wallet/entities/digest_request.dart';
part 'node.g.dart'; part 'node.g.dart';
@HiveType(typeId: 1) @HiveType(typeId: Node.typeId)
class Node extends HiveObject with Keyable { class Node extends HiveObject with Keyable {
Node( Node(
{@required this.uri, {@required this.uri,
@ -26,6 +26,7 @@ class Node extends HiveObject with Keyable {
typeRaw = map['typeRaw'] as int, typeRaw = map['typeRaw'] as int,
useSSL = map['useSSL'] as bool; useSSL = map['useSSL'] as bool;
static const typeId = 1;
static const boxName = 'Nodes'; static const boxName = 'Nodes';
@HiveField(0) @HiveField(0)

View file

@ -14,4 +14,5 @@ class PreferencesKey {
static const displayActionListModeKey = 'display_list_mode'; static const displayActionListModeKey = 'display_list_mode';
static const currentPinLength = 'current_pin_length'; static const currentPinLength = 'current_pin_length';
static const currentLanguageCode = 'language_code'; static const currentLanguageCode = 'language_code';
static const currentDefaultSettingsMigrationVersion = 'current_default_settings_migration_version';
} }

View file

@ -2,10 +2,11 @@ import 'package:hive/hive.dart';
part 'template.g.dart'; part 'template.g.dart';
@HiveType(typeId: 6) @HiveType(typeId: Template.typeId)
class Template extends HiveObject { class Template extends HiveObject {
Template({this.name, this.address, this.cryptoCurrency, this.amount}); Template({this.name, this.address, this.cryptoCurrency, this.amount});
static const typeId = 6;
static const boxName = 'Template'; static const boxName = 'Template';
@HiveField(0) @HiveField(0)

View file

@ -2,10 +2,11 @@ import 'package:hive/hive.dart';
part 'transaction_description.g.dart'; part 'transaction_description.g.dart';
@HiveType(typeId: 2) @HiveType(typeId: TransactionDescription.typeId)
class TransactionDescription extends HiveObject { class TransactionDescription extends HiveObject {
TransactionDescription({this.id, this.recipientAddress, this.transactionNote}); TransactionDescription({this.id, this.recipientAddress, this.transactionNote});
static const typeId = 2;
static const boxName = 'TransactionDescriptions'; static const boxName = 'TransactionDescriptions';
static const boxKey = 'transactionDescriptionsBoxKey'; static const boxKey = 'transactionDescriptionsBoxKey';

View file

@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/wallet_type.dart';
part 'wallet_info.g.dart'; part 'wallet_info.g.dart';
@HiveType(typeId: 4) @HiveType(typeId: WalletInfo.typeId)
class WalletInfo extends HiveObject { class WalletInfo extends HiveObject {
WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight, WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
this.timestamp, this.dirPath, this.path, this.address); this.timestamp, this.dirPath, this.path, this.address);
@ -23,6 +23,7 @@ class WalletInfo extends HiveObject {
date.millisecondsSinceEpoch ?? 0, dirPath, path, address); date.millisecondsSinceEpoch ?? 0, dirPath, path, address);
} }
static const typeId = 4;
static const boxName = 'WalletInfo'; static const boxName = 'WalletInfo';
@HiveField(0) @HiveField(0)

View file

@ -4,8 +4,9 @@ import 'package:hive/hive.dart';
part 'wallet_type.g.dart'; part 'wallet_type.g.dart';
const walletTypes = [WalletType.monero, WalletType.bitcoin]; const walletTypes = [WalletType.monero, WalletType.bitcoin];
const walletTypeTypeId = 5;
@HiveType(typeId: 5) @HiveType(typeId: walletTypeTypeId)
enum WalletType { enum WalletType {
@HiveField(0) @HiveField(0)
monero, monero,

View file

@ -2,7 +2,7 @@ import 'package:hive/hive.dart';
part 'exchange_template.g.dart'; part 'exchange_template.g.dart';
@HiveType(typeId: 7) @HiveType(typeId: ExchangeTemplate.typeId)
class ExchangeTemplate extends HiveObject { class ExchangeTemplate extends HiveObject {
ExchangeTemplate({ ExchangeTemplate({
this.amount, this.amount,
@ -13,6 +13,7 @@ class ExchangeTemplate extends HiveObject {
this.receiveAddress this.receiveAddress
}); });
static const typeId = 7;
static const boxName = 'ExchangeTemplate'; static const boxName = 'ExchangeTemplate';
@HiveField(0) @HiveField(0)

View file

@ -6,7 +6,7 @@ import 'package:cake_wallet/entities/format_amount.dart';
part 'trade.g.dart'; part 'trade.g.dart';
@HiveType(typeId: 3) @HiveType(typeId: Trade.typeId)
class Trade extends HiveObject { class Trade extends HiveObject {
Trade( Trade(
{this.id, {this.id,
@ -27,6 +27,7 @@ class Trade extends HiveObject {
toRaw = to?.raw, toRaw = to?.raw,
stateRaw = state?.raw; stateRaw = state?.raw;
static const typeId = 3;
static const boxName = 'Trades'; static const boxName = 'Trades';
static const boxKey = 'tradesBoxKey'; static const boxKey = 'tradesBoxKey';

View file

@ -1,7 +1,3 @@
import 'package:cake_wallet/core/backup.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -12,6 +8,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cw_monero/wallet.dart' as monero_wallet; import 'package:cw_monero/wallet.dart' as monero_wallet;
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/router.dart' as Router; import 'package:cake_wallet/router.dart' as Router;
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -32,20 +29,46 @@ import 'package:cake_wallet/src/screens/root/root.dart';
final navigatorKey = GlobalKey<NavigatorState>(); final navigatorKey = GlobalKey<NavigatorState>();
void main() async { Future<void> main() async {
try { try {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
final appDir = await getApplicationDocumentsDirectory(); final appDir = await getApplicationDocumentsDirectory();
await Hive.close();
Hive.init(appDir.path); Hive.init(appDir.path);
Hive.registerAdapter(ContactAdapter());
Hive.registerAdapter(NodeAdapter()); if (!Hive.isAdapterRegistered(Contact.typeId)) {
Hive.registerAdapter(TransactionDescriptionAdapter()); Hive.registerAdapter(ContactAdapter());
Hive.registerAdapter(TradeAdapter()); }
Hive.registerAdapter(WalletInfoAdapter());
Hive.registerAdapter(WalletTypeAdapter()); if (!Hive.isAdapterRegistered(Node.typeId)) {
Hive.registerAdapter(TemplateAdapter()); Hive.registerAdapter(NodeAdapter());
Hive.registerAdapter(ExchangeTemplateAdapter()); }
if (!Hive.isAdapterRegistered(TransactionDescription.typeId)) {
Hive.registerAdapter(TransactionDescriptionAdapter());
}
if (!Hive.isAdapterRegistered(Trade.typeId)) {
Hive.registerAdapter(TradeAdapter());
}
if (!Hive.isAdapterRegistered(WalletInfo.typeId)) {
Hive.registerAdapter(WalletInfoAdapter());
}
if (!Hive.isAdapterRegistered(walletTypeTypeId)) {
Hive.registerAdapter(WalletTypeAdapter());
}
if (!Hive.isAdapterRegistered(Template.typeId)) {
Hive.registerAdapter(TemplateAdapter());
}
if (!Hive.isAdapterRegistered(ExchangeTemplate.typeId)) {
Hive.registerAdapter(ExchangeTemplateAdapter());
}
final secureStorage = FlutterSecureStorage(); final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey = await getEncryptionKey( final transactionDescriptionsBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: TransactionDescription.boxKey); secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
@ -57,11 +80,11 @@ void main() async {
TransactionDescription.boxName, TransactionDescription.boxName,
encryptionKey: transactionDescriptionsBoxKey); encryptionKey: transactionDescriptionsBoxKey);
final trades = final trades =
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey); await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName); final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await Hive.openBox<Template>(Template.boxName); final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates = final exchangeTemplates =
await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName); await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
await initialSetup( await initialSetup(
sharedPreferences: await SharedPreferences.getInstance(), sharedPreferences: await SharedPreferences.getInstance(),
nodes: nodes, nodes: nodes,
@ -72,7 +95,8 @@ void main() async {
templates: templates, templates: templates,
exchangeTemplates: exchangeTemplates, exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions, transactionDescriptions: transactionDescriptions,
initialMigrationVersion: 5); secureStorage: secureStorage,
initialMigrationVersion: 9);
runApp(App()); runApp(App());
} catch (e) { } catch (e) {
runApp(MaterialApp( runApp(MaterialApp(
@ -80,7 +104,7 @@ void main() async {
home: Scaffold( home: Scaffold(
body: Container( body: Container(
margin: margin:
EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20), EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
child: Text( child: Text(
'Error:\n${e.toString()}', 'Error:\n${e.toString()}',
style: TextStyle(fontSize: 22), style: TextStyle(fontSize: 22),
@ -88,17 +112,20 @@ void main() async {
} }
} }
Future<void> initialSetup({@required SharedPreferences sharedPreferences, Future<void> initialSetup(
@required Box<Node> nodes, {@required SharedPreferences sharedPreferences,
@required Box<WalletInfo> walletInfoSource, @required Box<Node> nodes,
@required Box<Contact> contactSource, @required Box<WalletInfo> walletInfoSource,
@required Box<Trade> tradesSource, @required Box<Contact> contactSource,
// @required FiatConvertationService fiatConvertationService, @required Box<Trade> tradesSource,
@required Box<Template> templates, // @required FiatConvertationService fiatConvertationService,
@required Box<ExchangeTemplate> exchangeTemplates, @required Box<Template> templates,
@required Box<TransactionDescription> transactionDescriptions, @required Box<ExchangeTemplate> exchangeTemplates,
int initialMigrationVersion = 6}) async { @required Box<TransactionDescription> transactionDescriptions,
FlutterSecureStorage secureStorage,
int initialMigrationVersion = 9}) async {
await defaultSettingsMigration( await defaultSettingsMigration(
secureStorage: secureStorage,
version: initialMigrationVersion, version: initialMigrationVersion,
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
walletInfoSource: walletInfoSource, walletInfoSource: walletInfoSource,
@ -113,7 +140,7 @@ Future<void> initialSetup({@required SharedPreferences sharedPreferences,
templates: templates, templates: templates,
exchangeTemplates: exchangeTemplates, exchangeTemplates: exchangeTemplates,
transactionDescriptionBox: transactionDescriptions); transactionDescriptionBox: transactionDescriptions);
bootstrap(navigatorKey); await bootstrap(navigatorKey);
monero_wallet.onStartup(); monero_wallet.onStartup();
} }
@ -125,16 +152,14 @@ class App extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final settingsStore = getIt
.get<AppStore>()
.settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>();
final initialRoute = authenticationStore.state == AuthenticationState.denied
? Routes.disclaimer
: Routes.login;
return Observer(builder: (BuildContext context) { return Observer(builder: (BuildContext context) {
final settingsStore = getIt.get<AppStore>().settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>();
final initialRoute =
authenticationStore.state == AuthenticationState.denied
? Routes.disclaimer
: Routes.login;
final currentTheme = settingsStore.currentTheme; final currentTheme = settingsStore.currentTheme;
final statusBarBrightness = currentTheme.type == ThemeType.dark final statusBarBrightness = currentTheme.type == ThemeType.dark
? Brightness.light ? Brightness.light

View file

@ -19,14 +19,12 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
final settingsStore = getIt.get<SettingsStore>(); final settingsStore = getIt.get<SettingsStore>();
final fiatConversionStore = getIt.get<FiatConversionStore>(); final fiatConversionStore = getIt.get<FiatConversionStore>();
if (authenticationStore.state == AuthenticationState.uninitialized) { final currentWalletName = getIt
final currentWalletName = getIt .get<SharedPreferences>()
.get<SharedPreferences>() .getString(PreferencesKey.currentWalletName);
.getString(PreferencesKey.currentWalletName); authenticationStore.state = currentWalletName == null
authenticationStore.state = currentWalletName == null ? AuthenticationState.denied
? AuthenticationState.denied : AuthenticationState.installed;
: AuthenticationState.installed;
}
startAuthenticationStateChange(authenticationStore, navigatorKey); startAuthenticationStateChange(authenticationStore, navigatorKey);
startCurrentWalletChangeReaction( startCurrentWalletChangeReaction(

View file

@ -106,9 +106,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
param2: false)); param2: false));
case Routes.restoreOptions: case Routes.restoreOptions:
final type = settings.arguments as WalletType;
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => getIt.get<RestoreOptionsPage>(param1: type)); builder: (_) => getIt.get<RestoreOptionsPage>());
case Routes.restoreWalletOptions: case Routes.restoreWalletOptions:
final type = WalletType.monero; //settings.arguments as WalletType; final type = WalletType.monero; //settings.arguments as WalletType;

View file

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
@ -34,7 +36,7 @@ class BackupPage extends BasePage {
Center( Center(
child: Container( child: Container(
padding: EdgeInsets.only(left: 20, right: 20), padding: EdgeInsets.only(left: 20, right: 20),
height: 225, height: 300,
child: Column(children: [ child: Column(children: [
Text( Text(
'Backup password:', 'Backup password:',
@ -43,9 +45,21 @@ class BackupPage extends BasePage {
Padding( Padding(
padding: EdgeInsets.only(top: 20, bottom: 10), padding: EdgeInsets.only(top: 20, bottom: 10),
child: Observer( child: Observer(
builder: (_) => Text( builder: (_) => GestureDetector(
backupViewModelBase.backupPassword, onTap: () {
style: TextStyle(fontSize: 26), Clipboard.setData(ClipboardData(
text:
backupViewModelBase.backupPassword));
showBar<void>(
context,
S.of(context).transaction_details_copied(
'Backup password'));
},
child: Text(
backupViewModelBase.backupPassword,
style: TextStyle(fontSize: 26),
textAlign: TextAlign.center,
),
))), ))),
Padding( Padding(
padding: EdgeInsets.all(20), padding: EdgeInsets.all(20),

View file

@ -84,5 +84,6 @@ class RestoreFromBackupPage extends BasePage {
} }
await restoreFromBackupViewModel.import(textEditingController.text); await restoreFromBackupViewModel.import(textEditingController.text);
textEditingController.text = '';
} }
} }

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -7,10 +6,9 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class RestoreOptionsPage extends BasePage { class RestoreOptionsPage extends BasePage {
RestoreOptionsPage({@required this.type}); RestoreOptionsPage();
static const _aspectRatioImage = 2.086; static const _aspectRatioImage = 2.086;
final WalletType type;
@override @override
String get title => S.current.restore_restore_wallet; String get title => S.current.restore_restore_wallet;
@ -21,33 +19,30 @@ class RestoreOptionsPage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
return Container( return Container(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
padding: EdgeInsets.all(24), padding: EdgeInsets.all(24),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
RestoreButton( RestoreButton(
onPressed: () => onPressed: () =>
Navigator.pushNamed( Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome),
context, Routes.restoreWalletOptionsFromWelcome), image: imageSeedKeys,
image: imageSeedKeys, title: S.of(context).restore_title_from_seed_keys,
title: S.of(context).restore_title_from_seed_keys, description:
description: S.of(context).restore_description_from_seed_keys S.of(context).restore_description_from_seed_keys),
), Padding(
Padding( padding: EdgeInsets.only(top: 24),
padding: EdgeInsets.only(top: 24), child: RestoreButton(
child: RestoreButton( onPressed: () =>
onPressed: () => Navigator.pushNamed( Navigator.pushNamed(context, Routes.restoreFromBackup),
context, Routes.restoreFromBackup), image: imageBackup,
image: imageBackup, title: S.of(context).restore_title_from_backup,
title: S.of(context).restore_title_from_backup, description: S.of(context).restore_description_from_backup),
description: S.of(context).restore_description_from_backup )
), ],
) ),
], ));
),
)
);
} }
} }

View file

@ -160,7 +160,7 @@ class WelcomePage extends BasePage {
child: PrimaryImageButton( child: PrimaryImageButton(
onPressed: () => onPressed: () =>
Navigator.pushNamed(context, Navigator.pushNamed(context,
Routes.restoreWalletOptionsFromWelcome), Routes.restoreOptions),
image: restoreWalletImage, image: restoreWalletImage,
text: S text: S
.of(context) .of(context)

View file

@ -18,7 +18,7 @@ class TrailButton extends StatelessWidget {
caption, caption,
style: TextStyle( style: TextStyle(
color: color:
Theme.of(context).accentTextTheme.display4.decorationColor, Theme.of(context).accentTextTheme.bodyText2.color,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 14), fontSize: 14),
), ),

View file

@ -85,10 +85,10 @@ abstract class SettingsStoreBase with Store {
(String languageCode) => sharedPreferences.setString( (String languageCode) => sharedPreferences.setString(
PreferencesKey.currentLanguageCode, languageCode)); PreferencesKey.currentLanguageCode, languageCode));
reaction((_) => balanceDisplayMode, reaction(
(BalanceDisplayMode mode) => sharedPreferences.setInt( (_) => balanceDisplayMode,
PreferencesKey.currentBalanceDisplayModeKey, (BalanceDisplayMode mode) => sharedPreferences.setInt(
mode.serialize())); PreferencesKey.currentBalanceDisplayModeKey, mode.serialize()));
this this
.nodes .nodes
@ -158,7 +158,7 @@ abstract class SettingsStoreBase with Store {
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
false; false;
final legacyTheme = final legacyTheme =
(sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false)
? ThemeType.dark.index ? ThemeType.dark.index
: ThemeType.bright.index; : ThemeType.bright.index;
final savedTheme = ThemeList.deserialize( final savedTheme = ThemeList.deserialize(
@ -203,6 +203,29 @@ abstract class SettingsStoreBase with Store {
initialLanguageCode: savedLanguageCode); initialLanguageCode: savedLanguageCode);
} }
Future<void> reload(
{@required Box<Node> nodeSource,
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
TransactionPriority initialTransactionPriority = TransactionPriority.slow,
BalanceDisplayMode initialBalanceDisplayMode =
BalanceDisplayMode.availableBalance}) async {
final settings = await SettingsStoreBase.load(
nodeSource: nodeSource,
initialBalanceDisplayMode: initialBalanceDisplayMode,
initialFiatCurrency: initialFiatCurrency,
initialTransactionPriority: initialTransactionPriority);
fiatCurrency = settings.fiatCurrency;
actionlistDisplayMode = settings.actionlistDisplayMode;
transactionPriority = settings.transactionPriority;
balanceDisplayMode = settings.balanceDisplayMode;
shouldSaveRecipientAddress = settings.shouldSaveRecipientAddress;
allowBiometricalAuthentication = settings.allowBiometricalAuthentication;
currentTheme = settings.currentTheme;
pinCodeLength = settings.pinCodeLength;
languageCode = settings.languageCode;
appVersion = settings.appVersion;
}
Future<void> _saveCurrentNode(Node node, WalletType walletType) async { Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
switch (walletType) { switch (walletType) {
case WalletType.bitcoin: case WalletType.bitcoin:

View file

@ -1,4 +1,4 @@
import 'package:cake_wallet/core/backup.dart'; import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/store/secret_store.dart'; import 'package:cake_wallet/store/secret_store.dart';
@ -52,7 +52,7 @@ abstract class BackupViewModelBase with Store {
Future<BackupExportFile> exportBackup() async { Future<BackupExportFile> exportBackup() async {
try { try {
state = IsExecutingState(); state = IsExecutingState();
final backupContent = await backupService.exportBackup('', nonce: ''); final backupContent = await backupService.exportBackup(backupPassword);
state = ExecutedSuccessfullyState(); state = ExecutedSuccessfullyState();
return BackupExportFile(backupContent.toList(), return BackupExportFile(backupContent.toList(),
@ -60,6 +60,7 @@ abstract class BackupViewModelBase with Store {
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
state = FailureState(e.toString()); state = FailureState(e.toString());
return null;
} }
} }

View file

@ -1,11 +1,17 @@
import 'dart:io'; import 'dart:io';
import 'package:hive/hive.dart';
import 'package:cake_wallet/core/backup.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
part 'restore_from_backup_view_model.g.dart'; part 'restore_from_backup_view_model.g.dart';
class RestoreFromBackupViewModel = RestoreFromBackupViewModelBase with _$RestoreFromBackupViewModel; class RestoreFromBackupViewModel = RestoreFromBackupViewModelBase
with _$RestoreFromBackupViewModel;
abstract class RestoreFromBackupViewModelBase with Store { abstract class RestoreFromBackupViewModelBase with Store {
RestoreFromBackupViewModelBase(this.backupService); RestoreFromBackupViewModelBase(this.backupService);
@ -15,15 +21,36 @@ abstract class RestoreFromBackupViewModelBase with Store {
final BackupService backupService; final BackupService backupService;
@action
void reset() => filePath = '';
Future<void> import(String password) async { Future<void> import(String password) async {
if (filePath?.isEmpty ?? true) { try {
// FIXME: throw exception; if (filePath?.isEmpty ?? true) {
return; // FIXME: throw exception;
return;
}
final file = File(filePath);
final data = await file.readAsBytes();
await backupService.importBackup(data, password);
await main();
final store = getIt.get<AppStore>();
ReactionDisposer reaction;
await store.settingsStore.reload(nodeSource: getIt.get<Box<Node>>());
reaction = autorun((_) {
final wallet = store.wallet;
if (wallet != null) {
store.authenticationStore.state = AuthenticationState.allowed;
reaction?.reaction?.dispose();
}
});
} catch (e) {
print(e.toString());
} }
final file = File(filePath);
final data = await file.readAsBytes();
await backupService.importBackup(data, password, nonce: null);
} }
} }

View file

@ -122,6 +122,19 @@ abstract class SettingsViewModelBase with Store {
onItemSelected: (ThemeBase theme) => onItemSelected: (ThemeBase theme) =>
_settingsStore.currentTheme = theme) _settingsStore.currentTheme = theme)
], ],
[
RegularListItem(
title: 'Backup',
handler: (BuildContext context) {
Navigator.of(context).pushNamed(Routes.auth, arguments:
(bool isAuthenticatedSuccessfully, AuthPageState auth) {
auth.close();
if (isAuthenticatedSuccessfully) {
Navigator.of(context).pushNamed(Routes.backup);
}
});
}),
],
[ [
LinkListItem( LinkListItem(
title: 'Email', title: 'Email',