Compare commits

...

70 Commits

Author SHA1 Message Date
poka 2e10ee0743 Change version v0.0.1+6 2021-02-17 01:19:48 +01:00
poka 70fbce3621 Merge branch 'UXProfiles' 2021-02-17 01:15:02 +01:00
poka c55b1ed3d4 Change pay button design 2021-02-17 01:13:47 +01:00
poka b69d8d3f2b Big change on profiles UX; Can switch between pay view and history view 2021-02-17 00:15:08 +01:00
poka 5c8fd998af Remove unused search.dart file 2021-02-16 21:28:13 +01:00
poka 3217be291e Search works fine for pubkey; Add checksum to short pubkey format 2021-02-16 17:40:25 +01:00
poka 57cbcd2096 Start to implement search action 2021-02-16 04:57:13 +01:00
poka 16e9f79e14 Change profile basic disposition 2021-02-16 03:55:01 +01:00
poka 1893502e6b Merge branch 'importCesiumWallets' 2021-02-16 03:07:08 +01:00
poka 1522c46256 Settings: Add button to import Cesium wallet, and button to generate new keystore 2021-02-16 03:04:33 +01:00
poka f5d4e2507b Merge pull request 'fix/dubp-legacy-pubkey' (#15) from fix/dubp-legacy-pubkey into importCesiumWallets
Reviewed-on: #15
2021-02-16 00:32:00 +01:00
librelois 03f301a6f5 fix(dubp): get_legacy_pubkey params order 2021-02-16 00:32:00 +01:00
librelois a0013a9c26 tests(dubp): add test for legacy dewif 2021-02-16 00:32:00 +01:00
poka 533f19dabc Can import and use Cesium wallet (salut/password) 2021-02-15 23:57:38 +01:00
poka 2e484d4373 Merge branch 'rebuildHQWorkflow' 2021-02-15 03:59:49 +01:00
poka c4d774e256 All pubkey in monospace everywhere; Fix delete all derivations; Fix delete all pubkey refresh 2021-02-15 03:59:07 +01:00
poka 6891df981e Fix scrollable wallet list 2021-02-15 02:37:00 +01:00
poka f430facfb5 HQ Wallet workflow is complete 2021-02-15 01:44:25 +01:00
poka 5ec6b715b0 Workflow OK for 1 HD wallet and first derivation only 2021-02-14 23:17:03 +01:00
poka 4bbb7e3915 Remove pubkey from HD wallet infos; Rename portefeuille to trousseau 2021-02-14 21:04:58 +01:00
poka 12d2f98672 Merge pull request 'dubp/dewif-meta-data' (#13) from dubp/dewif-meta-data into master
Reviewed-on: #13
2021-02-14 19:50:03 +01:00
librelois 673e2db2ba feat(dubp): add method getDewifMetaData 2021-02-14 17:36:44 +01:00
librelois 2981181817 fix(dubp): master pubkey of HD wallet must not be accessible 2021-02-14 17:14:35 +01:00
poka 2d234e5a33 Use local variables 2021-02-13 01:04:40 +01:00
poka db6aa150b5 Fix errors on http link timeout endpoint not closed 2021-02-13 01:03:05 +01:00
poka 6a96b69bca Remove useless TODO 2021-02-12 21:58:23 +01:00
poka f0294480f3 Merge branch 'improveEndpointSelection' 2021-02-12 21:34:37 +01:00
poka 27ae053af0 Big improve on selection of GVA endpoint at startup 2021-02-12 21:31:42 +01:00
poka 0aa5d8a5af Change String type to list for BIP32 pubkeys reading 2021-02-12 18:57:35 +01:00
poka 2ec8949dde Generate HD wallets; Try to get 3 firsts BIP32 pubkeys of dewif 2021-02-12 18:57:35 +01:00
librelois c09560c9e4 Merge branch 'fix/dubp-dewif-generation-wallet-type-inverted' 2021-02-12 18:55:52 +01:00
librelois 2c9732638f fix(dubp): dewif: wallet type generation inverted 2021-02-12 18:55:03 +01:00
librelois 3b876cf383 Merge branch 'fix/dubp-getBip32DewifAccountsPublicKeys' 2021-02-12 18:24:00 +01:00
librelois d1ed079904 fix(dubp): getBip32DewifAccountsPublicKeys must return a List<String> 2021-02-12 18:23:29 +01:00
librelois 135b2627da Merge branch 'fix/dubp-remove-absolute-path' 2021-02-12 18:17:46 +01:00
poka 48b419213c fix(dubp): remove absolute path 2021-02-12 18:17:40 +01:00
poka 30b326bd5b Add little script to build all rust binaries 2021-02-12 01:56:08 +01:00
poka 2c5237b9bc Merge pull request 'feat(dubp): add transparent BIP32 wallet' (#12) from dubp/transparent-bip32 into master
Reviewed-on: #12
2021-02-12 01:08:22 +01:00
librelois 1a8a24601a feat(dubp): add transparent BIP32 wallet 2021-02-11 20:40:43 +01:00
poka 7284ce8efe Merge pull request 'Document dev environment' (#11) from doc into master
Reviewed-on: #11
2021-02-10 22:32:37 +01:00
Hugo Trentesaux b2314b10e9 Document dev environment 2021-02-10 20:15:21 +01:00
poka 55beb7cecc Merge branch 'feat/getPinLenght' 2021-02-09 23:13:51 +01:00
poka 2a1060850b Implement getPinLenght Sync 2021-02-09 23:11:11 +01:00
poka 5ec1e72030 Merge pull request 'ref(dubp): make getDewifSecretCodeLen synchronous' (#9) from dubp/sync-getDewifSecretCodeLen into master
Reviewed-on: #9
2021-02-09 20:20:47 +01:00
librelois 46ee3ec694 ref(dubp): make getDewifSecretCodeLen synchronous 2021-02-09 17:55:25 +01:00
poka 66b00daae9 Merge pull request 'feat(dubp): add method getDewifSecretCodeLen' (#8) from dubp/getDewifSecretCodeLen into master
Reviewed-on: #8
2021-02-09 00:00:23 +01:00
librelois 7d9c95bb66 feat(dubp): add method getDewifSecretCodeLen 2021-02-08 23:49:31 +01:00
poka f450f0a181 Split walletOptions and changePin provider; Apply change PIN lenght +- 3Go RAM 2021-02-08 13:36:50 +01:00
poka 54a4e0dae3 Use hasPreviousPage correctly to check if we are on beginning of history 2021-02-07 17:25:51 +01:00
poka e625a9bd47 Continue debug GVA cursor 2021-02-07 16:57:50 +01:00
poka 051a898904 Merge branch 'master' of https://git.p2p.legal/axiom-team/gecko 2021-02-07 02:59:55 +01:00
poka 4d9730739c Add debug for history Query state 2021-02-07 02:59:48 +01:00
poka 2138973df8 Merge pull request 'feat(dubp): add method genWalletFromDeprecatedSaltPassword' (#7) from dubp/import-legacy-wallet into master
Reviewed-on: #7
2021-02-06 21:29:26 +01:00
librelois 89f0e174fb feat(dubp): add method genWalletFromDeprecatedSaltPassword 2021-02-06 19:52:42 +01:00
poka d80b1190ae Peer-programming with Hugo: Copy pubkeys on tap and display notif for it. Improve pubkeys fonts 2021-02-06 08:31:14 +01:00
poka 3bf3f85b9c Continue debug Query GQL Widget 2021-02-04 21:13:02 +01:00
poka 6afd5b01ff WIP: Debug double execution of Query GQL Widget 2021-02-04 21:00:37 +01:00
poka 5934744884 Refix transcation history amount parsing 2021-02-03 20:59:12 +01:00
poka aa2c6671ee Merge remote-tracking branch 'origin/master' 2021-02-03 20:05:15 +01:00
poka c279066a5a Fix transaction amount parsing for multi-receivers 2021-02-03 20:04:42 +01:00
poka 26a2c978a9 Fix transaction amount parding for multi receivers 2021-02-03 18:45:08 +01:00
poka 231a82a470 Add duniter-g1.p2p.legal endpoint to list; update script build-apk 2021-02-03 00:50:58 +01:00
poka 4499f8c4fc Merge pull request 'add legacy ard adapt secret code len' (#6) from dubp into master
Reviewed-on: #6
2021-02-02 22:59:17 +01:00
librelois 7eff91c5dc feat(dubp):add methods getLegacyPublicKey & signLegacy for legacy wallet
legacy wallet is a wallet generated with deprecated (salt+password) process
2021-02-02 22:59:17 +01:00
librelois 1bc74f25f8 feat(dubp): adapt secret code len depending on system memory 2021-02-02 22:59:17 +01:00
librelois 61eca576f2 ref(dubp): make gen_mnemonic really async 2021-02-02 22:59:17 +01:00
poka 91bf7a653e Add Cs+ data 2021-02-02 20:33:09 +01:00
poka 6e744f3289 Mise à jour de 'README.md' 2021-01-30 20:34:17 +01:00
poka 8d0a3696dc Change demo gif 2021-01-30 19:17:48 +01:00
poka bfd5c666a8 Improve node selection; Notification for selected node 2021-01-30 19:00:31 +01:00
42 changed files with 2731 additions and 893 deletions

2
.gitignore vendored
View File

@ -54,3 +54,5 @@ packages/dubp_rs/lib/ffi.dart
# Rust things
/target
pubkeys.txt

18
Cargo.lock generated
View File

@ -222,9 +222,9 @@ dependencies = [
[[package]]
name = "cryptoxide"
version = "0.2.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da24927b5b899890bcb29205436c957b7892ec3a3fbffce81d710b9611e77778"
checksum = "f2824e117f942b77e942b14162316711d66f07d14b0f37a44fa4b4cad592b8fa"
[[package]]
name = "dart-bindgen"
@ -261,9 +261,9 @@ dependencies = [
[[package]]
name = "dup-crypto"
version = "0.36.0"
version = "0.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ed9887f92b29910736ad29f5631abcbd8fc6b2bd2dd5510dc1edd32b0265d04"
checksum = "e69f621e9575ed2647fb67a9e8a8d8d5cc8d9281dec2fc848c7619a159501562"
dependencies = [
"aes",
"arrayvec",
@ -272,6 +272,7 @@ dependencies = [
"bs58",
"byteorder",
"cryptoxide",
"ed25519-bip32",
"getrandom",
"once_cell",
"ring",
@ -281,6 +282,15 @@ dependencies = [
"zeroize",
]
[[package]]
name = "ed25519-bip32"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8827180a2b511141fbe49141e50b31a8d542465e0fb572f81f36feea2addfe92"
dependencies = [
"cryptoxide",
]
[[package]]
name = "fast-threadpool"
version = "0.3.0"

View File

@ -4,4 +4,7 @@ members = ["native/dubp_rs"]
[profile.release]
lto = true
codegen-units = 1
debug = true
debug = true
[patch.crates-io]
#dup-crypto = { path = "/home/elois/dev/duniter/libs/dubp-rs-libs/crypto" }

View File

@ -1,19 +1,67 @@
# Ğecko
Pay with Ğ1.
Ğecko is a transaction client written in Dart. It is fast and secure thanks to native code compilation, Rust cryptography bindings and Duniter's new GraphQL API (GVA). It is not intended to manage member accounts, but rather simple wallets.
The development is quite early, you can participate in the discussion [on the Duniter forum](https://forum.duniter.org/t/gecko-nouveau-client-de-paiements-1-sur-mobile-en-cours-de-developpement-dart-flutter/7857) (mostly FR)
## Getting Started
Work in progress.
<br><br><br>
<div align="center">
![Demo Gif](https://git.p2p.legal/axiom-team/gecko/raw/branch/master/assets/Demo-0.0.0+8.gif)
![Demo Gif](https://git.p2p.legal/axiom-team/gecko/raw/branch/master/assets/Demo-0.0.1+0.gif)
<br><br>
![Foo](https://git.p2p.legal/axiom-team/gecko/raw/commit/1cd2d63fe02949edabb69aa5fc498512c01db416/images/art/bb_gecko.png)
</div>
</div>
## Develop
To contribute to the code, we advise you to install the following development environment.
1. Android Studio
- Android VM
- Android NDK
1. Flutter SDK
1. VSCode/Codium Flutter extension
1. Rust toolchain
This will take about 12GB on your drive and 30 min of your time (with a good connection). Don't hesitate to ask on the forum for a peer-coding session if you are stuck.
### Android Studio
Android Studio will let you set up an Android VM and install tools you need.
- Install [Android Studio](https://developer.android.com/studio/) using your favorite installation method.
- At startup, do not open a project but click "configure" at the bottom of the "Welcome" menu
- In "SDK Manager"
- SDK Platforms Ttab
- note your SDK folder location (later used for Rust environment variables)
- select Android 11 (R) API level 30 (default)
- SDK Tools
- select NDK (native development kit used to compile Rust to native target)
- In "AVD Manager"
- create a virtual machine (ours is Pixel 4 32bits machine)
- launch it in the emulator
If you reach this point without trouble, you're good to go for the next step.
### Flutter SDK
Flutter is a powerfull SDK to develop Android apps. [Install it](https://flutter.dev/docs/get-started/install/linux) with your favorite installation method.
### VSCode
We are using VSCode and therefore document the process for this IDE. Of course you're free to use whatever you want.
Clone the ğecko repo and open a dart file (e.g. `lib/main.dart`). VSCode will suggest you to insall relevant extensions.
### Rust toolchain
After installing Rust with the standard [rustup install script](https://www.rust-lang.org/tools/install),
follow the instructions to build Rust dependencies [in this separate README](./packages/dubp_rs/README.md).
If you installed a 32bits VM, use the 32bits build command.
### Build the app
In a dart file (e.g. `lib/main.dart`), type the `F5` key to build the code. The app should open automatically in your VM which is running.

BIN
assets/Demo-0.0.1+0.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

BIN
assets/icon_user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,4 +1,5 @@
[
"https://g1.librelois.fr/gva",
"http://localhost:30901/gva"
"https://g1.librelois.fr/gva",
"https://duniter-gva.axiom-team.fr/gva",
"https://duniter-g1.p2p.legal/gva"
]

View File

@ -5,3 +5,5 @@ Directory appPath;
Directory walletsDirectory;
String appVersion;
SharedPreferences prefs;
String endPointGVA;
int ramSys;

View File

@ -1,5 +1,7 @@
import 'package:dubp/dubp.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/cesiumPlus.dart';
import 'package:gecko/models/changePin.dart';
import 'package:gecko/models/generateWallets.dart';
import 'package:gecko/models/history.dart';
import 'package:gecko/models/home.dart';
@ -24,23 +26,14 @@ Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
HomeProvider _homeProvider = HomeProvider();
await _homeProvider.getAppPath();
await _homeProvider.createDefaultAvatar();
appVersion = await _homeProvider.getAppVersion();
prefs = await SharedPreferences.getInstance();
final HiveStore _store =
await HiveStore.open(path: '${appPath.path}/gqlCache');
String randomEndpoint; // = await getRandomEndpoint();
int i = 0;
do {
if (i >= 5) {
print('NO VALID ENDPOINT FOUND !');
break;
}
if (i != 0) {
print(i.toString() + ' ème essai de recherche de endpoint GVA.');
await Future.delayed(Duration(milliseconds: 300));
}
randomEndpoint = await _homeProvider.getRandomEndpoint();
i++;
} while (randomEndpoint == 'HS');
// Get a valid GVA endpoint
endPointGVA = await _homeProvider.getValidEndpoint();
if (kReleaseMode && enableSentry) {
await SentryFlutter.init(
@ -48,32 +41,30 @@ Future<void> main() async {
options.dsn =
'https://c09587b46eaa42e8b9fda28d838ed180@o496840.ingest.sentry.io/5572110';
},
appRunner: () => runApp(Gecko(randomEndpoint)),
appRunner: () => runApp(Gecko(endPointGVA, _store)),
);
} else {
print('Debug mode enabled: No sentry alerte');
runApp(Gecko(
randomEndpoint,
));
runApp(Gecko(endPointGVA, _store));
}
}
// ignore: must_be_immutable
class Gecko extends StatelessWidget {
Gecko(this.randomEndpoint);
Gecko(this.randomEndpoint, this._store);
final String randomEndpoint;
final HiveStore _store;
@override
Widget build(BuildContext context) {
final _httpLink = HttpLink(
// 'http://192.168.1.91:10060/gva',
randomEndpoint,
);
final _client = ValueNotifier(
GraphQLClient(
cache: GraphQLCache(store: null),
cache: GraphQLCache(store: _store),
link: _httpLink,
),
);
@ -86,7 +77,9 @@ class Gecko extends StatelessWidget {
ChangeNotifierProvider(create: (_) => HistoryProvider('')),
ChangeNotifierProvider(create: (_) => MyWalletsProvider()),
ChangeNotifierProvider(create: (_) => GenerateWalletsProvider()),
ChangeNotifierProvider(create: (_) => WalletOptionsProvider())
ChangeNotifierProvider(create: (_) => WalletOptionsProvider()),
ChangeNotifierProvider(create: (_) => ChangePinProvider()),
ChangeNotifierProvider(create: (_) => CesiumPlusProvider())
],
child: GraphQLProvider(
client: _client,

124
lib/models/cesiumPlus.dart Normal file
View File

@ -0,0 +1,124 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
class CesiumPlusProvider with ChangeNotifier {
// String pubkey = '';
// CesiumPlusProvider(this.pubkey);
var decodedBytes;
var avatar64;
TextEditingController cesiumName = TextEditingController();
int iAvatar = 0;
bool isComplete = false;
Future<List> _buildQuery(_pubkey) async {
var queryGetAvatar = json.encode({
"query": {
"bool": {
"should": [
{
"match": {
'_id': {"query": _pubkey, "boost": 2}
}
},
{
"prefix": {'_id': _pubkey}
}
]
}
},
"highlight": {
"fields": {"title": {}, "tags": {}}
},
"from": 0,
"size": 100,
"_source": [
"title",
"avatar",
"avatar._content_type",
"description",
"city",
"address",
"socials.url",
"creationTime",
"membersCount",
"type"
],
"indices_boost": {"user": 100, "page": 1, "group": 0.01}
});
String cesiumPod = "https://g1.data.le-sou.org";
String requestUrl = "/user,page,group/profile,record/_search";
String podRequest = cesiumPod + requestUrl;
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
return [podRequest, queryGetAvatar, headers];
}
Future<String> getName(String _pubkey) async {
String _name;
List queryOptions = await _buildQuery(_pubkey);
final response = await http.post(queryOptions[0],
body: queryOptions[1], headers: queryOptions[2]);
// print('RESULT CESIUM QUERY: ${response.body}'); //For debug
final responseJson = json.decode(response.body);
if (responseJson['hits']['hits'].toString() == '[]') {
return '';
}
final bool _nameExist =
responseJson['hits']['hits'][0]['_source'].containsKey("title");
if (!_nameExist) {
return '';
}
_name = responseJson['hits']['hits'][0]['_source']['title'];
print(_name);
return _name;
}
Future<List> getAvatar(String _pubkey) async {
List queryOptions = await _buildQuery(_pubkey);
final response = await http.post(queryOptions[0],
body: queryOptions[1], headers: queryOptions[2]);
// print('RESULT CESIUM QUERY: ${response.body}'); //For debug
final responseJson = json.decode(response.body);
if (responseJson['hits']['hits'].toString() == '[]') {
return [File(appPath.path + '/default_avatar.png')];
}
final bool avatarExist =
responseJson['hits']['hits'][0]['_source'].containsKey("avatar");
if (!avatarExist) {
return [File(appPath.path + '/default_avatar.png')];
}
final _avatar =
responseJson['hits']['hits'][0]['_source']['avatar']['_content'];
var avatarFile =
File('${(await getTemporaryDirectory()).path}/avatar$iAvatar.png');
// final bool _isAvatarExist = await avatarFile.exists();
// if (_isAvatarExist) {
// await avatarFile.delete();
// }
await avatarFile.writeAsBytes(base64.decode(_avatar));
iAvatar++;
isComplete = true;
return [avatarFile];
}
// isNameComplete() {
// while (isComplete == false) {
// print(isComplete);
// Duration(milliseconds: 50);
// }
// }
}

45
lib/models/changePin.dart Normal file
View File

@ -0,0 +1,45 @@
import 'dart:io';
import 'package:dubp/dubp.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:gecko/globals.dart';
class ChangePinProvider with ChangeNotifier {
bool ischangedPin = false;
TextEditingController newPin = new TextEditingController();
Future<NewWallet> get badWallet => null;
Future<NewWallet> changePin(_name, _oldPin) async {
try {
final _walletFile = Directory('${walletsDirectory.path}/$_name');
final _dewif =
File(_walletFile.path + '/wallet.dewif').readAsLinesSync()[0];
NewWallet newWalletFile = await DubpRust.changeDewifPin(
dewif: _dewif,
oldPin: _oldPin,
);
newPin.text = newWalletFile.pin;
ischangedPin = true;
notifyListeners();
return newWalletFile;
} catch (e) {
print('Impossible de changer le code PIN.');
return badWallet;
}
}
Future storeWallet(context, _name, _newWalletFile) async {
final Directory walletNameDirectory =
Directory('${walletsDirectory.path}/$_name');
final walletFile = File('${walletNameDirectory.path}/wallet.dewif');
print(_newWalletFile);
walletFile.writeAsString('${_newWalletFile.dewif}');
Navigator.pop(context);
return _name;
}
}

View File

@ -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,34 +21,67 @@ class GenerateWalletsProvider with ChangeNotifier {
FocusNode walletNameFocus = FocusNode();
Color askedWordColor = Colors.black;
bool isAskedWordValid = false;
int nbrWord;
String generatedMnemonic;
bool walletIsGenerated = true;
TextEditingController mnemonicController = TextEditingController();
TextEditingController pubkey = TextEditingController();
TextEditingController pin = TextEditingController();
Future storeWallet(NewWallet wallet, _name, BuildContext context) async {
final Directory walletNameDirectory =
Directory('${walletsDirectory.path}/$_name');
final walletFile = File('${walletNameDirectory.path}/wallet.dewif');
final walletPubkey = File('${walletNameDirectory.path}/pubkey');
// Import wallet
TextEditingController cesiumID = TextEditingController();
TextEditingController cesiumPWD = TextEditingController();
TextEditingController cesiumPubkey = TextEditingController();
bool isCesiumIDVisible = false;
bool isCesiumPWDVisible = false;
bool canImport = false;
bool isPinChanged = false;
if (await walletNameDirectory.exists()) {
print('Ce wallet existe déjà, impossible de le créer.');
_showWalletExistDialog(context);
return 'Exist: DENY';
Future storeWallet(NewWallet wallet, String _name, BuildContext context,
{bool isHD = false}) async {
int nbrWallet;
if (isHD) {
nbrWallet = 0;
} else {
nbrWallet = 1;
}
Directory walletNbrDirectory;
do {
nbrWallet++;
walletNbrDirectory = Directory('${walletsDirectory.path}/$nbrWallet');
} while (await walletNbrDirectory.exists());
final walletFile = File('${walletNbrDirectory.path}/wallet.dewif');
await walletNbrDirectory.create();
await walletFile.writeAsString(wallet.dewif);
final configFile = File('${walletNbrDirectory.path}/config.txt');
if (isHD) {
final int _derivationNbr = 3;
List _pubkeysTmp = await DubpRust.getBip32DewifAccountsPublicKeys(
dewif: wallet.dewif,
secretCode: wallet.pin,
accountsIndex: [_derivationNbr]);
String _pubkey = _pubkeysTmp[0];
await configFile
.writeAsString('$nbrWallet:$_name:$_derivationNbr:$_pubkey');
Navigator.pop(context, true);
} else {
final int _derivationNbr = -1;
String _pubkey = await DubpRust.getDewifPublicKey(
dewif: wallet.dewif,
pin: wallet.pin,
);
await configFile
.writeAsString('$nbrWallet:$_name:$_derivationNbr:$_pubkey');
}
await walletNameDirectory.create();
await walletFile.writeAsString('${wallet.dewif}');
await walletPubkey.writeAsString('${wallet.publicKey}');
Navigator.pop(context, true);
Navigator.pop(context, wallet.publicKey);
// notifyListeners();
return _name;
}
@ -107,35 +141,6 @@ class GenerateWalletsProvider with ChangeNotifier {
notifyListeners();
}
Future<void> _showWalletExistDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Ce nom existe déjà'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Veuillez choisir un autre nom pour votre portefeuille.'),
],
),
),
actions: <Widget>[
TextButton(
child: Text("J'ai compris"),
onPressed: () {
Navigator.of(context).pop();
askedWordColor = Colors.green[500];
isAskedWordValid = true;
},
),
],
);
},
);
}
Future<String> generateMnemonic() async {
try {
generatedMnemonic = await DubpRust.genMnemonic(language: Language.french);
@ -160,7 +165,8 @@ class GenerateWalletsProvider with ChangeNotifier {
this.actualWallet = await DubpRust.genWalletFromMnemonic(
language: Language.french,
mnemonic: generatedMnemonic,
secretCodeType: SecretCodeType.letters);
secretCodeType: SecretCodeType.letters,
walletType: WalletType.bip32Ed25519);
} catch (e, stack) {
print(e);
if (kReleaseMode) {
@ -172,24 +178,26 @@ class GenerateWalletsProvider with ChangeNotifier {
}
mnemonicController.text = generatedMnemonic;
pubkey.text = this.actualWallet.publicKey;
pin.text = this.actualWallet.pin;
// notifyListeners();
return this.actualWallet;
}
Future<void> changePinCode() async {
this.actualWallet = await DubpRust.changeDewifPin(
dewif: this.actualWallet.dewif,
oldPin: this.actualWallet.pin,
Future<void> 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<Uint8List> printWallet(String _title, String _pubkey) async {
Future<Uint8List> printWallet(String _title) async {
final ByteData fontData =
await rootBundle.load("assets/OpenSans-Regular.ttf");
final pw.Font ttf = pw.Font.ttf(fontData.buffer.asByteData());
@ -203,12 +211,6 @@ class GenerateWalletsProvider with ChangeNotifier {
pageFormat: PdfPageFormat.a4,
build: (context) {
return pw.Column(children: <pw.Widget>[
pw.Text("Clé publique:",
style: pw.TextStyle(fontSize: 20, font: ttf)),
pw.SizedBox(height: 10),
pw.Text(_pubkey,
style: pw.TextStyle(fontSize: 15, font: ttf),
textAlign: pw.TextAlign.center),
pw.SizedBox(height: 20),
pw.Text("Phrase de restauration:",
style: pw.TextStyle(fontSize: 20, font: ttf)),
@ -232,4 +234,54 @@ class GenerateWalletsProvider with ChangeNotifier {
return pdf.save();
}
Future<void> 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();
}
}

View File

@ -1,19 +1,27 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sentry/sentry.dart' as sentry;
import 'package:qrscan/qrscan.dart' as scanner;
import 'dart:math';
import 'package:intl/intl.dart';
import 'package:truncate/truncate.dart';
import 'package:crypto/crypto.dart';
import 'package:fast_base58/fast_base58.dart';
class HistoryProvider with ChangeNotifier {
// String pubkey = 'D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU'; // For debug
String pubkey = '';
String pubkeyShort = '';
HistoryProvider(this.pubkey);
final TextEditingController _outputPubkey = new TextEditingController();
bool isTheEnd = false;
final TextEditingController outputPubkey = TextEditingController();
List transBC;
bool isFirstBuild = true;
String fetchMoreCursor;
Map pageInfo;
bool isHistoryScreen = false;
String historySwitchButtun = "Voir l'historique";
Future scan() async {
await Permission.camera.request();
@ -31,7 +39,7 @@ class HistoryProvider with ChangeNotifier {
return 'false';
}
if (barcode != null) {
this._outputPubkey.text = barcode;
this.outputPubkey.text = barcode;
isPubkey(barcode);
} else {
return 'false';
@ -52,7 +60,13 @@ class HistoryProvider with ChangeNotifier {
print("C'est une pubkey !!!");
this.pubkey = pubkey;
this._outputPubkey.text = pubkey;
getShortPubkey(pubkey);
this.outputPubkey.text = pubkey;
print(pubkeyShort);
isHistoryScreen = false;
historySwitchButtun = "Voir l'historique";
notifyListeners();
return pubkey;
@ -61,7 +75,29 @@ class HistoryProvider with ChangeNotifier {
return '';
}
List parseHistory(txs) {
String getShortPubkey(String pubkey) {
List<int> pubkeyByte = Base58Decode(pubkey);
Digest pubkeyS256 = sha256.convert(sha256.convert(pubkeyByte).bytes);
String pubkeyCheksum = Base58Encode(pubkeyS256.bytes);
String pubkeyChecksumShort = truncate(pubkeyCheksum, 3,
omission: "", position: TruncatePosition.end);
pubkeyShort = truncate(pubkey, 5,
omission: String.fromCharCode(0x2026),
position: TruncatePosition.end) +
truncate(pubkey, 4, omission: "", position: TruncatePosition.start) +
':$pubkeyChecksumShort';
return pubkeyShort;
}
// Pi: D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU // For debug
// Boris: JE6mkuzSpT3ePciCPRTpuMT9fqPUVVLJz2618d33p7tn
// Matograine portefeuille: 9p5nHsES6xujFR7pw2yGy4PLKKHgWsMvsDHaHF64Uj25.
// Lion simone: 78jhpprYkMNF6i5kQPXfkAVBpd2aqcpieNsXTSW4c21f
List parseHistory(txs, _pubkey) {
// print(txs);
var transBC = [];
int i = 0;
@ -71,7 +107,19 @@ class HistoryProvider with ChangeNotifier {
for (final trans in txs) {
var direction = trans['direction'];
final transaction = trans['node'];
var output = transaction['outputs'][0];
var output;
if (direction == "RECEIVED") {
for (String line in transaction['outputs']) {
if (line.contains(_pubkey)) {
output = line;
}
}
} else {
output = transaction['outputs'][0];
}
if (output == null) {
continue;
}
transBC.add(i);
transBC[i] = [];
@ -91,11 +139,13 @@ class HistoryProvider with ChangeNotifier {
num amountUD = amount / currentUD;
if (direction == "RECEIVED") {
transBC[i].add(transaction['issuers'][0]);
transBC[i].add(getShortPubkey(transaction['issuers'][0]));
transBC[i].add(amount.toString());
transBC[i].add(amountUD.toStringAsFixed(2));
} else if (direction == "SENT") {
final outPubkey = output.split("SIG(")[1].replaceAll(')', '');
transBC[i].add(outPubkey);
transBC[i].add(getShortPubkey(outPubkey));
transBC[i].add('- ' + amount.toString());
transBC[i].add(amountUD.toStringAsFixed(2));
}
@ -106,13 +156,15 @@ class HistoryProvider with ChangeNotifier {
return transBC;
}
FetchMoreOptions checkQueryResult(result, opts) {
FetchMoreOptions checkQueryResult(result, opts, _pubkey) {
final List<dynamic> blockchainTX =
(result.data['txsHistoryBc']['both']['edges'] as List<dynamic>);
final Map pageInfo = result.data['txsHistoryBc']['both']['pageInfo'];
pageInfo = result.data['txsHistoryBc']['both']['pageInfo'];
final String fetchMoreCursor = pageInfo['endCursor'];
fetchMoreCursor = pageInfo['endCursor'];
print('hasPreviousPage: ' + pageInfo['hasPreviousPage'].toString());
print('hasNextPage: ' + pageInfo['hasNextPage'].toString());
if (fetchMoreCursor != null) {
opts = FetchMoreOptions(
@ -134,26 +186,58 @@ class HistoryProvider with ChangeNotifier {
print(
"###### DEBUG H Parse blockchainTX list. Cursor: $fetchMoreCursor ######");
if (fetchMoreCursor != null) {
transBC = parseHistory(blockchainTX);
isTheEnd = false;
transBC = parseHistory(blockchainTX, _pubkey);
} else {
print("###### DEBUG H - Début de l'historique");
isTheEnd = true;
}
return opts;
}
void snackNode(context) {
if (isFirstBuild) {
String _message;
if (endPointGVA == 'HS') {
_message =
"Aucun noeud Duniter disponible, veuillez réessayer ultérieurement";
} else {
_message = "Vous êtes connecté au noeud\n${endPointGVA.split('/')[2]}";
}
final snackBar =
SnackBar(content: Text(_message), duration: Duration(seconds: 2));
Scaffold.of(context).showSnackBar(snackBar);
isFirstBuild = false;
}
}
void resetdHistory() {
this._outputPubkey.text = '';
this.outputPubkey.text = '';
notifyListeners();
}
num removeDecimalZero(double n) {
String result = n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 1);
String result = n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 2);
return num.parse(result);
}
snackCopyKey(context) {
final snackBar = SnackBar(
content:
Text("Cette clé publique a été copié dans votre presse-papier."),
duration: Duration(seconds: 2));
Scaffold.of(context).showSnackBar(snackBar);
}
void switchProfileView() {
isHistoryScreen = !isHistoryScreen;
if (isHistoryScreen) {
historySwitchButtun = "Payer";
} else {
historySwitchButtun = "Voir l'historique";
}
notifyListeners();
}
// num getBalance(_pubkey) {
// getBalance(_pubkey);
// }

View File

@ -1,15 +1,20 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:gecko/globals.dart';
import 'package:package_info/package_info.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
class HomeProvider with ChangeNotifier {
int _currentIndex = 0;
bool isSearching;
Icon searchIcon = Icon(Icons.search);
final TextEditingController searchQuery = new TextEditingController();
Widget appBarTitle = Text('Ğecko', style: TextStyle(color: Colors.grey[850]));
get currentIndex => _currentIndex;
@ -18,7 +23,7 @@ class HomeProvider with ChangeNotifier {
notifyListeners();
}
Future getAppVersion() async {
Future<String> getAppVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
@ -27,28 +32,54 @@ class HomeProvider with ChangeNotifier {
return version + '+' + buildNumber;
}
Future<String> getRandomEndpoint() async {
// TODO: Improve implemention of getRandomEndpoint()
// final _json = json.decode(await getJsonEndpoints());
// print('JSON !! :');
// print(_json);
// final _list = _json[];
Future<String> getValidEndpoint() async {
List _listEndpoints = await rootBundle
.loadString('config/gva_endpoints.json')
.then((jsonStr) => jsonDecode(jsonStr));
_listEndpoints.shuffle();
int i = 0;
String _endpoint;
int _statusCode = 0;
final _client = new HttpClient();
_client.connectionTimeout = const Duration(milliseconds: 800);
do {
i++;
print(i.toString() + ' ème essai de recherche de endpoint GVA.');
print('Try GVA endpoint: ${_listEndpoints[i]}');
if (i > 2) {
print('NO VALID GVA ENDPOINT FOUND');
_endpoint = 'HS';
break;
}
if (i != 0) {
await Future.delayed(const Duration(milliseconds: 300));
}
try {
final request = await _client.postUrl(Uri.parse(_listEndpoints[i]));
final response = await request.close();
_endpoint = _listEndpoints[i];
_statusCode = response.statusCode;
} on TimeoutException catch (_) {
print('This endpoint is timeout, next');
_statusCode = 50;
continue;
} on SocketException catch (_) {
print('This endpoint is a bad endpoint, next');
_statusCode = 70;
continue;
} on Exception {
print('Unknown error');
_statusCode = 60;
continue;
}
} while (_statusCode != 400);
// final _listEndpoints = ['https://g1.librelois.fr/gva'];
final _listEndpoints = [
'https://g1.librelois.fr/gva',
'https://duniter-gva.axiom-team.fr/gva'
];
final _endpoint = getRandomElement(_listEndpoints);
print('ENDPOINT: ' + _endpoint);
// http.post(_endpoint);
final response = await http.post(_endpoint);
if (response.statusCode != 400) {
print('Endpoint statutcode: ' + response.statusCode.toString());
return 'HS';
}
return _endpoint;
}
@ -63,9 +94,36 @@ class HomeProvider with ChangeNotifier {
}
}
Future createDefaultAvatar() async {
File defaultAvatar = File(appPath.path + '/default_avatar.png');
final bool isAvatarExist = await defaultAvatar.exists();
if (!isAvatarExist) {
final byteData = await rootBundle.load('assets/icon_user.png');
await defaultAvatar.writeAsBytes(byteData.buffer
.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
}
}
T getRandomElement<T>(List<T> list) {
final random = new Random();
final random = Random();
var i = random.nextInt(list.length);
return list[i];
}
void handleSearchStart() {
isSearching = true;
notifyListeners();
}
void handleSearchEnd() {
searchIcon = Icon(
Icons.search,
color: Colors.grey[850],
);
appBarTitle = Text('Ğecko', style: TextStyle(color: Colors.grey[850]));
isSearching = false;
searchQuery.clear();
notifyListeners();
}
}

View File

@ -6,7 +6,7 @@ import 'package:gecko/globals.dart';
import 'package:provider/provider.dart';
class MyWalletsProvider with ChangeNotifier {
Map listWallets = Map();
String listWallets;
bool checkIfWalletExist() {
if (appPath == null) {
@ -23,30 +23,33 @@ class MyWalletsProvider with ChangeNotifier {
}
}
Future importWallet() async {}
Map getAllWalletsNames() {
print(listWallets);
if (listWallets.isNotEmpty) {
listWallets.clear();
String getAllWalletsNames() {
final bool _isWalletsExists = checkIfWalletExist();
if (!_isWalletsExists) {
return '';
}
if (listWallets != null && listWallets.isNotEmpty) {
listWallets = '';
}
if (listWallets == null) {
listWallets = '';
}
print(walletsDirectory.path);
// int i = 0;
walletsDirectory
.listSync(recursive: false, followLinks: false)
.forEach((wallet) {
String _name = wallet.path.split('/').last;
List _pubkeyList = File(wallet.path + '/pubkey').readAsLinesSync();
String _pubkey = _pubkeyList[0];
print("$_name: $_pubkey");
listWallets[_name] = _pubkey;
// i++;
// for (var _wallets in listWallets) {
// _wallets.pubkey =
// }
.forEach((_wallet) {
File _walletConfig = File('${_wallet.path}/config.txt');
_walletConfig.readAsLinesSync().forEach((element) {
if (listWallets != '') {
listWallets += '\n';
}
listWallets += element;
// listWallets += "${element.split(':')[0]}:${element.split(':')[1]}:${element.split(':')[2]}"
});
});
return listWallets;
}
@ -59,6 +62,7 @@ class MyWalletsProvider with ChangeNotifier {
if (_answer) {
await walletsDirectory.delete(recursive: true);
await walletsDirectory.create();
notifyListeners();
Navigator.pop(context);
}
return 0;
@ -75,8 +79,8 @@ class MyWalletsProvider with ChangeNotifier {
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
return AlertDialog(
title: Text(
'Êtes-vous sûr de vouloir supprimer tous vos portefeuilles ?'),
title:
Text('Êtes-vous sûr de vouloir supprimer tous vos trousseaux ?'),
content: SingleChildScrollView(child: Text('')),
actions: <Widget>[
TextButton(
@ -102,6 +106,30 @@ class MyWalletsProvider with ChangeNotifier {
);
}
Future<void> generateNewDerivation(
context, String _name, int _walletNbr) async {
int _newDerivationNbr;
final _walletConfig =
File('${walletsDirectory.path}/$_walletNbr/config.txt');
if (await _walletConfig.readAsString() == '') {
_newDerivationNbr = 3;
} else {
String _lastWallet =
await _walletConfig.readAsLines().then((value) => value.last);
int _lastDerivation = int.parse(_lastWallet.split(':')[2]);
_newDerivationNbr = _lastDerivation + 3;
}
await _walletConfig.writeAsString('\n$_walletNbr:$_name:$_newDerivationNbr',
mode: FileMode.append);
print(await _walletConfig.readAsString());
notifyListeners();
Navigator.pop(context);
}
void rebuildWidget() {
notifyListeners();
}

View File

@ -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 {
@ -15,7 +14,8 @@ class WalletOptionsProvider with ChangeNotifier {
Future<NewWallet> get badWallet => null;
Future _getPubkeyFromDewif(_dewif, _pin) async {
Future _getPubkeyFromDewif(
String _dewif, _pin, int _pinLenght, int derivation) async {
String _pubkey;
RegExp regExp = new RegExp(
r'^[A-Z0-9]+$',
@ -23,42 +23,55 @@ class WalletOptionsProvider with ChangeNotifier {
multiLine: false,
);
if (regExp.hasMatch(_pin) == true && _pin.length == 6) {
if (regExp.hasMatch(_pin) == true && _pin.length == _pinLenght) {
print("Le format du code PIN est correct.");
} else {
print('Format de code PIN invalide');
return 'false';
}
try {
_pubkey = await DubpRust.getDewifPublicKey(dewif: _dewif, pin: _pin);
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';
}
}
}
Future readLocalWallet(String _name, String _pin) async {
Future readLocalWallet(int _walletNbr, String _name, String _pin,
int _pinLenght, int derivation) async {
isWalletUnlock = false;
print('NOM: ' + _name);
try {
File _walletFile = File('${walletsDirectory.path}/$_name/wallet.dewif');
File _walletFile =
File('${walletsDirectory.path}/$_walletNbr/wallet.dewif');
String _localDewif = await _walletFile.readAsString();
String _localPubkey;
if ((_localPubkey = await _getPubkeyFromDewif(_localDewif, _pin)) !=
if ((_localPubkey = await _getPubkeyFromDewif(
_localDewif, _pin, _pinLenght, derivation)) !=
'false') {
this.pubkey.text = _localPubkey;
isWalletUnlock = true;
@ -75,17 +88,43 @@ class WalletOptionsProvider with ChangeNotifier {
}
}
Future _renameWallet(_walletName, _newName) async {
final _walletFile = Directory('${walletsDirectory.path}/$_walletName');
int getPinLenght(_walletNbr) {
File _walletFile =
File('${walletsDirectory.path}/$_walletNbr/wallet.dewif');
String _localDewif = _walletFile.readAsStringSync();
try {
_walletFile.rename('${walletsDirectory.path}/$_newName');
} catch (e) {
print('ERREUR lors du renommage du wallet: $e');
}
final int _pinLenght = DubpRust.getDewifSecretCodeLen(
dewif: _localDewif, secretCodeType: SecretCodeType.letters);
return _pinLenght;
}
Future<bool> renameWalletAlerte(context, _walletName) async {
Future _renameWallet(_walletName, _newName, _walletNbr, _derivation) async {
final _walletConfig =
File('${walletsDirectory.path}/$_walletNbr/config.txt');
String newConfig =
await _walletConfig.readAsLines().then((List<String> lines) {
int nbrLines = lines.length;
int _index = lines.indexOf('$_walletNbr:$_walletName:$_derivation');
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();
await _walletConfig.writeAsString(newConfig);
_newWalletName.text = '';
}
Future<bool> renameWalletAlerte(
context, _walletName, _walletNbr, _derivation) async {
return showDialog<bool>(
context: context,
barrierDismissible: true, // user must tap button!
@ -111,12 +150,12 @@ class WalletOptionsProvider with ChangeNotifier {
TextButton(
child: Text("Valider"),
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
_renameWallet(_walletName, this._newWalletName.text);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _renameWallet(_walletName, this._newWalletName.text,
_walletNbr, _derivation);
});
// notifyListeners();
Navigator.pop(context, true);
Navigator.pop(context, true);
},
),
],
@ -125,21 +164,31 @@ class WalletOptionsProvider with ChangeNotifier {
);
}
Future<int> deleteWallet(context, _name) async {
try {
final _walletFile = Directory('${walletsDirectory.path}/$_name');
print('DELETE THAT ?: $_walletFile');
Future<int> deleteWallet(context, _walletNbr, _name, _derivation) async {
final bool _answer = await _confirmDeletingWallet(context, _name);
final bool _answer = await _confirmDeletingWallet(context, _name);
if (_answer) {
final _walletConfig =
File('${walletsDirectory.path}/$_walletNbr/config.txt');
if (_answer) {
if (_derivation != -1) {
String newConfig =
await _walletConfig.readAsLines().then((List<String> lines) {
lines.removeWhere(
(element) => element.contains('$_walletNbr:$_name:$_derivation'));
return lines.join('\n');
});
await _walletConfig.delete();
await _walletConfig.writeAsString(newConfig);
} else {
final _walletFile = Directory('${walletsDirectory.path}/$_walletNbr');
await _walletFile.delete(recursive: true);
Navigator.pop(context);
}
return 0;
} catch (e) {
return 1;
Navigator.pop(context);
}
return 0;
}
Future<bool> _confirmDeletingWallet(context, _walletName) async {
@ -153,8 +202,7 @@ class WalletOptionsProvider with ChangeNotifier {
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'Vous pourrez restaurer ce portefeuille à tout moment grace à votre phrase de restauration.'),
Text('Vous pourrez restaurer ce portefeuille plus tard.'),
],
),
),
@ -202,11 +250,18 @@ class WalletOptionsProvider with ChangeNotifier {
final Directory walletNameDirectory =
Directory('${walletsDirectory.path}/$_name');
final walletFile = File('${walletNameDirectory.path}/wallet.dewif');
print(_newWalletFile);
walletFile.writeAsString('${_newWalletFile.dewif}');
Navigator.pop(context);
return _name;
}
snackCopyKey(context) {
final snackBar = SnackBar(
content:
Text("Cette clé publique a été copié dans votre presse-papier."),
duration: Duration(seconds: 2));
Scaffold.of(context).showSnackBar(snackBar);
}
}

View File

@ -1,4 +1,8 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/cesiumPlus.dart';
import 'package:gecko/models/queries.dart';
import 'package:gecko/models/history.dart';
import 'package:flutter/material.dart';
@ -6,7 +10,6 @@ import 'package:flutter/foundation.dart';
import 'dart:ui';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
import 'package:truncate/truncate.dart';
// ignore: must_be_immutable
class HistoryScreen extends StatelessWidget with ChangeNotifier {
@ -16,6 +19,8 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
// HistoryProvider _historyProvider;
final _formKey = GlobalKey<FormState>();
FocusNode _pubkeyFocus = FocusNode();
List cesiumData;
final double avatarsSize = 80;
FetchMore fetchMore;
FetchMoreOptions opts;
@ -25,6 +30,11 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
this._outputPubkey.text = _historyProvider.pubkey;
print('Build pubkey : ' + _historyProvider.pubkey);
// _historyProvider.snackNode(context);
WidgetsBinding.instance.addPostFrameCallback((_) {
_historyProvider.snackNode(context);
});
return Scaffold(
floatingActionButton: Container(
height: 80.0,
@ -45,37 +55,19 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
),
),
body: Column(children: <Widget>[
SizedBox(height: 8),
TextField(
autofocus: false,
focusNode: _pubkeyFocus,
// Entrée de la pubkey
onChanged: (text) {
print("Clé tappxé: $text");
_historyProvider.isPubkey(text);
},
controller: this._outputPubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Tappez/Collez une clé publique, ou scannez',
hintStyle: TextStyle(fontSize: 14),
contentPadding:
EdgeInsets.symmetric(horizontal: 7, vertical: 15),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
),
style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
if (_historyProvider.pubkey != '') historyQuery(context),
SizedBox(height: 0),
if (_historyProvider.pubkey != '')
historyQuery(context, _historyProvider),
]));
}
historyQuery(context) {
Widget historyQuery(context, HistoryProvider _historyProvider) {
_pubkeyFocus.unfocus();
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
// HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
CesiumPlusProvider _cesiumPlusProvider =
Provider.of<CesiumPlusProvider>(context);
print("I'M HERE 1");
bool _isFirstExec = true;
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@ -90,8 +82,12 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
'cursor': null
},
),
builder: (QueryResult result, {refetch, fetchMore}) {
builder: (QueryResult result, {fetchMore, refetch}) {
print("I'M HERE 2 ! $_isFirstExec");
// print(result.source.isEager);
if (result.isLoading && result.data == null) {
print("I'M HERE 3 !");
return const Center(
child: CircularProgressIndicator(),
);
@ -115,7 +111,10 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
final num balance = _historyProvider
.removeDecimalZero(result.data['balance']['amount'] / 100);
opts = _historyProvider.checkQueryResult(result, opts);
opts = _historyProvider.checkQueryResult(
result, opts, _outputPubkey.text);
// _historyProvider.transBC = null;
// Build history list
return NotificationListener(
@ -123,40 +122,120 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
child: ListView(
controller: scrollController,
children: <Widget>[
SizedBox(height: 15),
SizedBox(height: 20),
if (_historyProvider.pubkey != '')
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(width: 70.0, height: 0.0),
Text(balance.toString() + ' Ğ1',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30.0)),
if (_isFirstExec)
Container(
padding:
const EdgeInsets.fromLTRB(12, 0, 5, 0),
child: FutureBuilder(
future: _cesiumPlusProvider
.getAvatar(_historyProvider.pubkey),
initialData: [
File(appPath.path +
'/default_avatar.png')
],
builder: (BuildContext context,
AsyncSnapshot<List> _avatar) {
cesiumData = _avatar.data;
// _cesiumPlusProvider.isComplete = true;
if (_avatar.connectionState !=
ConnectionState.done) {
return Image.file(
File(appPath.path +
'/default_avatar.png'),
height: avatarsSize);
}
if (_avatar.hasError) {
return Image.file(
File(appPath.path +
'/default_avatar.png'),
height: avatarsSize);
}
if (_avatar.hasData) {
return SingleChildScrollView(
padding: EdgeInsets.all(0.0),
child: Image.file(_avatar.data[0],
height: avatarsSize));
}
return Image.file(
File(appPath.path +
'/default_avatar.png'),
height: avatarsSize);
})),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: _historyProvider.pubkey));
_historyProvider.snackCopyKey(context);
},
child: Text(_historyProvider.pubkeyShort,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w800,
fontFamily: 'Monospace')),
),
Container(
padding: const EdgeInsets.only(right: 15),
child: IconButton(
icon: Icon(Icons.payments),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return paymentPopup(context);
});
},
iconSize: 30,
color: Color(0xFFB16E16)))
padding: const EdgeInsets.fromLTRB(
30, 0, 5, 0), // .only(right: 15),
child: Text('TODO')),
SizedBox(width: 0)
]),
SizedBox(height: 15),
const Divider(
color: Colors.grey,
height: 5,
thickness: 0.5,
indent: 0,
endIndent: 0,
),
_historyProvider.transBC == null
? Text('Aucune transaction à afficher.')
: loopTransactions(context, result),
if (_isFirstExec)
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
// padding: const EdgeInsets.,
child: FutureBuilder(
future: _cesiumPlusProvider
.getName(_historyProvider.pubkey),
initialData: '...',
builder: (context, snapshot) {
return Text(
snapshot.data != ''
? snapshot.data
: '-',
style: TextStyle(fontSize: 20));
}))
]),
SizedBox(height: 18),
if (_isFirstExec)
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Text(balance.toString() + ' Ğ1',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18.0))),
SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 1,
primary: Colors.grey[50], // background
onPrimary: Colors.black, // foreground
),
onPressed: () {
_historyProvider.switchProfileView();
},
child: Text(_historyProvider.historySwitchButtun,
style: TextStyle(
fontSize: 15, color: Color(0xffD28928)))),
// const Divider(
// color: Colors.grey,
// height: 5,
// thickness: 0.5,
// indent: 0,
// endIndent: 0,
// ),
_historyProvider.isHistoryScreen
? historyView(context, result)
: payView(context),
],
)),
onNotification: (t) {
@ -173,103 +252,114 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier {
));
}
Widget loopTransactions(context, result) {
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
Widget payView(context) {
TextEditingController payComment = new TextEditingController();
return Column(children: <Widget>[
for (var repository in _historyProvider.transBC)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
contentPadding: const EdgeInsets.all(5.0),
leading: Text(repository[1].toString(),
style: TextStyle(
fontSize: 12,
color: Colors.grey[800],
fontWeight: FontWeight.w700),
textAlign: TextAlign.center),
title: Text(repository[5],
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.center),
subtitle: Text(
truncate(repository[2], 20,
omission: "...", position: TruncatePosition.end),
style: TextStyle(fontSize: 11.0),
textAlign: TextAlign.center),
trailing: Text("${repository[3]} Ğ1",
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.justify),
dense: true,
isThreeLine: false,
onTap: () {
// this._outputPubkey.text = repository[2];
_historyProvider.isPubkey(repository[2]);
})),
if (result.isLoading)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
),
if (_historyProvider.isTheEnd)
Column(children: <Widget>[
SizedBox(height: 15),
Text("Début de l'historique.",
textAlign: TextAlign.center, style: TextStyle(fontSize: 20)),
SizedBox(height: 15)
])
]);
}
Widget paymentPopup(context) {
return AlertDialog(
content: Stack(
overflow: Overflow.visible,
children: <Widget>[
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('À:'),
Padding(
return Stack(
overflow: Overflow.visible,
children: <Widget>[
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 20),
Text('Commentaire:'),
Padding(
padding: EdgeInsets.all(8.0),
child: Text(this._outputPubkey.text,
child: TextField(
controller: payComment,
maxLines: 2,
textAlign: TextAlign.center,
style:
TextStyle(fontSize: 15, fontWeight: FontWeight.w500)),
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold))),
SizedBox(height: 20),
Text('Montant (Ğ1):'),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
textAlign: TextAlign.center,
maxLines: 1,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'(^\d*\.?\d*)'))
],
),
SizedBox(height: 20),
Text('Montant (Ğ1):'),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
textAlign: TextAlign.center,
autofocus: true,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'(^\d*\.?\d*)'))
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
child: Text("Payer"),
color: Color(0xffFFD68E),
),
Padding(
padding: const EdgeInsets.only(top: 15),
child: OutlineButton(
borderSide: BorderSide(width: 2, color: Color(0xffD28928)),
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
}
},
),
)
],
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Text("PAYER",
style: TextStyle(
fontSize: 25, color: Colors.grey[850]))),
))
],
),
],
),
),
],
);
}
Widget historyView(context, result) {
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
return _historyProvider.transBC == null
? Text('Aucune transaction à afficher.')
: Column(children: <Widget>[
for (var repository in _historyProvider.transBC)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: ListTile(
contentPadding: const EdgeInsets.all(5.0),
leading: Text(repository[1].toString(),
style: TextStyle(
fontSize: 12,
color: Colors.grey[800],
fontWeight: FontWeight.w700),
textAlign: TextAlign.center),
title: Text(repository[3],
style: TextStyle(
fontSize: 15.0, fontFamily: 'Monospace'),
textAlign: TextAlign.center),
subtitle: Text(repository[6] != '' ? repository[6] : '-',
style: TextStyle(fontSize: 12.0),
textAlign: TextAlign.center),
trailing: Text("${repository[4]} Ğ1",
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.justify),
dense: true,
isThreeLine: false,
onTap: () {
// this._outputPubkey.text = repository[2];
_historyProvider.isPubkey(repository[2]);
})),
if (result.isLoading)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
),
// if (_historyProvider.isTheEnd) // What I did before ...
if (!_historyProvider.pageInfo['hasPreviousPage'])
Column(children: <Widget>[
SizedBox(height: 15),
Text("Début de l'historique.",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20)),
SizedBox(height: 15)
])
]);
}
}

View File

@ -1,4 +1,5 @@
import 'package:gecko/globals.dart';
import 'package:gecko/models/history.dart';
import 'package:gecko/models/home.dart';
import 'package:gecko/screens/history.dart';
import 'package:flutter/material.dart';
@ -14,6 +15,7 @@ class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
HomeProvider _homeProvider = Provider.of<HomeProvider>(context);
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
return Scaffold(
drawer: Drawer(
child: Column(
@ -65,12 +67,51 @@ class HomeScreen extends StatelessWidget {
icon: new Icon(Icons.menu, color: Colors.grey[850]),
onPressed: () => Scaffold.of(context).openDrawer(),
)),
title: Text('Ğecko', style: TextStyle(color: Colors.grey[850])),
title: _homeProvider.appBarTitle,
actions: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.search, color: Colors.grey[850]),
),
padding: EdgeInsets.symmetric(horizontal: 16),
child: IconButton(
icon: _homeProvider.searchIcon,
color: Colors.grey[850],
onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) {
// return SearchList();
// }),
// );
if (_homeProvider.searchIcon.icon == Icons.search) {
_homeProvider.searchIcon = Icon(
Icons.close,
color: Colors.grey[850],
);
_homeProvider.appBarTitle = TextField(
autofocus: true,
controller: _homeProvider.searchQuery,
onChanged: (text) {
print("Clé tappé: $text");
final String searchResult =
_historyProvider.isPubkey(text);
if (searchResult != '') {
_homeProvider.currentIndex = 0;
}
},
style: TextStyle(
color: Colors.grey[850],
),
decoration: InputDecoration(
prefixIcon:
Icon(Icons.search, color: Colors.grey[850]),
hintText: "Rechercher ...",
hintStyle: TextStyle(color: Colors.grey[850])),
);
_homeProvider.handleSearchStart();
} else {
_homeProvider.handleSearchEnd();
}
}))
],
backgroundColor: Color(0xffFFD58D),
),

View File

@ -1,7 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'package:gecko/models/walletOptions.dart';
import 'package:gecko/models/changePin.dart';
import 'dart:io';
import 'package:provider/provider.dart';
@ -17,68 +17,85 @@ class ChangePinScreen extends StatelessWidget with ChangeNotifier {
@override
Widget build(BuildContext context) {
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context);
_walletOptions.changePin(walletName, oldPin);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: SizedBox(
height: 22,
child: Text(walletName),
)),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 80),
Text(
'Choisissez un code secret autogénéré :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(height: 30),
Container(
child: Stack(
alignment: Alignment.centerRight,
children: <Widget>[
TextField(
enabled: true,
controller: _walletOptions.newPin,
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: () async {
_newWalletFile =
await _walletOptions.changePin(walletName, oldPin);
},
ChangePinProvider _changePin = Provider.of<ChangePinProvider>(context);
// _walletOptions.changePin(walletName, oldPin);
// _walletOptions.newPin.text = _tmpPin;
return WillPopScope(
onWillPop: () {
_changePin.newPin.text = '';
return Future<bool>.value(true);
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
_changePin.newPin.text = '';
Navigator.of(context).pop();
}),
title: SizedBox(
height: 22,
child: Text(walletName),
)),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 80),
Text(
'Choisissez un code secret autogénéré :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(height: 30),
Container(
child: Stack(
alignment: Alignment.centerRight,
children: <Widget>[
TextField(
enabled: true,
controller: _changePin.newPin,
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: () async {
_newWalletFile =
await _changePin.changePin(walletName, oldPin);
},
),
],
),
],
),
),
SizedBox(height: 30),
SizedBox(
width: 200,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 12,
primary: Colors.green[400], //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => _walletOptions.storeWallet(
context, walletName, _newWalletFile),
child: Text('Confirmer', style: TextStyle(fontSize: 28))),
)
]))));
),
SizedBox(height: 30),
SizedBox(
width: 200,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 12,
primary:
Colors.green[400], //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: _changePin.newPin.text != ''
? () {
_changePin.newPin.text = '';
_changePin.storeWallet(
context, walletName, _newWalletFile);
}
: null,
child: Text('Confirmer', style: TextStyle(fontSize: 28))),
)
])))));
}
}

