| 1 | import 'dart:math'; |
| 2 | |
| 3 | import 'package:cw_core/get_height_by_date_zec.dart'; |
| 4 | import 'package:cw_core/pathForWallet.dart'; |
| 5 | import 'package:cw_core/utils/print_verbose.dart'; |
| 6 | import 'package:cw_core/wallet_type.dart'; |
| 7 | import 'package:cw_zcash/cw_zcash.dart'; |
| 8 | import 'package:sqflite_common_ffi/sqflite_ffi.dart'; |
| 9 | import 'package:path/path.dart' as p; |
| 10 | |
| 11 | class ZkoolSeed { |
| 12 | ZkoolSeed({required this.seed, required this.passphrase}); |
| 13 | |
| 14 | factory ZkoolSeed.fromYwalletSeed({required final String ywalletSeed}) { |
| 15 | final splSeed = ywalletSeed.split(" "); |
| 16 | String seed = ""; |
| 17 | ; |
| 18 | String passphrase = ""; |
| 19 | if ([13, 25].contains(splSeed.length)) { |
| 20 | seed = splSeed.take(splSeed.length - 1).join(" "); |
| 21 | passphrase = ywalletSeed.replaceFirst("${seed} ", ""); |
| 22 | } else { |
| 23 | seed = ywalletSeed; |
| 24 | } |
| 25 | return ZkoolSeed(seed: seed, passphrase: passphrase); |
| 26 | } |
| 27 | |
| 28 | String seed; |
| 29 | String passphrase; |
| 30 | } |
| 31 | |
| 32 | class YwalletAccountInfo { |
| 33 | YwalletAccountInfo({ |
| 34 | required this.idAccount, |
| 35 | required this.name, |
| 36 | required this.seed, |
| 37 | required this.aindex, |
| 38 | required this.sk, |
| 39 | required this.ivk, |
| 40 | required this.address, |
| 41 | }); |
| 42 | final int idAccount; |
| 43 | final String name; |
| 44 | final String seed; |
| 45 | late final zkoolSeed = ZkoolSeed.fromYwalletSeed(ywalletSeed: seed); |
| 46 | final int aindex; |
| 47 | final String sk; |
| 48 | final String ivk; |
| 49 | final String address; |
| 50 | static YwalletAccountInfo fromJson(final Map<String, dynamic> j) => YwalletAccountInfo( |
| 51 | idAccount: j['id_account'], |
| 52 | name: j['name'], |
| 53 | seed: j['seed'], |
| 54 | aindex: j['aindex'], |
| 55 | sk: j['sk'], |
| 56 | ivk: j['ivk'], |
| 57 | address: j['address'], |
| 58 | ); |
| 59 | } |
| 60 | |
| 61 | Future<void> migrateOldSqliteToZkool2({required final String walletName}) async { |
| 62 | final dbFile = p.join(await pathForWalletTypeDir(type: WalletType.zcash), "zec.db"); |
| 63 | final migrateDb = await openDatabase(dbFile); |
| 64 | final outputRaw = await migrateDb.rawQuery( |
| 65 | "SELECT `id_account`, `name`, `seed`, `aindex`, `sk`, `ivk`, `address` FROM `accounts`", |
| 66 | ); |
| 67 | final output = outputRaw.map(YwalletAccountInfo.fromJson); |
| 68 | |
| 69 | final walletId = await ZcashWalletBase.getLegacyZcashAccountIdForName(walletName); |
| 70 | for (final ya in output) { |
| 71 | if (ya.idAccount != walletId) { |
| 72 | continue; |
| 73 | } |
| 74 | printV("migrating account: ${ya.name} - ${walletName}"); |
| 75 | int birthHeight = await ZcashHeight.getBlockHeightByTime(DateTime(2026, 1, 1)); |
| 76 | |
| 77 | final accTxs = await migrateDb.rawQuery( |
| 78 | "SELECT height FROM transactions WHERE account = ${ya.idAccount}", |
| 79 | ); |
| 80 | for (final tx in accTxs) { |
| 81 | final txHeight = int.tryParse(tx['height'].toString()) ?? birthHeight; |
| 82 | birthHeight = min(birthHeight, txHeight); |
| 83 | } |
| 84 | |
| 85 | final accountId = await ZcashWalletBase.restoreZcashWalletFromSeed( |
| 86 | name: walletName, |
| 87 | seed: ya.zkoolSeed.seed, |
| 88 | passphrase: ya.zkoolSeed.passphrase, |
| 89 | birthHeight: birthHeight, |
| 90 | ); |
| 91 | await ZcashWalletBase.saveAccountId(walletName, accountId); |
| 92 | return; |
| 93 | } |
| 94 | |
| 95 | throw Exception("migration not finished (wallet name: $walletName not found)"); |
| 96 | } |