feat: add methode to store cs+ avatar (disabled due to sr25519 cryptotype)

This commit is contained in:
poka 2023-12-09 21:00:45 +01:00
parent 3e18df973b
commit 3458702631
5 changed files with 220 additions and 116 deletions

View File

@ -2,10 +2,13 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart'; import 'package:gecko/globals.dart';
import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/substrate_sdk.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:crypto/crypto.dart';
// import 'package:http/http.dart' as http; // import 'package:http/http.dart' as http;
class CesiumPlusProvider with ChangeNotifier { class CesiumPlusProvider with ChangeNotifier {
@ -13,107 +16,186 @@ class CesiumPlusProvider with ChangeNotifier {
CancelToken avatarCancelToken = CancelToken(); CancelToken avatarCancelToken = CancelToken();
Future<List> _buildQuery(String address) async { final Map<String, String> _headers = {
var queryGetAvatar = json.encode({ 'Content-type': 'application/json',
"query": { 'Accept': 'application/json',
"bool": { };
"should": [
{ // List _buildQueryGetAvatar(String pubkeyV1) {
"match": { // final queryGetAvatar = json.encode({
'_id': {"query": address, "boost": 2} // "query": {
} // "bool": {
}, // "should": [
{ // {
"prefix": {'_id': address} // "match": {
} // '_id': {"query": pubkeyV1, "boost": 1}
] // }
} // },
}, // {
"highlight": { // "prefix": {'_id': pubkeyV1}
"fields": {"title": {}, "tags": {}} // }
}, // ]
"from": 0, // }
"size": 100, // },
"_source": [ // "_source": [
"title", // "avatar",
"avatar", // "avatar._content_type",
"avatar._content_type", // ],
"description", // "indices_boost": {"user": 1, "page": 1, "group": 0.01}
"city", // });
"address",
"socials.url", // const requestUrl = "/user,page,group/profile,record/_search";
"creationTime", // final podRequest = cesiumPod + requestUrl;
"membersCount",
"type" // return [podRequest, queryGetAvatar];
], // }
"indices_boost": {"user": 100, "page": 1, "group": 0.01}
Future<List> _buildQuerySetAvatar(
String pubkeyV1, String address, String avatar) async {
int timeSent = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final queryGetAvatar = json.encode({
"avatar": {"_content": avatar, "_content_type": "image/png"},
"time": timeSent,
"issuer": pubkeyV1,
"version": 2,
"tags": []
}); });
String requestUrl = "/user,page,group/profile,record/_search"; final requestUrl =
String podRequest = cesiumPod + requestUrl; "/user/profile?pubkey=$pubkeyV1/_update?pubkey=$pubkeyV1";
final podRequest = cesiumPod + requestUrl;
Map<String, String> headers = { final signedDocument = await signDoc(queryGetAvatar, address);
'Content-type': 'application/json',
'Accept': 'application/json', return [podRequest, signedDocument];
}
Future<String> signDoc(String document, String address) async {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
final hashDocBytes = utf8.encode(document);
final hashDoc = sha256.convert(hashDocBytes);
final hashDocHex = hashDoc.toString().toUpperCase();
// Generate signature of document
final signature = await sub.signCsPlusDocument(hashDocHex, address);
// Build final document
final Map<String, dynamic> data = {
'hash': hashDocHex,
'signature': signature
};
final signJSON = jsonEncode(data);
final Map<String, dynamic> finalJSON = {
...jsonDecode(signJSON),
...jsonDecode(document)
}; };
return [podRequest, queryGetAvatar, headers]; return jsonEncode(finalJSON);
} }
// Future<String> getName(String address) async {
// String? name;
Future<String> getName(String address) async { // if (g1WalletsBox.get(address)?.csName != null) {
String? name; // return g1WalletsBox.get(address)!.csName!;
// }
if (g1WalletsBox.get(address)?.csName != null) { // List queryOptions = await _buildQueryName(address);
return g1WalletsBox.get(address)!.csName!;
}
List queryOptions = await _buildQuery(address); // var dio = Dio();
// late Response response;
// try {
// response = await dio.post(
// queryOptions[0],
// data: queryOptions[1],
// options: Options(
// headers: queryOptions[2],
// sendTimeout: const Duration(seconds: 3),
// receiveTimeout: const Duration(seconds: 5),
// ),
// );
// // response = await http.post((Uri.parse(queryOptions[0])),
// // body: queryOptions[1], headers: queryOptions[2]);
// } catch (e) {
// log.e(e);
// }
var dio = Dio(); // if (response.data['hits']['hits'].toString() == '[]') {
late Response response; // return '';
try { // }
response = await dio.post( // final bool nameExist =
queryOptions[0], // response.data['hits']['hits'][0]['_source'].containsKey("title");
data: queryOptions[1], // if (!nameExist) {
options: Options( // return '';
headers: queryOptions[2], // }
sendTimeout: const Duration(seconds: 3), // name = response.data['hits']['hits'][0]['_source']['title'];
receiveTimeout: const Duration(seconds: 5),
),
);
// response = await http.post((Uri.parse(queryOptions[0])),
// body: queryOptions[1], headers: queryOptions[2]);
} catch (e) {
log.e(e);
}
if (response.data['hits']['hits'].toString() == '[]') { // name ??= '';
return ''; // g1WalletsBox.get(address)!.csName = name;
}
final bool nameExist =
response.data['hits']['hits'][0]['_source'].containsKey("title");
if (!nameExist) {
return '';
}
name = response.data['hits']['hits'][0]['_source']['title'];
name ??= ''; // return name;
g1WalletsBox.get(address)!.csName = name; // }
return name;
}
Future<Image> getAvatar(String address, double size) async { Future<Image> getAvatar(String address, double size) async {
return defaultAvatar(size);
// final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
// if (await isAvatarExist(address)) {
// return await getAvatarLocal(address, size);
// }
// final pubkeyV1 = await sub.addressToPubkeyB58(address);
// var dio = Dio();
// List queryOptions = _buildQueryGetAvatar(pubkeyV1);
// late Response response;
// try {
// response = await dio
// .post(queryOptions[0],
// data: queryOptions[1],
// options: Options(
// headers: _headers,
// sendTimeout: const Duration(seconds: 4),
// receiveTimeout: const Duration(seconds: 15),
// ),
// cancelToken: avatarCancelToken)
// .timeout(
// const Duration(seconds: 15),
// );
// } catch (e) {
// log.e(e);
// }
// if (response.data['hits']['hits'].toString() == '[]' ||
// !response.data['hits']['hits'][0]['_source'].containsKey("avatar")) {
// return defaultAvatar(size);
// }
// final avatar =
// response.data['hits']['hits'][0]['_source']['avatar']['_content'];
// final avatarFile = await saveAvatar(address, avatar);
// final finalAvatar = Image.file(
// avatarFile,
// height: size,
// fit: BoxFit.fitWidth,
// );
// return finalAvatar;
}
Future<bool> setAvatar(String address, String avatarPath) async {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false); final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
if (await isAvatarExist(address)) {
return await getAvatarLocal(address, size);
}
final pubkeyV1 = await sub.addressToPubkeyB58(address); final pubkeyV1 = await sub.addressToPubkeyB58(address);
var dio = Dio(); var dio = Dio();
final Uint8List avatarBytes = await File(avatarPath).readAsBytes();
final avatarString = base64Encode(avatarBytes);
List queryOptions = await _buildQuery(pubkeyV1); List queryOptions =
await _buildQuerySetAvatar(pubkeyV1, address, avatarString);
log.d(queryOptions[0]);
log.d(jsonDecode(queryOptions[1]));
late Response response; late Response response;
try { try {
@ -121,7 +203,7 @@ class CesiumPlusProvider with ChangeNotifier {
.post(queryOptions[0], .post(queryOptions[0],
data: queryOptions[1], data: queryOptions[1],
options: Options( options: Options(
headers: queryOptions[2], headers: _headers,
sendTimeout: const Duration(seconds: 4), sendTimeout: const Duration(seconds: 4),
receiveTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 15),
), ),
@ -129,29 +211,12 @@ class CesiumPlusProvider with ChangeNotifier {
.timeout( .timeout(
const Duration(seconds: 15), const Duration(seconds: 15),
); );
// response = await http.post((Uri.parse(queryOptions[0])), log.d(response.data);
// body: queryOptions[1], headers: queryOptions[2]); return response.statusCode == 200;
} catch (e) { } catch (e) {
log.e(e); log.e(e);
return false;
} }
if (response.data['hits']['hits'].toString() == '[]' ||
!response.data['hits']['hits'][0]['_source'].containsKey("avatar")) {
return defaultAvatar(size);
}
final avatar =
response.data['hits']['hits'][0]['_source']['avatar']['_content'];
final avatarFile = await saveAvatar(address, avatar);
final finalAvatar = Image.file(
avatarFile,
height: size,
fit: BoxFit.fitWidth,
);
return finalAvatar;
} }
Future<String> getLocalPath() async { Future<String> getLocalPath() async {
@ -188,7 +253,9 @@ class CesiumPlusProvider with ChangeNotifier {
Future deleteAvatarFolder() async { Future deleteAvatarFolder() async {
final path = await getLocalPath(); final path = await getLocalPath();
final avatarFolder = Directory('$path/avatars/'); final avatarFolder = Directory('$path/avatars/');
await avatarFolder.delete(recursive: true); if (await avatarFolder.exists()) {
await avatarFolder.delete(recursive: true);
}
} }
} }

View File

@ -136,6 +136,42 @@ class SubstrateSdk with ChangeNotifier {
return res?.signature ?? ''; return res?.signature ?? '';
} }
Future<String> signCsPlusDocument(String document, String address) async {
final myWallets =
Provider.of<MyWalletsProvider>(homeContext, listen: false);
final messageToSign = Uint8List.fromList(document.codeUnits);
// final pubkeyV1 = json.decode(document)['issuer'];
// final address = await pubkeyV1ToAddress(pubkeyV1);
// final walletData = myWallets.getWalletDataByAddress(address);
// final derivationPath =
// walletData!.derivation == null ? '' : "//${walletData.derivation}";
// final seed = await getSeed(address, myWallets.pinCode);
// final addressEd25519 = await importAccount(
// mnemonic: seed,
// password: 'AAAAA',
// cryptoType: CryptoType.ed25519,
// derivePath: derivationPath);
// final pubkeyEd25519 = await addressToPubkeyB58(addressEd25519);
final signatureString =
await _signMessage(messageToSign, address, myWallets.pinCode);
final signatureInt = HEX.decode(signatureString.substring(2));
final signature64 = base64Encode(signatureInt);
// await deleteAccounts([addressEd25519]);
// log.d("""
// $addressEd25519
// $pubkeyEd25519
// $derivationPath
// $signature64
// """);
return signature64;
}
//////////////////////////////////////////// ////////////////////////////////////////////
////////// 2: GET ONCHAIN STORAGE ////////// ////////// 2: GET ONCHAIN STORAGE //////////
//////////////////////////////////////////// ////////////////////////////////////////////
@ -453,9 +489,12 @@ class SubstrateSdk with ChangeNotifier {
return Base58Encode(await addressToPubkey(address)); return Base58Encode(await addressToPubkey(address));
} }
// Future pubkeyToAddress(String pubkey) async { Future<String> pubkeyV1ToAddress(String pubkey) async {
// await sdk.api.account.encodeAddress([pubkey]); final pubkeyByte = Base58Decode(pubkey);
// } final String pubkeyHex = '0x${HEX.encode(pubkeyByte)}';
final address = await sdk.api.account.encodeAddress([pubkeyHex]);
return address!.keys.first;
}
Future initCurrencyParameters() async { Future initCurrencyParameters() async {
try { try {
@ -681,7 +720,8 @@ class SubstrateSdk with ChangeNotifier {
Future<String> importAccount( Future<String> importAccount(
{String mnemonic = '', {String mnemonic = '',
String derivePath = '', String derivePath = '',
required String password}) async { required String password,
CryptoType cryptoType = CryptoType.sr25519}) async {
const keytype = KeyType.mnemonic; const keytype = KeyType.mnemonic;
if (mnemonic != '') generatedMnemonic = mnemonic; if (mnemonic != '') generatedMnemonic = mnemonic;
@ -695,7 +735,7 @@ class SubstrateSdk with ChangeNotifier {
name: derivePath, name: derivePath,
password: password, password: password,
derivePath: derivePath, derivePath: derivePath,
cryptoType: CryptoType.sr25519) cryptoType: cryptoType)
.catchError((e) { .catchError((e) {
importIsLoading = false; importIsLoading = false;
notifyListeners(); notifyListeners();
@ -752,12 +792,6 @@ class SubstrateSdk with ChangeNotifier {
return seedText; return seedText;
} }
int getDerivationNumber(String address) {
final account = getKeypair(address);
final deriveNbr = account.name!.split('//')[1];
return int.parse(deriveNbr);
}
Future<KeyPairData?> changePassword(BuildContext context, String address, Future<KeyPairData?> changePassword(BuildContext context, String address,
String passOld, String passNew) async { String passOld, String passNew) async {
final account = getKeypair(address); final account = getKeypair(address);

View File

@ -286,6 +286,8 @@ class WalletOptions extends StatelessWidget {
if (newPath != '') { if (newPath != '') {
wallet.imageCustomPath = newPath; wallet.imageCustomPath = newPath;
walletBox.put(wallet.key, wallet); walletBox.put(wallet.key, wallet);
// Uncomment to enable Cs+ avatar storage
// CesiumPlusProvider().setAvatar(wallet.address, newPath);
} }
walletProvider.reload(); walletProvider.reload();
}, },

View File

@ -298,7 +298,7 @@ packages:
source: hosted source: hosted
version: "0.3.3+7" version: "0.3.3+7"
crypto: crypto:
dependency: transitive dependency: "direct main"
description: description:
name: crypto name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab

View File

@ -59,6 +59,7 @@ dependencies:
tutorial_coach_mark: ^1.2.8 tutorial_coach_mark: ^1.2.8
confetti: ^0.7.0 confetti: ^0.7.0
url_launcher: ^6.1.11 url_launcher: ^6.1.11
crypto: ^3.0.3
dev_dependencies: dev_dependencies:
# flutter_launcher_icons: ^0.9.2 # flutter_launcher_icons: ^0.9.2