View File

@ -17,7 +17,6 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
NewWallet generatedWallet;
TextEditingController _mnemonicController = TextEditingController();
TextEditingController _pubkey = TextEditingController();
TextEditingController _inputRestoreWord = TextEditingController();
TextEditingController walletName = TextEditingController();
FocusNode _wordFocus = FocusNode();
@ -30,7 +29,6 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
Provider.of<MyWalletsProvider>(context);
this._mnemonicController.text = generatedMnemonic;
this._pubkey.text = generatedWallet.publicKey;
return WillPopScope(
onWillPop: () {
_generateWalletProvider.isAskedWordValid = false;
@ -49,38 +47,21 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
}),
title: SizedBox(
height: 22,
child: Text('Confirmez ce portefeuille'),
child: Text('Enregistrer ce trousseau'),
)),
body: Center(
child: Column(children: <Widget>[
SizedBox(height: 15),
Text(
'Votre clé publique est :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextField(
enabled: false,
controller: this._pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text(
'Quel est le ${_generateWalletProvider.nbrWord + 1}ème mot de votre phrase de restauration ?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
Container(
width: 360,
child: Text(
'Quel est le ${_generateWalletProvider.nbrWord + 1}ème mot de votre phrase de restauration ?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
)),
TextFormField(
focusNode: _wordFocus,
autofocus: true,
@ -99,14 +80,16 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
color: _generateWalletProvider.askedWordColor,
fontWeight: FontWeight.w500)),
SizedBox(height: 12),
Text(
'Choisissez un nom pour votre portefeuille :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
Container(
width: 360,
child: Text(
'Choisissez un nom pour votre premier portefeuille :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
)),
TextFormField(
focusNode: _generateWalletProvider.walletNameFocus,
inputFormatters: [
@ -145,7 +128,8 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
await _generateWalletProvider.storeWallet(
generatedWallet,
walletName.text,
context);
context,
isHD: true);
_generateWalletProvider.isAskedWordValid =
false;
_generateWalletProvider.askedWordColor =

View File

@ -13,7 +13,6 @@ class GenerateWalletsScreen extends StatelessWidget {
String currentText = "";
var pinColor = Colors.grey[300];
GlobalKey _toolTipPubkey = GlobalKey();
GlobalKey _toolTipSentence = GlobalKey();
GlobalKey _toolTipSecret = GlobalKey();
@ -28,7 +27,7 @@ class GenerateWalletsScreen extends StatelessWidget {
appBar: AppBar(
title: SizedBox(
height: 22,
child: Text('Générer un portefeuille'),
child: Text('Générer un trousseau'),
)),
floatingActionButton: Container(
height: 80.0,
@ -45,102 +44,91 @@ class GenerateWalletsScreen extends StatelessWidget {
backgroundColor: Color(
0xffEFEFBF), //Color(0xffFFD68E), //Color.fromARGB(500, 204, 255, 255),
))),
body: SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 20),
toolTips(_toolTipPubkey, 'Clé publique:',
"C'est votre RIB en Ğ1, les gens l'utiliseront pour vous payer"),
TextField(
enabled: false,
controller: _generateWalletProvider.pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
SizedBox(height: 8),
toolTips(_toolTipSentence, 'Phrase de restauration:',
"Notez et gardez cette phrase précieusement sur un papier, elle vous servira à restaurer votre portefeuille sur un autre appareil"),
TextField(
enabled: false,
controller: _generateWalletProvider.mnemonicController,
maxLines: 3,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
),
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.w400)),
SizedBox(height: 8),
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: <Widget>[
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();
},
),
],
),
),
SizedBox(height: 20),
new ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: _generateWalletProvider.walletIsGenerated
? () {
_generateWalletProvider.nbrWord =
_generateWalletProvider.getRandomInt();
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmStoreWallet(
generatedMnemonic:
_generateWalletProvider.generatedMnemonic,
generatedWallet:
_generateWalletProvider.actualWallet);
}),
);
}
: null,
child: Text('Enregistrer ce portefeuille',
style: TextStyle(fontSize: 20))),
SizedBox(height: 20),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return PrintWallet(
_generateWalletProvider.generatedMnemonic,
_generateWalletProvider.actualWallet.publicKey);
}),
);
},
child: Icon(Icons.print))
]),
));
body: Builder(
builder: (ctx) => SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 20),
toolTips(_toolTipSentence, 'Phrase de restauration:',
"Notez et gardez cette phrase précieusement sur un papier, elle vous servira à restaurer votre portefeuille sur un autre appareil"),
TextField(
enabled: false,
controller: _generateWalletProvider.mnemonicController,
maxLines: 3,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
),
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.w400)),
SizedBox(height: 8),
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: <Widget>[
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: false);
},
),
],
),
),
SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: _generateWalletProvider.walletIsGenerated
? () {
_generateWalletProvider.nbrWord =
_generateWalletProvider.getRandomInt();
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmStoreWallet(
generatedMnemonic:
_generateWalletProvider
.generatedMnemonic,
generatedWallet: _generateWalletProvider
.actualWallet);
}),
);
}
: null,
child: Text('Enregistrer ce trousseau',
style: TextStyle(fontSize: 20))),
SizedBox(height: 20),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return PrintWallet(
_generateWalletProvider.generatedMnemonic);
}),
);
},
child: Icon(Icons.print))
]),
)));
}
Widget toolTips(_key, _text, _message) {
@ -157,6 +145,7 @@ class GenerateWalletsScreen extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(width: 20),
Column(children: <Widget>[
SizedBox(
width: 30,
@ -179,10 +168,9 @@ class GenerateWalletsScreen extends StatelessWidget {
// ignore: must_be_immutable
class PrintWallet extends StatelessWidget {
PrintWallet(this.sentence, this.pubkey);
PrintWallet(this.sentence);
final String sentence;
final String pubkey;
@override
Widget build(BuildContext context) {
@ -190,10 +178,9 @@ class PrintWallet extends StatelessWidget {
Provider.of<GenerateWalletsProvider>(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Imprimer ce portefeuille')),
appBar: AppBar(title: Text('Imprimer ce trousseau')),
body: PdfPreview(
build: (format) =>
_generateWalletProvider.printWallet(sentence, pubkey),
build: (format) => _generateWalletProvider.printWallet(sentence),
),
),
);

View File

@ -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<GenerateWalletsProvider>(context);
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(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<bool>.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: <Widget>[
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: <Widget>[
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: <Widget>[
SizedBox(width: 20),
Column(children: <Widget>[
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)
])));
}
}

View File

@ -3,19 +3,25 @@ import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'package:gecko/models/myWallets.dart';
import 'package:gecko/models/walletOptions.dart';
import 'package:gecko/screens/myWallets/changePin.dart';
import 'dart:async';
import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
// ignore: must_be_immutable
class WalletOptions extends StatelessWidget with ChangeNotifier {
WalletOptions({Key keyMyWallets, @required this.walletName})
WalletOptions(
{Key keyMyWallets,
@required this.walletNbr,
@required this.walletName,
@required this.derivation})
: super(key: keyMyWallets);
int walletNbr;
String walletName;
int derivation;
StreamController<ErrorAnimationType> errorController;
TextEditingController _enterPin = new TextEditingController();
TextEditingController _enterPin = TextEditingController();
final formKey = GlobalKey<FormState>();
bool hasError = false;
var pinColor = Color(0xffF9F9F1);
@ -32,6 +38,9 @@ class WalletOptions extends StatelessWidget with ChangeNotifier {
Provider.of<MyWalletsProvider>(context);
errorController = StreamController<ErrorAnimationType>();
// _walletOptions.isWalletUnlock = false;
final int _pinLenght = _walletOptions.getPinLenght(this.walletNbr);
return WillPopScope(
onWillPop: () {
_walletOptions.isWalletUnlock = false;
@ -43,210 +52,213 @@ class WalletOptions extends StatelessWidget with ChangeNotifier {
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
Navigator.of(context).pop();
_walletOptions.isWalletUnlock = false;
Navigator.of(context).pop();
}),
title: SizedBox(
height: 22,
child: Text(walletName),
)),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
Visibility(
visible: _walletOptions.isWalletUnlock,
child: Expanded(
child: Column(children: <Widget>[
SizedBox(height: 15),
Text(
'Clé publique:',
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(height: 15),
Text(
_walletOptions.pubkey.text,
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold),
),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
body: Builder(
builder: (ctx) => SafeArea(
child: Column(children: <Widget>[
Visibility(
visible: _walletOptions.isWalletUnlock,
child: Expanded(
child: Column(children: <Widget>[
SizedBox(height: 15),
Text(
'Clé publique:',
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(height: 15),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: _walletOptions.pubkey.text));
_walletOptions.snackCopyKey(ctx);
},
child: Text(
_walletOptions.pubkey.text,
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold,
fontFamily: 'Monospace'),
)),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
primary: Color(
0xffFFD68E), //Color(0xffFFD68E), // background
onPrimary:
Colors.black, // foreground
),
onPressed: () => _walletOptions
.renameWalletAlerte(
context,
walletName,
walletNbr,
derivation)
.then((_result) {
if (_result == true) {
WidgetsBinding.instance
.addPostFrameCallback(
(_) {
_myWalletProvider
.listWallets =
_myWalletProvider
.getAllWalletsNames();
_myWalletProvider
.rebuildWidget();
});
Navigator.pop(
context, true);
}
}),
child: Text(
'Renommer ce portefeuille',
style: TextStyle(
fontSize: 20)))))),
SizedBox(height: 30),
SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
primary: Color(
0xffFFD68E), //Color(0xffFFD68E), // background
elevation: 6,
primary: Colors
.redAccent, //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => _walletOptions
.renameWalletAlerte(
context, walletName)
.then((_result) {
if (_result == true) {
WidgetsBinding.instance
.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider
.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
}
}),
child: Text('Renommer ce portefeuille',
style: TextStyle(fontSize: 20)))))),
SizedBox(height: 30),
SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
primary: Color(
0xffFFD68E), //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
onPressed: () async {
await _walletOptions.deleteWallet(context,
walletNbr, walletName, derivation);
WidgetsBinding.instance
.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider
.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
},
child: Text('Supprimer ce portefeuille',
style: TextStyle(fontSize: 20)))),
SizedBox(height: 50),
Text(
'Portefeuille déverrouillé',
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.w700,
fontSize: 15),
),
onPressed: () {
// changePin(widget.walletName, this.walletPin);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ChangePinScreen(
walletName: walletName,
oldPin: this.walletPin);
}),
);
},
child: Text('Changer mon code secret',
style: TextStyle(fontSize: 20)))),
SizedBox(height: 30),
SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 6,
primary: Colors
.redAccent, //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
SizedBox(height: 10)
]))),
Visibility(
visible: !_walletOptions.isWalletUnlock,
child: Expanded(
child: Column(children: <Widget>[
SizedBox(height: 80),
Text(
'Veuillez tapper votre code secret pour dévérouiller votre portefeuille.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.w400),
),
onPressed: () async {
await _walletOptions.deleteWallet(
context, walletName);
WidgetsBinding.instance.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
},
child: Text('Supprimer ce portefeuille',
style: TextStyle(fontSize: 20)))),
SizedBox(height: 50),
Text(
'Portefeuille déverrouillé',
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.w700,
fontSize: 15),
),
SizedBox(height: 10)
]))),
Visibility(
visible: !_walletOptions.isWalletUnlock,
child: Expanded(
child: Column(children: <Widget>[
SizedBox(height: 80),
Text(
'Veuillez tapper votre code secret pour dévérouiller votre portefeuille.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.w400),
),
SizedBox(height: 50),
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 30),
child: PinCodeTextField(
autoFocus: true,
appContext: context,
pastedTextStyle: TextStyle(
color: Colors.green.shade600,
fontWeight: FontWeight.bold,
),
length: 6,
obscureText: false,
obscuringCharacter: '*',
animationType: AnimationType.fade,
validator: (v) {
if (v.length < 6) {
return "Votre code PIN fait 6 caractères";
} else {
return null;
}
},
pinTheme: PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: 60,
fieldWidth: 50,
activeFillColor:
hasError ? Colors.orange : Colors.white,
),
cursorColor: Colors.black,
animationDuration: Duration(milliseconds: 300),
textStyle: TextStyle(fontSize: 20, height: 1.6),
backgroundColor: pinColor,
enableActiveFill: false,
errorAnimationController: errorController,
controller: _enterPin,
keyboardType: TextInputType.text,
boxShadows: [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
)
],
onCompleted: (_pin) async {
print("Completed");
final resultWallet =
await _walletOptions.readLocalWallet(
this.walletName, _pin.toUpperCase());
if (resultWallet == 'bad') {
errorController.add(ErrorAnimationType
.shake); // Triggering error shake animation
hasError = true;
pinColor = Colors.red[200];
notifyListeners();
} else {
pinColor = Colors.green[200];
// setState(() {});
// await Future.delayed(Duration(milliseconds: 50));
this.walletPin = _pin.toUpperCase();
// isWalletUnlock = true;
notifyListeners();
}
},
onChanged: (value) {
if (pinColor != Color(0xffF9F9F1)) {
pinColor = Color(0xffF9F9F1);
}
print(value);
},
)),
)
]))),
])))));
SizedBox(height: 50),
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 30),
child: PinCodeTextField(
autoFocus: true,
appContext: context,
pastedTextStyle: TextStyle(
color: Colors.green.shade600,
fontWeight: FontWeight.bold,
),
length: _pinLenght,
obscureText: false,
obscuringCharacter: '*',
animationType: AnimationType.fade,
validator: (v) {
if (v.length < _pinLenght) {
return "Votre code PIN fait $_pinLenght caractères";
} else {
return null;
}
},
pinTheme: PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: 60,
fieldWidth: 50,
activeFillColor: hasError
? Colors.orange
: Colors.white,
),
cursorColor: Colors.black,
animationDuration:
Duration(milliseconds: 300),
textStyle:
TextStyle(fontSize: 20, height: 1.6),
backgroundColor: pinColor,
enableActiveFill: false,
errorAnimationController: errorController,
controller: _enterPin,
keyboardType: TextInputType.text,
boxShadows: [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
)
],
onCompleted: (_pin) async {
print("Completed");
final resultWallet =
await _walletOptions.readLocalWallet(
this.walletNbr,
this.walletName,
_pin.toUpperCase(),
_pinLenght,
this.derivation);
if (resultWallet == 'bad') {
errorController.add(ErrorAnimationType
.shake); // Triggering error shake animation
hasError = true;
pinColor = Colors.red[200];
notifyListeners();
} else {
pinColor = Colors.green[200];
// setState(() {});
// await Future.delayed(Duration(milliseconds: 50));
this.walletPin = _pin.toUpperCase();
// isWalletUnlock = true;
notifyListeners();
}
},
onChanged: (value) {
if (pinColor != Color(0xffF9F9F1)) {
pinColor = Color(0xffF9F9F1);
}
print(value);
},
)),
)
]))),
])))));
}
}

