keyring.dart 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import 'dart:async';
  2. import 'package:flutter_aes_ecb_pkcs5/flutter_aes_ecb_pkcs5.dart';
  3. import 'package:get_storage/get_storage.dart';
  4. import 'package:polkawallet_sdk/api/apiKeyring.dart';
  5. import 'package:polkawallet_sdk/storage/localStorage.dart';
  6. import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
  7. import 'package:polkawallet_sdk/utils/index.dart';
  8. import 'package:polkawallet_sdk/utils/localStorage.dart';
  9. /// A [Keyring] instance maintains the local storage
  10. /// of key-pairs for users.
  11. /// We need to pass the storage instance to [WalletSDK]'s
  12. /// keyring api for account management.
  13. class Keyring {
  14. late KeyringPrivateStore store;
  15. int? get ss58 => store.ss58;
  16. int? setSS58(int? ss58) {
  17. store.ss58 = ss58;
  18. return ss58;
  19. }
  20. KeyPairData get current {
  21. final list = allAccounts;
  22. if (list.length > 0) {
  23. final i = list.indexWhere((e) => e.pubKey == store.currentPubKey);
  24. return i >= 0 ? list[i] : KeyPairData();
  25. }
  26. return KeyPairData();
  27. }
  28. void setCurrent(KeyPairData acc) {
  29. store.setCurrentPubKey(acc.pubKey);
  30. }
  31. List<KeyPairData> get keyPairs {
  32. return store.list.map((e) => KeyPairData.fromJson(e)).toList();
  33. }
  34. List<KeyPairData> get externals {
  35. return store.externals.map((e) => KeyPairData.fromJson(e)).toList();
  36. }
  37. List<KeyPairData> get contacts {
  38. return store.contacts.map((e) => KeyPairData.fromJson(e)).toList();
  39. }
  40. List<KeyPairData> get allAccounts {
  41. final res = keyPairs;
  42. res.addAll(externals);
  43. return res;
  44. }
  45. List<KeyPairData> get allWithContacts {
  46. final res = keyPairs;
  47. res.addAll(contacts);
  48. return res;
  49. }
  50. List<KeyPairData> get optionals {
  51. final res = allAccounts;
  52. res.removeWhere((e) => e.pubKey == current.pubKey);
  53. return res;
  54. }
  55. Future<void> init(List<int> ss58List) async {
  56. store = KeyringPrivateStore(ss58List);
  57. await store.init();
  58. }
  59. }
  60. class KeyringPrivateStore {
  61. KeyringPrivateStore(this.ss58List);
  62. final KeyringStorage _storage = KeyringStorage();
  63. final LocalStorage _storageOld = LocalStorage();
  64. final List<int> ss58List;
  65. Map<String, Map> _pubKeyAddressMap = {};
  66. Map<String, String> _iconsMap = {};
  67. Map<String, Map> _indicesMap = {};
  68. int? ss58 = 0;
  69. String? get currentPubKey => _storage.currentPubKey.val;
  70. void setCurrentPubKey(String? pubKey) {
  71. _storage.currentPubKey.val = pubKey;
  72. }
  73. List get list {
  74. return _formatAccount(_storage.keyPairs.val.toList());
  75. }
  76. List get externals {
  77. final ls = _storage.contacts.val.toList();
  78. ls.retainWhere((e) => e['observation'] ?? false);
  79. return _formatAccount(ls);
  80. }
  81. List get contacts {
  82. return _formatAccount(_storage.contacts.val.toList());
  83. }
  84. Map<String, Map> get pubKeyAddressMap {
  85. return _pubKeyAddressMap;
  86. }
  87. List _formatAccount(List ls) {
  88. ls.forEach((e) {
  89. final networkSS58 = ss58.toString();
  90. if (_pubKeyAddressMap[networkSS58] != null &&
  91. _pubKeyAddressMap[networkSS58]![e['pubKey']] != null) {
  92. e['address'] = _pubKeyAddressMap[networkSS58]![e['pubKey']];
  93. }
  94. e['icon'] = _iconsMap[e['pubKey']];
  95. e['indexInfo'] = _indicesMap[e['address']];
  96. });
  97. return ls;
  98. }
  99. /// the [GetStorage] package needs to be initiated before use.
  100. Future<void> init() async {
  101. await GetStorage.init(sdk_storage_key);
  102. await _loadKeyPairsFromStorage();
  103. }
  104. /// load keyPairs form local storage to memory.
  105. Future<void> _loadKeyPairsFromStorage() async {
  106. final ls = await _storageOld.getAccountList();
  107. if (ls.length > 0) {
  108. ls.retainWhere((e) {
  109. // delete all storageOld data
  110. _storageOld.removeAccount(e['pubKey']);
  111. if (e['mnemonic'] != null || e['rawSeed'] != null) {
  112. e.remove('mnemonic');
  113. e.remove('rawSeed');
  114. }
  115. // retain accounts from storageOld
  116. final i = _storage.keyPairs.val.indexWhere((pair) {
  117. return pair['pubKey'] == e['pubKey'];
  118. });
  119. return i < 0;
  120. });
  121. final List pairs = _storage.keyPairs.val.toList();
  122. pairs.addAll(ls);
  123. _storage.keyPairs.val = pairs;
  124. // load current account pubKey
  125. final curr = await _storageOld.getCurrentAccount();
  126. if (curr != null && curr.isNotEmpty) {
  127. setCurrentPubKey(curr);
  128. _storageOld.setCurrentAccount('');
  129. }
  130. // and move all encrypted seeds to new storage
  131. _migrateSeeds();
  132. }
  133. }
  134. void updatePubKeyAddressMap(Map<String, Map> data) {
  135. _pubKeyAddressMap = data;
  136. }
  137. void updateIconsMap(Map<String, String> data) {
  138. _iconsMap.addAll(data);
  139. }
  140. void updateIndicesMap(Map<String, Map> data) {
  141. _indicesMap = data;
  142. }
  143. Future<void> addAccount(Map acc) async {
  144. final pairs = _storage.keyPairs.val.toList();
  145. // remove duplicated account and add a new one
  146. pairs.retainWhere((e) => e['pubKey'] != acc['pubKey']);
  147. pairs.add(acc);
  148. _storage.keyPairs.val = pairs;
  149. setCurrentPubKey(acc['pubKey']);
  150. }
  151. Future<void> addContact(Map acc) async {
  152. final ls = _storage.contacts.val.toList();
  153. ls.add(acc);
  154. _storage.contacts.val = ls;
  155. if (acc['observation'] ?? false) {
  156. setCurrentPubKey(acc['pubKey']);
  157. }
  158. }
  159. Future<void> updateAccount(Map acc, {bool isExternal: false}) async {
  160. if (isExternal) {
  161. updateContact(acc);
  162. } else {
  163. _updateKeyPair(acc);
  164. }
  165. }
  166. Future<void> _updateKeyPair(Map acc) async {
  167. final List pairs = _storage.keyPairs.val.toList();
  168. pairs.removeWhere((e) => e['pubKey'] == acc['pubKey']);
  169. pairs.add(acc);
  170. _storage.keyPairs.val = pairs;
  171. }
  172. Future<void> updateContact(Map acc) async {
  173. final ls = _storage.contacts.val.toList();
  174. ls.removeWhere((e) => e['pubKey'] == acc['pubKey']);
  175. ls.add(acc);
  176. _storage.contacts.val = ls;
  177. }
  178. Future<void> deleteAccount(String? pubKey) async {
  179. _deleteKeyPair(pubKey);
  180. final mnemonics = Map.of(_storage.encryptedMnemonics.val);
  181. mnemonics.removeWhere((key, _) => key == pubKey);
  182. _storage.encryptedMnemonics.val = mnemonics;
  183. final seeds = Map.of(_storage.encryptedRawSeeds.val);
  184. seeds.removeWhere((key, _) => key == pubKey);
  185. _storage.encryptedRawSeeds.val = seeds;
  186. }
  187. Future<void> _deleteKeyPair(String? pubKey) async {
  188. final List pairs = _storage.keyPairs.val.toList();
  189. pairs.removeWhere((e) => e['pubKey'] == pubKey);
  190. _storage.keyPairs.val = pairs;
  191. if (pairs.length > 0) {
  192. setCurrentPubKey(pairs[0]['pubKey']);
  193. } else if (externals.length > 0) {
  194. setCurrentPubKey(externals[0]['pubKey']);
  195. } else {
  196. setCurrentPubKey('');
  197. }
  198. }
  199. Future<void> deleteContact(String pubKey) async {
  200. final ls = _storage.contacts.val.toList();
  201. ls.removeWhere((e) => e['pubKey'] == pubKey);
  202. _storage.contacts.val = ls;
  203. }
  204. Future<void> encryptSeedAndSave(
  205. String? pubKey, seed, seedType, password) async {
  206. final String key = Encrypt.passwordToEncryptKey(password);
  207. final String? encrypted = await FlutterAesEcbPkcs5.encryptString(seed, key);
  208. // read old data from storage-old
  209. final Map stored = await (_storageOld.getSeeds(seedType)) ?? {};
  210. stored[pubKey] = encrypted;
  211. // and save to new storage
  212. if (seedType == KeyType.mnemonic.toString().split('.')[1]) {
  213. final mnemonics = Map.from(_storage.encryptedMnemonics.val);
  214. mnemonics.addAll(stored);
  215. _storage.encryptedMnemonics.val = mnemonics;
  216. return;
  217. }
  218. if (seedType == KeyType.rawSeed.toString().split('.')[1]) {
  219. final seeds = Map.from(_storage.encryptedRawSeeds.val);
  220. seeds.addAll(stored);
  221. _storage.encryptedRawSeeds.val = seeds;
  222. }
  223. }
  224. Future<void> updateEncryptedSeed(String? pubKey, passOld, passNew) async {
  225. final seed = await (getDecryptedSeed(pubKey, passOld)) ?? {};
  226. encryptSeedAndSave(pubKey, seed['seed'], seed['type'], passNew);
  227. }
  228. Future<Map<String, dynamic>?> getDecryptedSeed(
  229. String? pubKey, password) async {
  230. final key = Encrypt.passwordToEncryptKey(password);
  231. final mnemonic = _storage.encryptedMnemonics.val[pubKey];
  232. if (mnemonic != null) {
  233. final res = {'type': KeyType.mnemonic.toString().split('.')[1]};
  234. try {
  235. res['seed'] =
  236. (await FlutterAesEcbPkcs5.decryptString(mnemonic, key)) ?? "";
  237. } catch (err) {
  238. print(err);
  239. }
  240. return res;
  241. }
  242. final rawSeed = _storage.encryptedRawSeeds.val[pubKey];
  243. if (rawSeed != null) {
  244. final res = {'type': KeyType.rawSeed.toString().split('.')[1]};
  245. try {
  246. res['seed'] =
  247. (await FlutterAesEcbPkcs5.decryptString(rawSeed, key)) ?? "";
  248. } catch (err) {
  249. print(err);
  250. }
  251. return res;
  252. }
  253. return null;
  254. }
  255. Future<bool> checkSeedExist(KeyType keyType, String pubKey) async {
  256. switch (keyType) {
  257. case KeyType.mnemonic:
  258. return _storage.encryptedMnemonics.val[pubKey] != null;
  259. case KeyType.rawSeed:
  260. return _storage.encryptedRawSeeds.val[pubKey] != null;
  261. default:
  262. return false;
  263. }
  264. }
  265. Future<void> _migrateSeeds() async {
  266. final res = await Future.wait([
  267. _storageOld.getSeeds('mnemonic'),
  268. _storageOld.getSeeds('rawSeed'),
  269. ]);
  270. if (res[0]!.keys.length > 0) {
  271. final mnemonics = Map.of(_storage.encryptedMnemonics.val);
  272. mnemonics.addAll(res[0]!);
  273. _storage.encryptedMnemonics.val = mnemonics;
  274. _storageOld.setSeeds('mnemonic', {});
  275. }
  276. if (res[1]!.keys.length > 0) {
  277. final seeds = Map.of(_storage.encryptedRawSeeds.val);
  278. seeds.addAll(res[1]!);
  279. _storage.encryptedRawSeeds.val = seeds;
  280. _storageOld.setSeeds('rawSeed', {});
  281. }
  282. }
  283. }