Compare commits
70 Commits
Author | SHA1 | Date |
---|---|---|
poka | 2e10ee0743 | |
poka | 70fbce3621 | |
poka | c55b1ed3d4 | |
poka | b69d8d3f2b | |
poka | 5c8fd998af | |
poka | 3217be291e | |
poka | 57cbcd2096 | |
poka | 16e9f79e14 | |
poka | 1893502e6b | |
poka | 1522c46256 | |
poka | f5d4e2507b | |
librelois | 03f301a6f5 | |
librelois | a0013a9c26 | |
poka | 533f19dabc | |
poka | 2e484d4373 | |
poka | c4d774e256 | |
poka | 6891df981e | |
poka | f430facfb5 | |
poka | 5ec6b715b0 | |
poka | 4bbb7e3915 | |
poka | 12d2f98672 | |
librelois | 673e2db2ba | |
librelois | 2981181817 | |
poka | 2d234e5a33 | |
poka | db6aa150b5 | |
poka | 6a96b69bca | |
poka | f0294480f3 | |
poka | 27ae053af0 | |
poka | 0aa5d8a5af | |
poka | 2ec8949dde | |
librelois | c09560c9e4 | |
librelois | 2c9732638f | |
librelois | 3b876cf383 | |
librelois | d1ed079904 | |
librelois | 135b2627da | |
poka | 48b419213c | |
poka | 30b326bd5b | |
poka | 2c5237b9bc | |
librelois | 1a8a24601a | |
poka | 7284ce8efe | |
Hugo Trentesaux | b2314b10e9 | |
poka | 55beb7cecc | |
poka | 2a1060850b | |
poka | 5ec1e72030 | |
librelois | 46ee3ec694 | |
poka | 66b00daae9 | |
librelois | 7d9c95bb66 | |
poka | f450f0a181 | |
poka | 54a4e0dae3 | |
poka | e625a9bd47 | |
poka | 051a898904 | |
poka | 4d9730739c | |
poka | 2138973df8 | |
librelois | 89f0e174fb | |
poka | d80b1190ae | |
poka | 3bf3f85b9c | |
poka | 6afd5b01ff | |
poka | 5934744884 | |
poka | aa2c6671ee | |
poka | c279066a5a | |
poka | 26a2c978a9 | |
poka | 231a82a470 | |
poka | 4499f8c4fc | |
librelois | 7eff91c5dc | |
librelois | 1bc74f25f8 | |
librelois | 61eca576f2 | |
poka | 91bf7a653e | |
poka | 6e744f3289 | |
poka | 8d0a3696dc | |
poka | bfd5c666a8 |
|
@ -54,3 +54,5 @@ packages/dubp_rs/lib/ffi.dart
|
|||
|
||||
# Rust things
|
||||
/target
|
||||
|
||||
pubkeys.txt
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" }
|
||||
|
|
62
README.md
62
README.md
|
@ -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.
|
Binary file not shown.
After Width: | Height: | Size: 3.6 MiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -5,3 +5,5 @@ Directory appPath;
|
|||
Directory walletsDirectory;
|
||||
String appVersion;
|
||||
SharedPreferences prefs;
|
||||
String endPointGVA;
|
||||
int ramSys;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
// }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
|
|
|
@ -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))),
|
||||
)
|
||||
])))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
])));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
},
|
||||
)),
|
||||
)
|
||||
]))),
|
||||
])))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 == '');
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)?
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
41
pubspec.lock
41
pubspec.lock
|
@ -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:
|
||||
|
|
10
pubspec.yaml
10
pubspec.yaml
|
@ -5,7 +5,7 @@ description: A new Flutter project.
|
|||
# pub.dev using `pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
version: 0.0.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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 !"
|
Loading…
Reference in New Issue