View File

@ -2,18 +2,20 @@ 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';
// ignore: must_be_immutable
class WalletsHome extends StatelessWidget {
final _derivationKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context);
print('BUILD: WalletsHome');
_walletOptions.isWalletUnlock = false;
myWalletProvider.listWallets = myWalletProvider.getAllWalletsNames();
final bool isWalletsExists = myWalletProvider.checkIfWalletExist();
@ -28,12 +30,11 @@ class WalletsHome extends StatelessWidget {
child: FloatingActionButton(
heroTag: "buttonGenerateWallet",
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return GenerateWalletsScreen();
}),
);
showDialog(
context: context,
builder: (BuildContext context) {
return addNewDerivation(context, 1);
});
},
child: Container(
height: 40.0,
@ -64,7 +65,7 @@ class WalletsHome extends StatelessWidget {
return GenerateWalletsScreen();
}),
),
child: Text('Générer un portefeuille',
child: Text('Générer un trousseau',
style: TextStyle(fontSize: 20))),
SizedBox(height: 15),
Center(
@ -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))),
])),
@ -87,38 +93,94 @@ class WalletsHome extends StatelessWidget {
}
Widget myWalletsList(BuildContext context) {
MyWalletsProvider myWalletProvider =
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
// TODO: Show history of my wallets
// HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
// Map _balance = Map();
final bool isWalletsExists = _myWalletProvider.checkIfWalletExist();
List _listWallets = [];
myWalletProvider.listWallets.forEach((_name, _pubkey) {
_listWallets.add(_name);
// _balance[_name] = _historyProvider.getBalance(_pubkey).toString();
print(_name + _pubkey);
});
if (!isWalletsExists) {
return Text('');
}
return Column(children: <Widget>[
if (_myWalletProvider.listWallets == '') {
return Expanded(
child: Center(
child: Text(
'Veuillez générer votre premier portefeuille',
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
)));
}
List _listWallets = _myWalletProvider.listWallets.split('\n');
return Expanded(
child: ListView(children: <Widget>[
SizedBox(height: 8),
for (String _repository in _listWallets)
ListTile(
contentPadding: const EdgeInsets.all(5.0),
contentPadding: const EdgeInsets.only(left: 7.0),
leading: Padding(
padding: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(6.0),
child: Text("0 Ğ1", style: TextStyle(fontSize: 14.0))),
title: Text(_repository, style: TextStyle(fontSize: 16.0)),
subtitle: Text(myWalletProvider.listWallets[_repository],
style: TextStyle(fontSize: 11.0)),
// subtitle: Text(_repository.split(':')[3],
// style: TextStyle(fontSize: 12.0, fontFamily: 'Monospace')),
title:
Text(_repository.split(':')[1], style: TextStyle(fontSize: 16.0)),
dense: true,
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WalletOptions(walletName: _repository);
return WalletOptions(
walletNbr: int.parse(_repository.split(':')[0]),
walletName: _repository.split(':')[1],
derivation: int.parse(_repository.split(':')[2]));
}));
},
)
]);
]));
}
Widget addNewDerivation(context, int _walletNbr) {
final TextEditingController _newDerivationName = TextEditingController();
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
return AlertDialog(
content: Stack(
overflow: Overflow.visible,
children: <Widget>[
Form(
key: _derivationKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Nom du portefeuille:'),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _newDerivationName,
textAlign: TextAlign.center,
autofocus: true,
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
child: Text("Créer"),
color: Color(0xffFFD68E),
onPressed: () async {
await _myWalletProvider
.generateNewDerivation(
context, _newDerivationName.text, _walletNbr)
.then((_) => _newDerivationName.text == '');
},
),
)
],
),
),
],
),
);
}
}

