Implement identity workflow

This commit is contained in:
poka 2022-05-24 16:51:40 +02:00
parent adcb876203
commit 14f784fdc9
14 changed files with 280 additions and 57 deletions

View File

@ -7,6 +7,9 @@ import 'package:hive/hive.dart';
import 'package:logger/logger.dart';
import 'package:shared_preferences/shared_preferences.dart';
// Version of box data
const int dataVersion = 1;
// Files paths
Directory? appPath;

View File

@ -5,28 +5,32 @@ part 'wallet_data.g.dart';
@HiveType(typeId: 0)
class WalletData extends HiveObject {
@HiveField(0)
int? chest;
int? version;
@HiveField(1)
String? address;
int? chest;
@HiveField(2)
int? number;
String? address;
@HiveField(3)
String? name;
int? number;
@HiveField(4)
int? derivation;
String? name;
@HiveField(5)
String? imageName;
int? derivation;
@HiveField(6)
String? imageName;
@HiveField(7)
File? imageFile;
WalletData(
{this.chest,
{this.version,
this.chest,
this.address,
this.number,
this.name,

View File

@ -17,33 +17,36 @@ class WalletDataAdapter extends TypeAdapter<WalletData> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return WalletData(
chest: fields[0] as int?,
address: fields[1] as String?,
number: fields[2] as int?,
name: fields[3] as String?,
derivation: fields[4] as int?,
imageName: fields[5] as String?,
imageFile: fields[6] as File?,
version: fields[0] as int?,
chest: fields[1] as int?,
address: fields[2] as String?,
number: fields[3] as int?,
name: fields[4] as String?,
derivation: fields[5] as int?,
imageName: fields[6] as String?,
imageFile: fields[7] as File?,
);
}
@override
void write(BinaryWriter writer, WalletData obj) {
writer
..writeByte(7)
..writeByte(8)
..writeByte(0)
..write(obj.chest)
..write(obj.version)
..writeByte(1)
..write(obj.address)
..write(obj.chest)
..writeByte(2)
..write(obj.number)
..write(obj.address)
..writeByte(3)
..write(obj.name)
..write(obj.number)
..writeByte(4)
..write(obj.derivation)
..write(obj.name)
..writeByte(5)
..write(obj.imageName)
..write(obj.derivation)
..writeByte(6)
..write(obj.imageName)
..writeByte(7)
..write(obj.imageFile);
}

View File

@ -2,6 +2,9 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:provider/provider.dart';
class ChestProvider with ChangeNotifier {
void rebuildWidget() {
@ -10,8 +13,9 @@ class ChestProvider with ChangeNotifier {
Future deleteChest(context, ChestData _chest) async {
final bool? _answer = await (_confirmDeletingChest(context, _chest.name));
SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
if (_answer!) {
await _sub.deleteAccounts(getChestWallets(_chest));
await chestBox.delete(_chest.key);
if (chestBox.isEmpty) {
await configBox.put('currentChest', 0);
@ -28,6 +32,17 @@ class ChestProvider with ChangeNotifier {
}
}
List<String> getChestWallets(ChestData _chest) {
List<String> toDelete = [];
log.d(_chest.key);
walletBox.toMap().forEach((key, WalletData value) {
if (value.chest == _chest.key) {
toDelete.add(value.address!);
}
});
return toDelete;
}
Future<bool?> _confirmDeletingChest(context, String? _walletName) async {
return showDialog<bool>(
context: context,

View File

@ -81,6 +81,7 @@ class GenerateWalletsProvider with ChangeNotifier {
int? chestKey = chestBox.keys.last;
WalletData myWallet = WalletData(
version: dataVersion,
chest: chestKey,
address: address,
number: 0,

View File

@ -169,6 +169,7 @@ class MyWalletsProvider with ChangeNotifier {
context, _currentChest.address!, _newDerivationNbr, pinCode);
WalletData newWallet = WalletData(
version: dataVersion,
chest: _chest,
address: address,
number: _newWalletNbr,

View File

@ -232,6 +232,13 @@ class SubstrateSdk with ChangeNotifier {
}
}
Future<void> deleteAccounts(List<String> address) async {
for (var a in address) {
final account = getKeypair(a);
await sdk.api.keyring.deleteAccount(keyring, account);
}
}
Future<String> generateMnemonic({String lang = appLang}) async {
final gen = await sdk.api.keyring.generateMnemonic(ss58);
generatedMnemonic = gen.mnemonic!;
@ -294,6 +301,11 @@ class SubstrateSdk with ChangeNotifier {
required String password}) async {
setCurrentWallet(fromAddress);
log.d(keyring.current.address);
log.d(fromAddress);
log.d(password);
log.d(await checkPassword(fromAddress, password));
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
@ -318,25 +330,76 @@ class SubstrateSdk with ChangeNotifier {
}
}
Future<String> idtyStatus(String address) async {
// var tata = await sdk.webView!
// .evalJavascript('api.query.system.account("$address")');
var idtyIndex = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")');
if (idtyIndex == null) {
return 'noid';
}
final idtyStatus = await sdk.webView!
.evalJavascript('api.query.identity.identities($idtyIndex)');
if (idtyStatus != null) {
final String _status = idtyStatus['status'];
log.d(_status);
return (_status);
} else {
return 'expired';
}
}
Future<String> confirmIdentity(
String address, String name, String password) async {
// Confirm identity
setCurrentWallet(address);
log.d('idty: ' + keyring.current.address!);
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
final txInfo = TxInfoData(
'identity',
'confirmIdentity',
sender,
);
try {
final tata = await sdk.api.tx.signAndSend(
txInfo,
[name],
password,
);
log.d(tata);
return 'confirmed';
} on Exception catch (e) {
log.e(e);
// if (e.toString() == 'Exception: password check failed') {
// throw PasswordException('Bad password');
// }
return e.toString();
}
}
Future<String> derive(
BuildContext context, String address, int number, String password) async {
final keypair = getKeypair(address);
final seedMap =
await keyring.store.getDecryptedSeed(keypair.pubKey, password);
print(seedMap);
if (seedMap?['type'] != 'mnemonic') return '';
final List seedList = seedMap!['seed'].split('//');
generatedMnemonic = seedList[0];
int sourceDerivation = -1; // To get derivation number of this account
if (seedList.length > 1) {
sourceDerivation = int.parse(seedList[1]);
}
print(generatedMnemonic);
print(sourceDerivation);
return await importAccount(fromMnemonic: true, derivePath: '//$number');
return await importAccount(
fromMnemonic: true, derivePath: '//$number', password: password);
}
Future<bool> isMnemonicValid(String mnemonic) async {
@ -392,3 +455,8 @@ String getShortPubkey(String pubkey) {
return pubkeyShort;
}
class PasswordException implements Exception {
String cause;
PasswordException(this.cause);
}

View File

@ -148,6 +148,124 @@ class WalletOptionsProvider with ChangeNotifier {
}
}
Widget idtyStatus(BuildContext context, String address,
{bool isOwner = false}) {
return Consumer<SubstrateSdk>(builder: (context, _sub, _) {
return FutureBuilder(
future: _sub.idtyStatus(address),
initialData: '...',
builder: (context, snapshot) {
switch (snapshot.data.toString()) {
case 'noid':
{
return Column(children: const <Widget>[
Text(
'Aucune identité',
style: TextStyle(fontSize: 18, color: Colors.black),
),
]);
}
case 'Created':
{
return Column(children: <Widget>[
isOwner
? InkWell(
child: const Text(
'Identité créé, cliquez pour la confirmer',
style:
TextStyle(fontSize: 18, color: Colors.black),
),
onTap: () async {
await validateIdentity(context);
},
)
: const Text(
'Identité créé',
style: TextStyle(fontSize: 18, color: Colors.black),
),
]);
}
case 'ConfirmedByOwner':
{
return Column(children: const <Widget>[
Text(
'Identité confirmé',
style: TextStyle(fontSize: 18, color: Colors.black),
),
]);
}
case 'Validated':
{
return Column(children: const <Widget>[
Text(
'Membre validé !',
style: TextStyle(fontSize: 18, color: Colors.black),
),
]);
}
case 'expired':
{
return Column(children: const <Widget>[
Text(
'Identité expiré',
style: TextStyle(fontSize: 18, color: Colors.black),
),
]);
}
}
return SizedBox(
width: 230,
child: Column(children: const <Widget>[
Text(
'Statut inconnu',
style: TextStyle(fontSize: 18, color: Colors.black),
),
]),
);
});
});
}
Future<String?> validateIdentity(BuildContext context) async {
TextEditingController idtyName = TextEditingController();
SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
return showDialog<String>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Confirmez votre identité'),
content: SizedBox(
height: 100,
child: Column(children: [
const Text('Nom:'),
TextField(
controller: idtyName,
)
]),
),
actions: <Widget>[
TextButton(
child: const Text("Valider"),
onPressed: () async {
_sub.confirmIdentity(_walletOptions.address.text, idtyName.text,
_myWalletProvider.pinCode);
Navigator.pop(context);
},
),
],
);
},
);
}
void reloadBuild() {
notifyListeners();
}
@ -167,9 +285,10 @@ class WalletOptionsProvider with ChangeNotifier {
}
}
Widget balance(BuildContext context, String address, double size) {
String balanceCache = '';
// Map<String, String> balanceCache = {};
String balanceCache = '';
Widget balance(BuildContext context, String address, double size) {
return Column(children: <Widget>[
Consumer<SubstrateSdk>(builder: (context, _sdk, _) {
return FutureBuilder(

View File

@ -58,7 +58,7 @@ class ChooseWalletScreen extends StatelessWidget {
destAddress: _walletViewProvider.outputPubkey.text,
amount:
double.parse(_walletViewProvider.payAmount.text),
password: pin);
password: pin.toUpperCase());
await paymentsResult(context, resultPay);
},
child: const Text(

View File

@ -140,8 +140,9 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
fromMnemonic: true,
mnemonic: _generateWalletProvider
.generatedMnemonic!,
password:
_generateWalletProvider.pin.text,
password: _generateWalletProvider
.pin.text
.toUpperCase(),
derivePath: '//2');
await _generateWalletProvider.storeHDWChest(
address,

View File

@ -26,7 +26,7 @@ class GenerateFastChestScreen extends StatelessWidget {
_generateWalletProvider.pin.text = kDebugMode && debugPin
? 'AAAAA'
: _generateWalletProvider.changePinCode(reload: false);
: _generateWalletProvider.changePinCode(reload: false).toUpperCase();
return WillPopScope(
onWillPop: () {

View File

@ -90,6 +90,10 @@ class WalletOptions extends StatelessWidget {
walletName(walletProvider, _walletOptions),
SizedBox(height: isTall ? 5 : 0),
balance(context, walletProvider.address.text, 20),
SizedBox(height: isTall ? 5 : 0),
_walletOptions.idtyStatus(
context, _walletOptions.address.text,
isOwner: true),
]),
const Spacer(flex: 3),
]),
@ -212,8 +216,8 @@ class WalletOptions extends StatelessWidget {
walletProvider.isEditing
? 'assets/walletOptions/android-checkmark.png'
: 'assets/walletOptions/edit.png',
width: 20,
height: 20),
width: 25,
height: 25),
),
),
),

View File

@ -23,7 +23,7 @@ class OnboardingStepThirteen extends StatelessWidget {
_generateWalletProvider.pin.text = kDebugMode && debugPin
? 'AAAAA'
: _generateWalletProvider.changePinCode(reload: false);
: _generateWalletProvider.changePinCode(reload: false).toUpperCase();
return Scaffold(
appBar: AppBar(

View File

@ -27,7 +27,6 @@ class WalletViewScreen extends StatelessWidget {
Provider.of<WalletsProfilesProvider>(context, listen: false);
CesiumPlusProvider _cesiumPlusProvider =
Provider.of<CesiumPlusProvider>(context, listen: false);
_historyProvider.pubkey = pubkey!;
return Scaffold(
@ -285,21 +284,21 @@ class WalletViewScreen extends StatelessWidget {
primary: orangeC, // background
onPrimary: Colors.white, // foreground
),
onPressed: _walletViewProvider.payAmount.text !=
''
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return UnlockingWallet(
wallet: defaultWallet,
action: "pay");
},
),
);
}
: null,
onPressed:
_walletViewProvider.payAmount.text != ''
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return UnlockingWallet(
wallet: defaultWallet,
action: "pay");
},
),
);
}
: null,
child: const Text(
'Effectuer le virement',
style: TextStyle(
@ -325,6 +324,9 @@ class WalletViewScreen extends StatelessWidget {
CesiumPlusProvider _cesiumPlusProvider) {
const double _avatarSize = 140;
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
return Column(children: <Widget>[
Container(
height: 10,
@ -365,9 +367,11 @@ class WalletViewScreen extends StatelessWidget {
const SizedBox(height: 25),
Consumer<WalletOptionsProvider>(
builder: (context, walletProvider, _) {
return balance(context, pubkey!, 20);
return balance(context, pubkey!, 22);
}),
////
const SizedBox(height: 10),
_walletOptions.idtyStatus(context, pubkey!, isOwner: false),
// if (username == null &&
// g1WalletsBox.get(pubkey)?.username == null)
// Query(