diff --git a/lib/models/generateWallets.dart b/lib/models/generateWallets.dart index 05e27e1..77f1431 100644 --- a/lib/models/generateWallets.dart +++ b/lib/models/generateWallets.dart @@ -11,6 +11,7 @@ import 'package:sentry_flutter/sentry_flutter.dart' as sentry; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:printing/printing.dart'; +import 'package:truncate/truncate.dart'; class GenerateWalletsProvider with ChangeNotifier { GenerateWalletsProvider(); @@ -20,6 +21,7 @@ class GenerateWalletsProvider with ChangeNotifier { FocusNode walletNameFocus = FocusNode(); Color askedWordColor = Colors.black; bool isAskedWordValid = false; + int nbrWord; String generatedMnemonic; @@ -28,9 +30,23 @@ class GenerateWalletsProvider with ChangeNotifier { TextEditingController mnemonicController = TextEditingController(); TextEditingController pin = TextEditingController(); + // Import wallet + TextEditingController cesiumID = TextEditingController(); + TextEditingController cesiumPWD = TextEditingController(); + TextEditingController cesiumPubkey = TextEditingController(); + bool isCesiumIDVisible = false; + bool isCesiumPWDVisible = false; + bool canImport = false; + bool isPinChanged = false; + Future storeWallet(NewWallet wallet, String _name, BuildContext context, {bool isHD = false}) async { - int nbrWallet = 0; + int nbrWallet; + if (isHD) { + nbrWallet = 0; + } else { + nbrWallet = 1; + } Directory walletNbrDirectory; do { nbrWallet++; @@ -54,13 +70,18 @@ class GenerateWalletsProvider with ChangeNotifier { await configFile .writeAsString('$nbrWallet:$_name:$_derivationNbr:$_pubkey'); + Navigator.pop(context, true); } else { - await configFile.writeAsString('$nbrWallet:$_name'); + final int _derivationNbr = -1; + String _pubkey = await DubpRust.getDewifPublicKey( + dewif: wallet.dewif, + pin: wallet.pin, + ); + await configFile + .writeAsString('$nbrWallet:$_name:$_derivationNbr:$_pubkey'); } Navigator.pop(context, true); - Navigator.pop(context, true); - // notifyListeners(); return _name; } @@ -163,14 +184,17 @@ class GenerateWalletsProvider with ChangeNotifier { return this.actualWallet; } - Future changePinCode() async { - this.actualWallet = await DubpRust.changeDewifPin( - dewif: this.actualWallet.dewif, - oldPin: this.actualWallet.pin, + Future changePinCode({bool reload}) async { + actualWallet = await DubpRust.changeDewifPin( + dewif: actualWallet.dewif, + oldPin: actualWallet.pin, ); - pin.text = this.actualWallet.pin; - // notifyListeners(); + pin.text = actualWallet.pin; + isPinChanged = true; + if (reload) { + notifyListeners(); + } } Future printWallet(String _title) async { @@ -210,4 +234,54 @@ class GenerateWalletsProvider with ChangeNotifier { return pdf.save(); } + + Future generateCesiumWalletPubkey( + String _cesiumID, String _cesiumPWD) async { + actualWallet = await DubpRust.genWalletFromDeprecatedSaltPassword( + salt: _cesiumID, password: _cesiumPWD); + String _walletPubkey = await DubpRust.getLegacyPublicKey( + salt: _cesiumID, password: _cesiumPWD); + + cesiumPubkey.text = _walletPubkey; + print(_walletPubkey); + } + + Future importWallet(context, _cesiumID, _cesiumPWD) async { + String _walletPubkey = await DubpRust.getLegacyPublicKey( + salt: _cesiumID, password: _cesiumPWD); + String shortPubkey = truncate(_walletPubkey, 9, + omission: "...", position: TruncatePosition.end); + await storeWallet( + actualWallet, 'Portefeuille Cesium - $shortPubkey', context); + cesiumID.text = ''; + cesiumPWD.text = ''; + cesiumPubkey.text = ''; + canImport = false; + isPinChanged = false; + pin.text = ''; + isCesiumIDVisible = false; + isCesiumPWDVisible = false; + notifyListeners(); + } + + void cesiumIDisVisible() { + isCesiumIDVisible = !isCesiumIDVisible; + notifyListeners(); + } + + void cesiumPWDisVisible() { + isCesiumPWDVisible = !isCesiumPWDVisible; + notifyListeners(); + } + + void showPinIfEmpty() { + if (!isPinChanged) { + changePinCode(reload: true); + isPinChanged = true; + } + } + + void reloadBuild() { + notifyListeners(); + } } diff --git a/lib/models/myWallets.dart b/lib/models/myWallets.dart index 03d0072..f1b96fb 100644 --- a/lib/models/myWallets.dart +++ b/lib/models/myWallets.dart @@ -23,8 +23,6 @@ class MyWalletsProvider with ChangeNotifier { } } - Future importWallet() async {} - String getAllWalletsNames() { final bool _isWalletsExists = checkIfWalletExist(); if (!_isWalletsExists) { diff --git a/lib/models/walletOptions.dart b/lib/models/walletOptions.dart index 3fd5257..0b708c0 100644 --- a/lib/models/walletOptions.dart +++ b/lib/models/walletOptions.dart @@ -3,7 +3,6 @@ import 'package:dubp/dubp.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'dart:async'; -import 'package:sentry/sentry.dart' as sentry; import 'package:gecko/globals.dart'; class WalletOptionsProvider with ChangeNotifier { @@ -30,26 +29,35 @@ class WalletOptionsProvider with ChangeNotifier { print('Format de code PIN invalide'); return 'false'; } - try { - List _pubkeysTmp = await DubpRust.getBip32DewifAccountsPublicKeys( - dewif: _dewif, secretCode: _pin, accountsIndex: [derivation]); - _pubkey = _pubkeysTmp[0]; - this.pubkey.text = _pubkey; - notifyListeners(); + if (derivation != -1) { + try { + List _pubkeysTmp = await DubpRust.getBip32DewifAccountsPublicKeys( + dewif: _dewif, secretCode: _pin, accountsIndex: [derivation]); + _pubkey = _pubkeysTmp[0]; + this.pubkey.text = _pubkey; + notifyListeners(); - return _pubkey; - } catch (e, stack) { - print('Bad PIN code !'); - print(e); - if (kReleaseMode) { - await sentry.Sentry.captureException( - e, - stackTrace: stack, - ); + return _pubkey; + } catch (e) { + print('Bad PIN code !'); + print(e); + notifyListeners(); + + return 'false'; } - notifyListeners(); + } else { + try { + _pubkey = await DubpRust.getDewifPublicKey(dewif: _dewif, pin: _pin); + this.pubkey.text = _pubkey; + notifyListeners(); + return _pubkey; + } catch (e) { + print('Bad PIN code !'); + print(e); + notifyListeners(); - return 'false'; + return 'false'; + } } } @@ -97,12 +105,17 @@ class WalletOptionsProvider with ChangeNotifier { String newConfig = await _walletConfig.readAsLines().then((List lines) { + int nbrLines = lines.length; int _index = lines.indexOf('$_walletNbr:$_walletName:$_derivation'); - lines.removeWhere( - (element) => element == '$_walletNbr:$_walletName:$_derivation'); - lines.insert(_index, '$_walletNbr:$_newName:$_derivation'); - - return lines.join('\n'); + print(nbrLines); + if (nbrLines != 1) { + lines.removeWhere((element) => + element.contains('$_walletNbr:$_walletName:$_derivation')); + lines.insert(_index, '$_walletNbr:$_newName:$_derivation'); + return lines.join('\n'); + } else { + return '$_walletNbr:$_newName:$_derivation'; + } }); await _walletConfig.delete(); @@ -158,16 +171,21 @@ class WalletOptionsProvider with ChangeNotifier { final _walletConfig = File('${walletsDirectory.path}/$_walletNbr/config.txt'); - String newConfig = - await _walletConfig.readAsLines().then((List lines) { - lines.removeWhere( - (element) => element.contains('$_walletNbr:$_name:$_derivation')); + if (_derivation != -1) { + String newConfig = + await _walletConfig.readAsLines().then((List lines) { + lines.removeWhere( + (element) => element.contains('$_walletNbr:$_name:$_derivation')); - return lines.join('\n'); - }); + return lines.join('\n'); + }); - await _walletConfig.delete(); - await _walletConfig.writeAsString(newConfig); + await _walletConfig.delete(); + await _walletConfig.writeAsString(newConfig); + } else { + final _walletFile = Directory('${walletsDirectory.path}/$_walletNbr'); + await _walletFile.delete(recursive: true); + } Navigator.pop(context); } return 0; diff --git a/lib/screens/myWallets/generateWallets.dart b/lib/screens/myWallets/generateWallets.dart index 6ce90a5..5333173 100644 --- a/lib/screens/myWallets/generateWallets.dart +++ b/lib/screens/myWallets/generateWallets.dart @@ -83,7 +83,8 @@ class GenerateWalletsScreen extends StatelessWidget { icon: Icon(Icons.replay), color: Color(0xffD28928), onPressed: () { - _generateWalletProvider.changePinCode(); + _generateWalletProvider.changePinCode( + reload: false); }, ), ], diff --git a/lib/screens/myWallets/importWallet.dart b/lib/screens/myWallets/importWallet.dart new file mode 100644 index 0000000..2a548af --- /dev/null +++ b/lib/screens/myWallets/importWallet.dart @@ -0,0 +1,240 @@ +import 'dart:async'; +import 'package:flutter/services.dart'; +import 'package:gecko/models/generateWallets.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/myWallets.dart'; +import 'package:gecko/models/walletOptions.dart'; +import 'package:provider/provider.dart'; + +class ImportWalletScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + GlobalKey _toolTipSecret = GlobalKey(); + Timer _debounce; + GenerateWalletsProvider _generateWalletProvider = + Provider.of(context); + MyWalletsProvider _myWalletProvider = + Provider.of(context); + WalletOptionsProvider _walletOptions = + Provider.of(context); + + _generateWalletProvider.showPinIfEmpty(); + + return WillPopScope( + onWillPop: () { + _generateWalletProvider.cesiumID.text = ''; + _generateWalletProvider.cesiumPWD.text = ''; + _generateWalletProvider.cesiumPubkey.text = ''; + _generateWalletProvider.pin.text = ''; + _generateWalletProvider.canImport = false; + _generateWalletProvider.isPinChanged = false; + _generateWalletProvider.isCesiumIDVisible = false; + _generateWalletProvider.isCesiumPWDVisible = false; + _generateWalletProvider.reloadBuild(); + return Future.value(true); + }, + child: Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + _generateWalletProvider.cesiumID.text = ''; + _generateWalletProvider.cesiumPWD.text = ''; + _generateWalletProvider.cesiumPubkey.text = ''; + _generateWalletProvider.pin.text = ''; + _generateWalletProvider.canImport = false; + _generateWalletProvider.isPinChanged = false; + _generateWalletProvider.isCesiumIDVisible = false; + _generateWalletProvider.isCesiumPWDVisible = false; + _generateWalletProvider.reloadBuild(); + Navigator.of(context).pop(); + }), + title: SizedBox( + height: 22, + child: Text('Importer un portefeuille'), + )), + body: Builder( + builder: (ctx) => SafeArea( + child: Column(children: [ + SizedBox(height: 20), + TextFormField( + onChanged: (text) { + if (_debounce?.isActive ?? false) + // _generateWalletProvider.canImport = false; + // _generateWalletProvider.reloadBuild(); + _debounce.cancel(); + _debounce = + Timer(const Duration(milliseconds: 200), () { + print("ID Cesium tappé: $text"); + _generateWalletProvider + .generateCesiumWalletPubkey(text, + _generateWalletProvider.cesiumPWD.text) + .then((value) { + _generateWalletProvider.canImport = true; + _generateWalletProvider.reloadBuild(); + }); + }); + }, + keyboardType: TextInputType.text, + controller: _generateWalletProvider.cesiumID, + obscureText: !_generateWalletProvider + .isCesiumIDVisible, //This will obscure text dynamically + decoration: InputDecoration( + hintText: 'Entrez votre identifiant Cesium', + suffixIcon: IconButton( + icon: Icon( + _generateWalletProvider.isCesiumIDVisible + ? Icons.visibility + : Icons.visibility_off, + color: Colors.black, + ), + onPressed: () { + _generateWalletProvider.cesiumIDisVisible(); + }, + ), + ), + ), + SizedBox(height: 15), + TextFormField( + onChanged: (text) { + if (_debounce?.isActive ?? false) + // _generateWalletProvider.canImport = false; + // _generateWalletProvider.reloadBuild(); + _debounce.cancel(); + _debounce = + Timer(const Duration(milliseconds: 200), () { + print("ID Cesium tappé: $text"); + _generateWalletProvider + .generateCesiumWalletPubkey( + _generateWalletProvider.cesiumID.text, + text) + .then((value) { + _generateWalletProvider.canImport = true; + _generateWalletProvider.reloadBuild(); + }); + }); + }, + keyboardType: TextInputType.text, + controller: _generateWalletProvider.cesiumPWD, + obscureText: !_generateWalletProvider + .isCesiumPWDVisible, //This will obscure text dynamically + decoration: InputDecoration( + hintText: 'Entrez votre mot de passe Cesium', + suffixIcon: IconButton( + icon: Icon( + _generateWalletProvider.isCesiumPWDVisible + ? Icons.visibility + : Icons.visibility_off, + color: Colors.black, + ), + onPressed: () { + _generateWalletProvider.cesiumPWDisVisible(); + }, + ), + ), + ), + SizedBox(height: 15), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData( + text: _generateWalletProvider + .cesiumPubkey.text)); + _walletOptions.snackCopyKey(ctx); + }, + child: Text( + _generateWalletProvider.cesiumPubkey.text, + style: TextStyle( + fontSize: 14.0, + color: Colors.black, + fontWeight: FontWeight.bold, + fontFamily: 'Monospace'), + )), + SizedBox(height: 20), + toolTips(_toolTipSecret, 'Code secret:', + "Retenez bien votre code secret, il vous sera demandé à chaque paiement, ainsi que pour configurer votre portefeuille"), + Container( + child: Stack( + alignment: Alignment.centerRight, + children: [ + TextField( + enabled: false, + controller: _generateWalletProvider.pin, + maxLines: 1, + textAlign: TextAlign.center, + decoration: InputDecoration(), + style: TextStyle( + fontSize: 30.0, + color: Colors.black, + fontWeight: FontWeight.bold)), + IconButton( + icon: Icon(Icons.replay), + color: Color(0xffD28928), + onPressed: () { + _generateWalletProvider.changePinCode( + reload: true); + }, + ), + ], + ), + ), + SizedBox(height: 30), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Color(0xffFFD68E), // background + onPrimary: Colors.black, // foreground + ), + onPressed: _generateWalletProvider.canImport && + _generateWalletProvider.isPinChanged + ? () { + _generateWalletProvider + .importWallet( + context, + _generateWalletProvider + .cesiumID.text, + _generateWalletProvider + .cesiumPWD.text) + .then((value) { + _myWalletProvider.rebuildWidget(); + }); + } + : null, + child: Text('Importer ce portefeuille Cesium', + style: TextStyle(fontSize: 20))), + ]), + )))); + } + + Widget toolTips(_key, _text, _message) { + return GestureDetector( + onTap: () { + final dynamic _toolTip = _key.currentState; + _toolTip.ensureTooltipVisible(); + }, + child: Tooltip( + padding: EdgeInsets.all(10), + key: _key, + showDuration: Duration(seconds: 5), + message: _message, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(width: 20), + Column(children: [ + SizedBox( + width: 30, + height: 25, + child: Icon(Icons.info_outline, + size: 22, color: Color(0xffD28928))), + SizedBox(height: 1) + ]), + Text( + _text, + style: TextStyle( + fontSize: 15.0, + color: Colors.grey[600], + fontWeight: FontWeight.w400), + ), + SizedBox(width: 45) + ]))); + } +} diff --git a/lib/screens/myWallets/walletsHome.dart b/lib/screens/myWallets/walletsHome.dart index c593dd1..e66b0d3 100644 --- a/lib/screens/myWallets/walletsHome.dart +++ b/lib/screens/myWallets/walletsHome.dart @@ -2,6 +2,7 @@ import 'package:gecko/models/myWallets.dart'; import 'package:gecko/models/walletOptions.dart'; import 'package:gecko/screens/myWallets/generateWallets.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/screens/myWallets/importWallet.dart'; import 'package:gecko/screens/myWallets/walletOptions.dart'; import 'package:provider/provider.dart'; @@ -78,7 +79,12 @@ class WalletsHome extends StatelessWidget { primary: Color(0xffFFD68E), // background onPrimary: Colors.black, // foreground ), - onPressed: () => myWalletProvider.importWallet(), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return ImportWalletScreen(); + }), + ), child: Text('Importer un portefeuille existant', style: TextStyle(fontSize: 20))), ])), diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 5d59521..ce0091b 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:dubp/dubp.dart'; import 'package:gecko/models/myWallets.dart'; +import 'package:gecko/screens/myWallets/generateWallets.dart'; import 'dart:io'; +import 'package:gecko/screens/myWallets/importWallet.dart'; + // ignore: must_be_immutable class SettingsScreen extends StatelessWidget { String generatedMnemonic; @@ -28,18 +31,56 @@ class SettingsScreen extends StatelessWidget { child: Text('Paramètres'), )), body: Column(children: [ + SizedBox(height: 40), + SizedBox( + height: 50, + width: 500, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 5, + primary: Color(0xFFFFCA6F), // background + onPrimary: Colors.black, // foreground + ), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return ImportWalletScreen(); + }), + ).then((value) => { + if (value == true) {Navigator.pop(context)} + }), + child: Text("Importer un portefeuille Cesium", + style: TextStyle(fontSize: 15)))), SizedBox(height: 20), + SizedBox( + height: 50, + width: 500, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 5, + primary: Color(0xFFFFCA6F), // background + onPrimary: Colors.black, // foreground + ), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return GenerateWalletsScreen(); + }), + ).then((value) => { + if (value == true) {Navigator.pop(context)} + }), + child: Text("Générer un nouveau trousseau", + style: TextStyle(fontSize: 15)))), Expanded( child: Align( alignment: Alignment.bottomCenter, child: SizedBox( height: 100, - width: 1000, + width: 500, child: ElevatedButton( style: ElevatedButton.styleFrom( elevation: 5, - primary: Colors - .redAccent, //Color(0xffFFD68E), // background + primary: Colors.redAccent, // background onPrimary: Colors.black, // foreground ), onPressed: () async => { diff --git a/native/dubp_rs/src/legacy.rs b/native/dubp_rs/src/legacy.rs index 049b6d1..45aef12 100644 --- a/native/dubp_rs/src/legacy.rs +++ b/native/dubp_rs/src/legacy.rs @@ -49,3 +49,34 @@ pub(super) fn sign(salt: &str, password: &str, msg: &str) -> String { .sign(msg.as_bytes()) .to_base64() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_dewif_legacy() -> Result<(), DubpError> { + let wallet = gen_dewif_from_legacy( + "g1", + "salt".to_owned(), + "pass".to_owned(), + false, + SecretCodeType::Letters, + 1_000_000_000, + )?; + let dewif = &wallet[0]; + let secret_code = &wallet[1]; + let pubkey = &wallet[2]; + + assert_eq!(pubkey, "3YumN7F7D8c2hmkHLHf3ZD8wc3tBHiECEK9zLPkaJtAF"); + + assert_eq!(get_pubkey("salt", "pass"), pubkey.to_owned()); + + assert_eq!( + crate::dewif::get_pubkey(Currency::from(G1_CURRENCY), &dewif, &secret_code)?, + pubkey.to_owned() + ); + + Ok(()) + } +} diff --git a/packages/dubp_rs/lib/dubp.dart b/packages/dubp_rs/lib/dubp.dart index da50c41..d5efb5c 100644 --- a/packages/dubp_rs/lib/dubp.dart +++ b/packages/dubp_rs/lib/dubp.dart @@ -256,8 +256,8 @@ class DubpRust { singleCompletePort(completer, callback: _handleErr); native.get_legacy_pubkey( sendPort.nativePort, - Utf8.toUtf8(password), Utf8.toUtf8(salt), + Utf8.toUtf8(password), ); return completer.future; } diff --git a/pubspec.yaml b/pubspec.yaml index 3d54a60..8b28242 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: A new Flutter project. # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.0.1+4 +version: 0.0.1+5 environment: sdk: ">=2.7.0 <3.0.0"