View File

@ -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,23 +31,61 @@ class SettingsScreen extends StatelessWidget {
child: Text('Paramètres'),
)),
body: Column(children: <Widget>[
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: () => {
onPressed: () async => {
print('Suppression de tous les wallets'),
_myWallets.deleteAllWallet(context)
await _myWallets.deleteAllWallet(context)
},
child: Text(
"EFFACER TOUS MES PORTEFEUILLES, LE TEMPS DE L'ALPHA",

View File

@ -10,7 +10,7 @@ crate-type = ["rlib"]
[dependencies]
allo-isolate = "0.1.6"
dup-crypto = { version = "0.36.0", features = ["dewif", "mnemonic", "mnemonic_french", "rand", "scrypt"] }
dup-crypto = { version = "0.41.1", features = ["bip32-ed25519", "dewif", "mnemonic", "mnemonic_french", "rand", "scrypt"] }
fast-threadpool = { version = "0.3.0", default-features = false }
once_cell = { version = "1.3.1", default-features = false, features = ["std"] }
thiserror = "1.0.23"

View File

@ -13,31 +13,26 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pub mod bip32;
pub mod classic;
use crate::*;
pub(super) fn change_secret_code(
currency: &str,
dewif: &str,
old_secret_code: &str,
member_wallet: bool,
secret_code_type: SecretCodeType,
system_memory: i64,
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
old_secret_code,
)
.map_err(DubpError::DewifReadError)?;
if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() {
let new_secret_code = gen_secret_code(member_wallet, secret_code_type)?;
let new_log_n = log_n(system_memory);
let new_secret_code = gen_secret_code(member_wallet, secret_code_type, new_log_n)?;
let dewif = dup_crypto::dewif::write_dewif_v1_content(currency, &keypair, &new_secret_code);
let pubkey = keypair.public_key().to_base58();
Ok(vec![dewif, new_secret_code, pubkey])
} else {
Err(DubpError::DewifReadError(DewifReadError::CorruptedContent))
}
let new_dewif =
dup_crypto::dewif::change_dewif_passphrase(dewif, old_secret_code, &new_secret_code)
.map_err(DubpError::DewifReadError)?;
Ok(vec![new_dewif, new_secret_code])
}
pub(super) fn gen_dewif(
@ -46,68 +41,96 @@ pub(super) fn gen_dewif(
mnemonic: &str,
member_wallet: bool,
secret_code_type: SecretCodeType,
system_memory: i64,
wallet_type: WalletType,
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let mnemonic =
Mnemonic::from_phrase(mnemonic, language).map_err(|_| DubpError::WrongLanguage)?;
let seed = dup_crypto::mnemonic::mnemonic_to_seed(&mnemonic);
let keypair = KeyPairFromSeed32Generator::generate(seed);
let secret_code = gen_secret_code(member_wallet, secret_code_type)?;
let dewif = dup_crypto::dewif::write_dewif_v1_content(currency, &keypair, &secret_code);
let pubkey = keypair.public_key().to_base58();
Ok(vec![dewif, secret_code, pubkey])
let log_n = log_n(system_memory);
let secret_code = gen_secret_code(member_wallet, secret_code_type, log_n)?;
let dewif = match wallet_type {
WalletType::Bip32Ed25519 => {
let keypair = dup_crypto::keys::ed25519::bip32::KeyPair::from_seed(seed.clone());
let pubkey = keypair.public_key();
dup_crypto::dewif::write_dewif_v4_content(currency, log_n, &secret_code, &pubkey, seed)
}
WalletType::Ed25519 => {
let keypair = KeyPairFromSeed32Generator::generate(seed);
dup_crypto::dewif::write_dewif_v3_content(currency, &keypair, log_n, &secret_code)
}
};
Ok(vec![dewif, secret_code])
}
pub(super) fn get_pubkey(currency: Currency, dewif: &str, pin: &str) -> Result<String, DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&pin.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() {
Ok(keypair.public_key().to_base58())
} else {
Err(DubpError::DewifReadError(DewifReadError::CorruptedContent))
}
}
pub(super) fn sign(currency: &str, dewif: &str, pin: &str, msg: &str) -> Result<String, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&pin.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() {
Ok(keypair.generate_signator().sign(msg.as_bytes()).to_base64())
} else {
Err(DubpError::DewifReadError(DewifReadError::CorruptedContent))
}
}
pub(super) fn sign_several(
currency: &str,
pub(super) fn get_dewif_meta(
dewif: &str,
pin: &str,
msgs: &[&str],
member_wallet: bool,
secret_code_type: SecretCodeType,
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let dup_crypto::dewif::DewifMeta {
currency,
log_n,
version,
} = dup_crypto::dewif::read_dewif_meta(dewif).map_err(DubpError::DewifReadError)?;
let secret_code_len =
crate::secret_code::compute_secret_code_len(member_wallet, secret_code_type, log_n)?;
Ok(vec![
currency.to_string(),
secret_code_len.to_string(),
version.to_string(),
])
}
pub(super) fn get_pubkey(
currency: Currency,
dewif: &str,
secret_code: &str,
) -> Result<String, DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&pin.to_ascii_uppercase(),
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() {
let signator = keypair.generate_signator();
Ok(msgs
.iter()
.map(|msg| signator.sign(msg.as_bytes()).to_base64())
.collect())
} else {
Err(DubpError::DewifReadError(DewifReadError::CorruptedContent))
match keypairs.next() {
Some(KeyPairEnum::Ed25519(keypair)) => Ok(keypair.public_key().to_base58()),
Some(KeyPairEnum::Bip32Ed25519(_)) => Err(DubpError::GetMasterPubkeyOfHdWallet),
Some(_) => Err(DubpError::UnsupportedDewifVersion),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(super) fn get_secret_code_len(
dewif: *const raw::c_char,
member_wallet: u32,
secret_code_type: u32,
) -> Result<usize, DubpError> {
let dewif = char_ptr_to_str(dewif)?;
let member_wallet = member_wallet != 0;
let secret_code_type = SecretCodeType::from(secret_code_type);
let log_n = dup_crypto::dewif::read_dewif_log_n(ExpectedCurrency::Any, dewif)
.map_err(DubpError::DewifReadError)?;
Ok(crate::secret_code::compute_secret_code_len(
member_wallet,
secret_code_type,
log_n,
)?)
}
pub(crate) fn log_n(system_memory: i64) -> u8 {
if system_memory > 3_000_000_000 {
15
} else {
12
}
}

View File

@ -0,0 +1,97 @@
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*;
pub(crate) fn get_accounts_pubkeys(
currency: Currency,
dewif: &str,
secret_code: &str,
accounts_indexs: Vec<DerivationIndex>,
) -> Result<Vec<String>, DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => Ok(accounts_indexs
.into_iter()
.map(|account_index| {
master_keypair
.derive(account_index)
.public_key()
.to_base58()
})
.collect()),
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(crate) fn sign_transparent(
account_index: DerivationIndex,
currency: &str,
dewif: &str,
secret_code: &str,
msg: &str,
) -> Result<String, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => Ok(master_keypair
.derive(account_index)
.generate_signator()
.sign(msg.as_bytes())
.to_base64()),
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(crate) fn sign_several_transparent(
account_index: DerivationIndex,
currency: &str,
dewif: &str,
secret_code: &str,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => {
let signator = master_keypair.derive(account_index).generate_signator();
Ok(msgs
.iter()
.map(|msg| signator.sign(msg.as_bytes()).to_base64())
.collect())
}
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}

View File

@ -0,0 +1,60 @@
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*;
pub(crate) fn sign(
currency: &str,
dewif: &str,
secret_code: &str,
msg: &str,
) -> Result<String, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() {
Ok(keypair.generate_signator().sign(msg.as_bytes()).to_base64())
} else {
Err(DubpError::DewifReadError(DewifReadError::CorruptedContent))
}
}
pub(crate) fn sign_several(
currency: &str,
dewif: &str,
secret_code: &str,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() {
let signator = keypair.generate_signator();
Ok(msgs
.iter()
.map(|msg| signator.sign(msg.as_bytes()).to_base64())
.collect())
} else {
Err(DubpError::DewifReadError(DewifReadError::CorruptedContent))
}
}

View File

@ -13,6 +13,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use dup_crypto::keys::ed25519::bip32::InvalidDerivationIndex;
use crate::*;
/// Dubp error
@ -22,8 +24,16 @@ pub(crate) enum DubpError {
DewifReadError(DewifReadError),
#[error("I/O error: {0}")]
IoErr(io::Error),
#[error("{0}")]
InvalidDerivationIndex(InvalidDerivationIndex),
#[error("Digits secret code forbid for member wallet")]
DigitsCodeForbidForMemberWallet,
#[error("It is forbidden to retrieve the master public key of an HD wallet.")]
GetMasterPubkeyOfHdWallet,
#[error("this wallet is not an HD wallet")]
NotHdWallet,
#[error("this account index is not a transparent account index")]
NotTransparentAccountIndex,
#[error("A given parameter is null")]
NullParamErr,
#[error("fail to generate random bytes")]
@ -32,6 +42,8 @@ pub(crate) enum DubpError {
UnknownCurrencyName,
#[error("Unknown language")]
UnknownLanguage,
#[error("Unsupported DEWIF version")]
UnsupportedDewifVersion,
#[error("{0}")]
Utf8Error(std::str::Utf8Error),
#[error("Wrong language")]
@ -44,6 +56,12 @@ impl From<io::Error> for DubpError {
}
}
impl From<InvalidDerivationIndex> for DubpError {
fn from(e: InvalidDerivationIndex) -> Self {
Self::InvalidDerivationIndex(e)
}
}
pub(crate) struct DartRes(allo_isolate::ffi::DartCObject);
impl DartRes {
pub(crate) fn err<E: ToString>(e: E) -> allo_isolate::ffi::DartCObject {

View File

@ -15,6 +15,7 @@
use crate::*;
#[derive(Clone, Copy, Debug)]
pub(crate) enum SecretCodeType {
Digits,
Letters,
@ -28,6 +29,20 @@ impl From<u32> for SecretCodeType {
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum WalletType {
Ed25519,
Bip32Ed25519,
}
impl From<u32> for WalletType {
fn from(i: u32) -> Self {
if i == 1 {
WalletType::Bip32Ed25519
} else {
WalletType::Ed25519
}
}
}
pub(crate) fn char_ptr_to_str<'a>(c_char_ptr: *const raw::c_char) -> Result<&'a str, DubpError> {
if c_char_ptr.is_null() {
@ -37,6 +52,16 @@ pub(crate) fn char_ptr_to_str<'a>(c_char_ptr: *const raw::c_char) -> Result<&'a
}
}
pub(crate) fn char_ptr_prt_to_vec_hard_derivation_index(
u32_ptr: *const u32,
len: u32,
) -> Result<Vec<DerivationIndex>, DubpError> {
u32_ptr_to_vec_u32(u32_ptr, len)
.into_iter()
.map(|ai| DerivationIndex::hard(ai).map_err(DubpError::InvalidDerivationIndex))
.collect()
}
pub(crate) fn char_ptr_prt_to_vec_str<'a>(
char_ptr_ptr: *const *const raw::c_char,
len: u32,
@ -60,6 +85,24 @@ pub(crate) fn parse_currency(currency: &str) -> Result<Currency, DubpError> {
Ok(Currency::from(currency_code))
}
pub(crate) fn transparent_account_index(account_index: u32) -> Result<DerivationIndex, DubpError> {
if account_index % 3 == 0 {
DerivationIndex::hard(account_index).map_err(DubpError::InvalidDerivationIndex)
} else {
Err(DubpError::NotTransparentAccountIndex)
}
}
pub(crate) fn u32_ptr_to_vec_u32(u32_ptr: *const u32, len: u32) -> Vec<u32> {
let len = len as usize;
let u32_slice: &[u32] = unsafe { std::slice::from_raw_parts(u32_ptr, len) };
let mut vec = Vec::with_capacity(len);
for u32_ in u32_slice {
vec.push(*u32_);
}
vec
}
pub(crate) fn u32_to_language(i: u32) -> Result<Language, DubpError> {
match i {
0 => Ok(Language::English),

View File

@ -0,0 +1,82 @@
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*;
use dup_crypto::keys::ed25519::{KeyPairFromSaltedPasswordGenerator, SaltedPassword};
pub(super) fn gen_dewif_from_legacy(
currency: &str,
salt: String,
password: String,
member_wallet: bool,
secret_code_type: SecretCodeType,
system_memory: i64,
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters()
.generate(SaltedPassword::new(salt, password));
let log_n = crate::dewif::log_n(system_memory);
let secret_code = gen_secret_code(member_wallet, secret_code_type, log_n)?;
let dewif = dup_crypto::dewif::write_dewif_v3_content(currency, &keypair, log_n, &secret_code);
let pubkey = keypair.public_key().to_base58();
Ok(vec![dewif, secret_code, pubkey])
}
pub(super) fn get_pubkey(salt: &str, password: &str) -> String {
KeyPairFromSaltedPasswordGenerator::with_default_parameters()
.generate(SaltedPassword::new(salt.to_owned(), password.to_owned()))
.public_key()
.to_base58()
}
pub(super) fn sign(salt: &str, password: &str, msg: &str) -> String {
KeyPairFromSaltedPasswordGenerator::with_default_parameters()
.generate(SaltedPassword::new(salt.to_owned(), password.to_owned()))
.generate_signator()
.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(())
}
}

View File

@ -19,6 +19,7 @@ mod r#async;
mod dewif;
mod error;
mod inputs;
mod legacy;
mod mnemonic;
mod secret_code;
@ -31,8 +32,8 @@ use dup_crypto::{
bases::b58::ToBase58,
dewif::{Currency, DewifReadError, ExpectedCurrency, G1_CURRENCY, G1_TEST_CURRENCY},
keys::{
ed25519::KeyPairFromSeed32Generator, KeyPair as _, KeyPairEnum, Signator as _,
Signature as _,
ed25519::bip32::DerivationIndex, ed25519::KeyPairFromSeed32Generator, KeyPair as _,
KeyPairEnum, Signator as _, Signature as _,
},
mnemonic::{Language, Mnemonic, MnemonicType},
};
@ -44,24 +45,35 @@ use thiserror::Error;
#[no_mangle]
pub extern "C" fn change_dewif_secret_code(
port: i64,
currency: *const raw::c_char,
dewif: *const raw::c_char,
old_pin: *const raw::c_char,
old_secret_code: *const raw::c_char,
member_wallet: u32,
secret_code_type: u32,
system_memory: i64,
) {
exec_async(
port,
|| {
let currency = char_ptr_to_str(currency)?;
let dewif = char_ptr_to_str(dewif)?;
let old_pin = char_ptr_to_str(old_pin)?;
let old_secret_code = char_ptr_to_str(old_secret_code)?;
let member_wallet = member_wallet != 0;
let secret_code_type = SecretCodeType::from(secret_code_type);
Ok((currency, dewif, old_pin, member_wallet, secret_code_type))
Ok((
dewif,
old_secret_code,
member_wallet,
secret_code_type,
system_memory,
))
},
|(currency, dewif, old_pin, member_wallet, secret_code_type)| {
dewif::change_secret_code(currency, dewif, old_pin, member_wallet, secret_code_type)
|(dewif, old_secret_code, member_wallet, secret_code_type, system_memory)| {
dewif::change_secret_code(
dewif,
old_secret_code,
member_wallet,
secret_code_type,
system_memory,
)
},
)
}
@ -74,6 +86,8 @@ pub extern "C" fn gen_dewif(
mnemonic: *const raw::c_char,
member_wallet: u32,
secret_code_type: u32,
system_memory: i64,
wallet_type: u32,
) {
exec_async(
port,
@ -83,21 +97,74 @@ pub extern "C" fn gen_dewif(
let mnemonic = char_ptr_to_str(mnemonic)?;
let member_wallet = member_wallet != 0;
let secret_code_type = SecretCodeType::from(secret_code_type);
let wallet_type = WalletType::from(wallet_type);
Ok((
currency,
language,
mnemonic,
member_wallet,
secret_code_type,
system_memory,
wallet_type,
))
},
|(currency, language, mnemonic, member_wallet, secret_code_type)| {
|(
currency,
language,
mnemonic,
member_wallet,
secret_code_type,
system_memory,
wallet_type,
)| {
dewif::gen_dewif(
currency,
language,
mnemonic,
member_wallet,
secret_code_type,
system_memory,
wallet_type,
)
},
)
}
#[no_mangle]
pub extern "C" fn gen_dewif_from_legacy(
port: i64,
currency: *const raw::c_char,
salt: *const raw::c_char,
password: *const raw::c_char,
member_wallet: u32,
secret_code_type: u32,
system_memory: i64,
) {
exec_async(
port,
|| {
let currency = char_ptr_to_str(currency)?;
let salt = char_ptr_to_str(salt)?.to_owned();
let password = char_ptr_to_str(password)?.to_owned();
let member_wallet = member_wallet != 0;
let secret_code_type = SecretCodeType::from(secret_code_type);
Ok((
currency,
salt,
password,
member_wallet,
secret_code_type,
system_memory,
))
},
|(currency, salt, password, member_wallet, secret_code_type, system_memory)| {
legacy::gen_dewif_from_legacy(
currency,
salt,
password,
member_wallet,
secret_code_type,
system_memory,
)
},
)
@ -105,7 +172,42 @@ pub extern "C" fn gen_dewif(
#[no_mangle]
pub extern "C" fn gen_mnemonic(port: i64, language: u32) {
Isolate::new(port).post(DartRes::from(mnemonic::gen_mnemonic(language)));
exec_async(port, || u32_to_language(language), mnemonic::gen_mnemonic)
}
#[no_mangle]
pub extern "C" fn get_dewif_meta(
port: i64,
dewif: *const raw::c_char,
member_wallet: u32,
secret_code_type: u32,
) {
exec_async(
port,
|| {
let dewif = char_ptr_to_str(dewif)?;
let member_wallet = member_wallet != 0;
let secret_code_type = SecretCodeType::from(secret_code_type);
Ok((dewif, member_wallet, secret_code_type))
},
|(dewif, member_wallet, secret_code_type)| {
dewif::get_dewif_meta(dewif, member_wallet, secret_code_type)
},
)
}
#[no_mangle]
pub extern "C" fn get_dewif_secret_code_len(
dewif: *const raw::c_char,
member_wallet: u32,
secret_code_type: u32,
) -> i32 {
if let Ok(secret_code_len) = dewif::get_secret_code_len(dewif, member_wallet, secret_code_type)
{
secret_code_len as i32
} else {
-1
}
}
#[no_mangle]
@ -127,6 +229,48 @@ pub extern "C" fn get_dewif_pubkey(
)
}
#[no_mangle]
pub extern "C" fn get_bip32_dewif_accounts_pubkeys(
port: i64,
currency: *const raw::c_char,
dewif: *const raw::c_char,
secret_code: *const raw::c_char,
accounts_indexs_len: u32,
accounts_indexs: *const u32,
) {
exec_async(
port,
|| {
let currency = parse_currency(char_ptr_to_str(currency)?)?;
let dewif = char_ptr_to_str(dewif)?;
let secret_code = char_ptr_to_str(secret_code)?;
let accounts_indexs =
char_ptr_prt_to_vec_hard_derivation_index(accounts_indexs, accounts_indexs_len)?;
Ok((currency, dewif, secret_code, accounts_indexs))
},
|(currency, dewif, secret_code, accounts_indexs)| {
dewif::bip32::get_accounts_pubkeys(currency, dewif, secret_code, accounts_indexs)
},
)
}
#[no_mangle]
pub extern "C" fn get_legacy_pubkey(
port: i64,
salt: *const raw::c_char,
password: *const raw::c_char,
) {
exec_async(
port,
|| {
let salt = char_ptr_to_str(salt)?;
let password = char_ptr_to_str(password)?;
Ok((salt, password))
},
|(salt, password)| Ok::<_, DubpError>(legacy::get_pubkey(salt, password)),
)
}
#[no_mangle]
pub extern "C" fn mnemonic_to_pubkey(
port: i64,
@ -136,6 +280,7 @@ pub extern "C" fn mnemonic_to_pubkey(
exec_async(
port,
|| {
let language = u32_to_language(language)?;
let mnemonic_phrase = char_ptr_to_str(mnemonic_phrase)?;
Ok((language, mnemonic_phrase))
},
@ -160,7 +305,51 @@ pub extern "C" fn sign(
let msg = char_ptr_to_str(msg)?;
Ok((currency, dewif, pin, msg))
},
|(currency, dewif, pin, msg)| dewif::sign(currency, dewif, pin, msg),
|(currency, dewif, pin, msg)| dewif::classic::sign(currency, dewif, pin, msg),
)
}
#[no_mangle]
pub extern "C" fn sign_bip32_transparent(
port: i64,
account_index: u32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
secret_code: *const raw::c_char,
msg: *const raw::c_char,
) {
exec_async(
port,
|| {
let account_index = transparent_account_index(account_index)?;
let currency = char_ptr_to_str(currency)?;
let dewif = char_ptr_to_str(dewif)?;
let pin = char_ptr_to_str(secret_code)?;
let msg = char_ptr_to_str(msg)?;
Ok((currency, dewif, pin, msg, account_index))
},
|(currency, dewif, secret_code, msg, account_index)| {
dewif::bip32::sign_transparent(account_index, currency, dewif, secret_code, msg)
},
)
}
#[no_mangle]
pub extern "C" fn sign_legacy(
port: i64,
salt: *const raw::c_char,
password: *const raw::c_char,
msg: *const raw::c_char,
) {
exec_async(
port,
|| {
let salt = char_ptr_to_str(salt)?;
let password = char_ptr_to_str(password)?;
let msg = char_ptr_to_str(msg)?;
Ok((salt, password, msg))
},
|(salt, password, msg)| Ok::<_, DubpError>(legacy::sign(salt, password, msg)),
)
}
@ -182,6 +371,32 @@ pub extern "C" fn sign_several(
let msgs = char_ptr_prt_to_vec_str(msgs, msgs_len)?;
Ok((currency, dewif, pin, msgs))
},
|(currency, dewif, pin, msgs)| dewif::sign_several(currency, dewif, pin, &msgs),
|(currency, dewif, pin, msgs)| dewif::classic::sign_several(currency, dewif, pin, &msgs),
)
}
#[no_mangle]
pub extern "C" fn sign_several_bip32_transparent(
port: i64,
account_index: u32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
pin: *const raw::c_char,
msgs_len: u32,
msgs: *const *const raw::c_char,
) {
exec_async(
port,
|| {
let account_index = transparent_account_index(account_index)?;
let currency = char_ptr_to_str(currency)?;
let dewif = char_ptr_to_str(dewif)?;
let pin = char_ptr_to_str(pin)?;
let msgs = char_ptr_prt_to_vec_str(msgs, msgs_len)?;
Ok((currency, dewif, pin, msgs, account_index))
},
|(currency, dewif, pin, msgs, account_index)| {
dewif::bip32::sign_several_transparent(account_index, currency, dewif, pin, &msgs)
},
)
}

View File

@ -15,15 +15,15 @@
use crate::*;
pub(super) fn gen_mnemonic(language: u32) -> Result<String, DubpError> {
let mnemonic = Mnemonic::new(MnemonicType::Words12, u32_to_language(language)?)
.map_err(|_| DubpError::RandErr)?;
pub(super) fn gen_mnemonic(language: Language) -> Result<String, DubpError> {
let mnemonic =
Mnemonic::new(MnemonicType::Words12, language).map_err(|_| DubpError::RandErr)?;
Ok(mnemonic.phrase().to_owned())
}
pub(super) fn mnemonic_to_pubkey(language: u32, mnemonic: &str) -> Result<String, DubpError> {
let mnemonic = Mnemonic::from_phrase(mnemonic, u32_to_language(language)?)
.map_err(|_| DubpError::WrongLanguage)?;
pub(super) fn mnemonic_to_pubkey(language: Language, mnemonic: &str) -> Result<String, DubpError> {
let mnemonic =
Mnemonic::from_phrase(mnemonic, language).map_err(|_| DubpError::WrongLanguage)?;
let seed = dup_crypto::mnemonic::mnemonic_to_seed(&mnemonic);
let keypair = KeyPairFromSeed32Generator::generate(seed);
Ok(keypair.public_key().to_base58())

View File

@ -15,28 +15,45 @@
use crate::*;
pub(crate) fn gen_secret_code(
pub(crate) fn compute_secret_code_len(
member_wallet: bool,
secret_code_type: SecretCodeType,
) -> Result<String, DubpError> {
log_n: u8,
) -> Result<usize, DubpError> {
match secret_code_type {
SecretCodeType::Digits => {
if member_wallet {
Err(DubpError::DigitsCodeForbidForMemberWallet)
} else if log_n >= 15 {
Ok(7)
} else {
gen_random_digits(8)
Ok(8)
}
}
SecretCodeType::Letters => {
if member_wallet {
gen_random_letters(10)
Ok(10)
} else if log_n >= 15 {
Ok(5)
} else {
gen_random_letters(6)
Ok(6)
}
}
}
}
pub(crate) fn gen_secret_code(
member_wallet: bool,
secret_code_type: SecretCodeType,
log_n: u8,
) -> Result<String, DubpError> {
let secret_code_len = compute_secret_code_len(member_wallet, secret_code_type, log_n)?;
match secret_code_type {
SecretCodeType::Digits => gen_random_digits(secret_code_len),
SecretCodeType::Letters => gen_random_letters(secret_code_len),
}
}
fn gen_random_digits(n: usize) -> Result<String, DubpError> {
let mut digits_string = dup_crypto::rand::gen_u32()
.map_err(|_| DubpError::RandErr)?

View File

@ -2,9 +2,33 @@ import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:isolate/ports.dart';
import "package:system_info/system_info.dart";
import 'ffi.dart' as native;
/// DEWIF meta data
class DewifMetaData {
/// Currency name
String currency;
/// Secret code length
int secretCodeLen;
/// DEWIF version
int version;
/// Wallet type
WalletType walletType;
DewifMetaData._(this.currency, this.secretCodeLen, this.version) {
if (version == 4) {
walletType = WalletType.bip32Ed25519;
} else {
walletType = WalletType.ed25519;
}
}
}
/// Language
enum Language {
/// English
@ -22,10 +46,7 @@ class NewWallet {
/// Secret code
String pin;
/// Public key
String publicKey;
NewWallet._(this.dewif, this.pin, this.publicKey);
NewWallet._(this.dewif, this.pin);
}
/// Secret code type
@ -37,6 +58,15 @@ enum SecretCodeType {
letters,
}
/// Wallet type
enum WalletType {
/// Ed25519
ed25519,
/// BIP32-Ed25519
bip32Ed25519,
}
/// DUBP Rust utilities
///
/// All the functions of this package are static methods of this
@ -45,7 +75,31 @@ class DubpRust {
/// Must be called only once at the start of your application.
static void setup() {
native.store_dart_post_cobject(NativeApi.postCObject);
print("Dubp Setup Done");
print("DUBP_RS Setup Done");
}
/// Change the secret code that encrypts the `dewif` keypair.
static Future<NewWallet> changeDewifPin({
String dewif,
String oldPin,
SecretCodeType secretCodeType = SecretCodeType.letters,
}) async {
int ram = SysInfo.getTotalPhysicalMemory();
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
native.change_dewif_secret_code(
sendPort.nativePort,
Utf8.toUtf8(dewif),
Utf8.toUtf8(oldPin),
0,
secretCodeType.index,
ram,
);
List<String> newWallet = await completer.future;
return Future.value(NewWallet._(newWallet[0], newWallet[1]));
}
/// Generate a random mnemonic
@ -60,27 +114,33 @@ class DubpRust {
return completer.future;
}
/// Change the secret code that encrypts the `dewif` keypair.
static Future<NewWallet> changeDewifPin({
/// Generate a wallet from a deprecated salt + password couple.
///
/// This deprecated method must be used only for compatibility purpose !
static Future<NewWallet> genWalletFromDeprecatedSaltPassword({
String currency = "g1",
String dewif,
String oldPin,
String salt,
String password,
SecretCodeType secretCodeType = SecretCodeType.letters,
}) async {
int ram = SysInfo.getTotalPhysicalMemory();
print('ram=$ram');
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
native.change_dewif_secret_code(
native.gen_dewif_from_legacy(
sendPort.nativePort,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(oldPin),
Utf8.toUtf8(salt),
Utf8.toUtf8(password),
0,
secretCodeType.index,
ram,
);
List<String> newWallet = await completer.future;
return Future.value(NewWallet._(newWallet[0], newWallet[1], newWallet[2]));
return Future.value(NewWallet._(newWallet[0], newWallet[1]));
}
/// Generate a wallet from a mnemonic phrase.
@ -95,7 +155,11 @@ class DubpRust {
Language language = Language.english,
String mnemonic,
SecretCodeType secretCodeType = SecretCodeType.letters,
WalletType walletType = WalletType.ed25519,
}) async {
int ram = SysInfo.getTotalPhysicalMemory();
print('ram=$ram');
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
@ -106,13 +170,51 @@ class DubpRust {
Utf8.toUtf8(mnemonic),
0,
secretCodeType.index,
ram,
walletType.index,
);
List<String> newWallet = await completer.future;
return Future.value(NewWallet._(newWallet[0], newWallet[1], newWallet[2]));
return Future.value(NewWallet._(newWallet[0], newWallet[1]));
}
/// Get pulblic key (in base 58) of `dewif` keypair.
//get_bip32_dewif_accounts_pubkeys
/// Get BIP32 accounts public keys (in base 58) of `dewif` master keypair.
static Future<List<String>> getBip32DewifAccountsPublicKeys(
{String currency = "g1",
String dewif,
String secretCode,
List<int> accountsIndex}) async {
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
native.get_bip32_dewif_accounts_pubkeys(
sendPort.nativePort,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(secretCode),
accountsIndex.length,
_listIntToPtr(accountsIndex));
return completer.future;
}
/// Get `dewif` keypair meta data.
static Future<DewifMetaData> getDewifMetaData(
{String dewif,
SecretCodeType secretCodeType = SecretCodeType.letters}) async {
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
native.get_dewif_meta(
sendPort.nativePort, Utf8.toUtf8(dewif), 0, secretCodeType.index);
List<String> dewifMetaData = await completer.future;
return Future.value(DewifMetaData._(dewifMetaData[0],
int.parse(dewifMetaData[1]), int.parse(dewifMetaData[2])));
}
/// Get public key (in base 58) of `dewif` keypair.
static Future<String> getDewifPublicKey(
{String currency = "g1", String dewif, String pin}) async {
final completer = Completer<String>();
@ -127,6 +229,39 @@ class DubpRust {
return completer.future;
}
/// Get secret code length of `dewif` keypair.
static int getDewifSecretCodeLen(
{String currency = "g1",
String dewif,
SecretCodeType secretCodeType = SecretCodeType.letters}) {
int res = native.get_dewif_secret_code_len(
Utf8.toUtf8(dewif),
0,
secretCodeType.index,
);
if (res == -1) {
print('DUBP_RS_ERROR: DEWIF file content is corrupted.');
throw 'DUBP_RS_ERROR: DEWIF file content is corrupted.';
} else {
return res;
}
}
/// Get public key (in base 58) of legacy wallet (password + salt)
///
/// This deprecated method must be used only for compatibility purpose !
static Future<String> getLegacyPublicKey({String password, String salt}) {
final completer = Completer<String>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.get_legacy_pubkey(
sendPort.nativePort,
Utf8.toUtf8(salt),
Utf8.toUtf8(password),
);
return completer.future;
}
/// Sign the message `message` with `dewif` keypair encryted in DEWIF format.
///
/// If you have several messages to sign, use `signSeveral` method instead.
@ -145,6 +280,48 @@ class DubpRust {
return completer.future;
}
/// Sign the message `message` with `dewif` Bip32-Ed25519 keypair encryted
/// in DEWIF format.
///
/// If you have several messages to sign, use `signSeveralBip32Transparent`
/// method instead.
static Future<String> signBip32Transparent(
{int accountIndex,
String currency = "g1",
String dewif,
String secretCode,
String message}) {
final completer = Completer<String>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.sign_bip32_transparent(
sendPort.nativePort,
accountIndex,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(secretCode),
Utf8.toUtf8(message),
);
return completer.future;
}
/// Sign the message `message` with legacy wallet (password + salt)
///
/// This deprecated method must be used only for compatibility purpose !
static Future<String> signLegacy(
{String password, String salt, String message}) {
final completer = Completer<String>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.sign_legacy(
sendPort.nativePort,
Utf8.toUtf8(password),
Utf8.toUtf8(salt),
Utf8.toUtf8(message),
);
return completer.future;
}
/// Sign several messages `messages` with `dewif` keypair encryted in DEWIF
/// format.
///
@ -172,6 +349,44 @@ class DubpRust {
return completer.future;
}
/// Sign several messages `messages` with `dewif` keypair encryted in DEWIF
/// format.
///
/// This method is optimized to sign several messages at once. If you have
/// several messages to sign, avoid calling the `sign` method for each
/// message. Use this `signSeveral` method instead.
static Future<List<String>> signSeveralBip32Transparent(
{int accountIndex,
String currency = "g1",
String dewif,
String pin,
List<String> messages}) {
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
native.sign_several_bip32_transparent(
sendPort.nativePort,
accountIndex,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(pin),
messages.length,
_listStringToPtr(messages),
);
return completer.future;
}
static Pointer<Uint32> _listIntToPtr(List<int> list) {
//final listUint32 = list.map(int.toUnsigned).toList();
final Pointer<Uint32> ptr = allocate(count: list.length);
for (var i = 0; i < list.length; i++) {
ptr[i] = list[i];
}
return ptr;
}
static Pointer<Pointer<Utf8>> _listStringToPtr(List<String> list) {
final listUtf8 = list.map(Utf8.toUtf8).toList();
final Pointer<Pointer<Utf8>> ptr = allocate(count: listUtf8.length);
@ -201,4 +416,14 @@ class DubpRust {
return arr;
}
}
/*static int _handleErrInt(String res) {
if (res.startsWith('DUBP_RS_ERROR: ')) {
final error = res;
print(error);
throw error;
} else {
return int.parse(res);
}
}*/
}

View File

@ -64,6 +64,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
file_utils:
dependency: transitive
description:
name: file_utils
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
flutter:
dependency: "direct main"
description: flutter
@ -74,6 +81,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
globbing:
dependency: transitive
description:
name: globbing
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1"
isolate:
dependency: "direct main"
description:
@ -135,6 +149,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
system_info:
dependency: "direct main"
description:
name: system_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
term_glyph:
dependency: transitive
description:

View File

@ -11,6 +11,7 @@ dependencies:
sdk: flutter
ffi: ^0.1.3
isolate: ^2.0.3
system_info: ^0.1.3
dev_dependencies:
effective_dart: ^1.0.0

View File

@ -100,7 +100,7 @@ packages:
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
dependency: "direct main"
description:
name: crypto
url: "https://pub.dartlang.org"
@ -120,6 +120,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
fast_base58:
dependency: "direct main"
description:
name: fast_base58
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.9"
ffi:
dependency: transitive
description:
@ -134,6 +141,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.1"
file_utils:
dependency: transitive
description:
name: file_utils
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
flutter:
dependency: "direct main"
description: flutter
@ -163,6 +177,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
globbing:
dependency: transitive
description:
name: globbing
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1"
gql:
dependency: transitive
description:
@ -218,14 +239,14 @@ packages:
name: graphql
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-beta.6"
version: "4.0.0"
graphql_flutter:
dependency: "direct main"
description:
name: graphql_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-beta.6"
version: "4.0.0"
hive:
dependency: transitive
description:
@ -574,6 +595,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6"
sync_http:
dependency: "direct main"
description:
name: sync_http
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
system_info:
dependency: transitive
description:
name: system_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
term_glyph:
dependency: transitive
description:

View File

@ -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.0+15
version: 0.0.1+6
environment:
sdk: ">=2.7.0 <3.0.0"
@ -20,7 +20,7 @@ dependencies:
permission_handler:
image_gallery_saver:
image_picker:
graphql_flutter: ^4.0.0-beta.6 #^3.1.0
graphql_flutter: ^4.0.0 #^3.1.0
provider: ^4.3.2+3
truncate: ^2.1.2
sentry_flutter: ^4.0.1
@ -31,6 +31,9 @@ dependencies:
package_info: ^0.4.3+2
printing: ^4.0.0
shared_preferences: ^0.5.12+4
sync_http: ^0.2.0
crypto: ^2.1.5
fast_base58:
flutter_icons:
@ -49,7 +52,8 @@ flutter:
assets:
- images/
- config/
- config/gva_endpoints.json
- assets/icon/gecko_final.png
- assets/
- assets/OpenSans-Regular.ttf
- assets/icon_user.png

View File

@ -10,7 +10,11 @@ ori_app="app.apk"
echo "Nom du build final: ${APPNAME}-${VERSION}+${BUILD}.apk"
#flutter build apk --split-per-abi --build-name $VERSION --build-number $BUILD
## To compile Rust binding
# cargo br
echo "To compile Rust binding, exec: cargo br"
flutter clean
if [[ $1 == "bundle" ]]; then
flutter build appbundle --release --target-platform android-arm,android-arm64 --build-name $VERSION --build-number $BUILD

14
scripts/build-rust.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -e
MY_PATH="`dirname \"$0\"`"
MY_PATH="`( cd \"$MY_PATH\" && pwd )`"
cd $MY_PATH/..
cargo bd
cargo make android-dev
cargo br
echo -e "\nRust dependencies have been successfully build !"