From 345870263126bd0eede5922b3d86b17a479e6711 Mon Sep 17 00:00:00 2001 From: poka Date: Sat, 9 Dec 2023 21:00:45 +0100 Subject: [PATCH] feat: add methode to store cs+ avatar (disabled due to sr25519 cryptotype) --- lib/providers/cesium_plus.dart | 275 ++++++++++++++-------- lib/providers/substrate_sdk.dart | 56 ++++- lib/screens/myWallets/wallet_options.dart | 2 + pubspec.lock | 2 +- pubspec.yaml | 1 + 5 files changed, 220 insertions(+), 116 deletions(-) diff --git a/lib/providers/cesium_plus.dart b/lib/providers/cesium_plus.dart index 4a798f0..d8ddb76 100644 --- a/lib/providers/cesium_plus.dart +++ b/lib/providers/cesium_plus.dart @@ -2,10 +2,13 @@ import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; +import 'package:crypto/crypto.dart'; + // import 'package:http/http.dart' as http; class CesiumPlusProvider with ChangeNotifier { @@ -13,107 +16,186 @@ class CesiumPlusProvider with ChangeNotifier { CancelToken avatarCancelToken = CancelToken(); - Future _buildQuery(String address) async { - var queryGetAvatar = json.encode({ - "query": { - "bool": { - "should": [ - { - "match": { - '_id': {"query": address, "boost": 2} - } - }, - { - "prefix": {'_id': address} - } - ] - } - }, - "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} + final Map _headers = { + 'Content-type': 'application/json', + 'Accept': 'application/json', + }; + + // List _buildQueryGetAvatar(String pubkeyV1) { + // final queryGetAvatar = json.encode({ + // "query": { + // "bool": { + // "should": [ + // { + // "match": { + // '_id': {"query": pubkeyV1, "boost": 1} + // } + // }, + // { + // "prefix": {'_id': pubkeyV1} + // } + // ] + // } + // }, + // "_source": [ + // "avatar", + // "avatar._content_type", + // ], + // "indices_boost": {"user": 1, "page": 1, "group": 0.01} + // }); + + // const requestUrl = "/user,page,group/profile,record/_search"; + // final podRequest = cesiumPod + requestUrl; + + // return [podRequest, queryGetAvatar]; + // } + + Future _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"; - String podRequest = cesiumPod + requestUrl; + final requestUrl = + "/user/profile?pubkey=$pubkeyV1/_update?pubkey=$pubkeyV1"; + final podRequest = cesiumPod + requestUrl; - Map headers = { - 'Content-type': 'application/json', - 'Accept': 'application/json', + final signedDocument = await signDoc(queryGetAvatar, address); + + return [podRequest, signedDocument]; + } + + Future signDoc(String document, String address) async { + final sub = Provider.of(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 data = { + 'hash': hashDocHex, + 'signature': signature + }; + final signJSON = jsonEncode(data); + final Map finalJSON = { + ...jsonDecode(signJSON), + ...jsonDecode(document) }; - return [podRequest, queryGetAvatar, headers]; + return jsonEncode(finalJSON); } + // Future getName(String address) async { + // String? name; - Future getName(String address) async { - String? name; + // if (g1WalletsBox.get(address)?.csName != null) { + // return g1WalletsBox.get(address)!.csName!; + // } - if (g1WalletsBox.get(address)?.csName != null) { - return g1WalletsBox.get(address)!.csName!; - } + // List queryOptions = await _buildQueryName(address); - 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(); - 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); - } + // if (response.data['hits']['hits'].toString() == '[]') { + // return ''; + // } + // final bool nameExist = + // response.data['hits']['hits'][0]['_source'].containsKey("title"); + // if (!nameExist) { + // return ''; + // } + // name = response.data['hits']['hits'][0]['_source']['title']; - if (response.data['hits']['hits'].toString() == '[]') { - return ''; - } - final bool nameExist = - response.data['hits']['hits'][0]['_source'].containsKey("title"); - if (!nameExist) { - return ''; - } - name = response.data['hits']['hits'][0]['_source']['title']; + // name ??= ''; + // g1WalletsBox.get(address)!.csName = name; - name ??= ''; - g1WalletsBox.get(address)!.csName = name; - - return name; - } + // return name; + // } Future getAvatar(String address, double size) async { + return defaultAvatar(size); + // final sub = Provider.of(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 setAvatar(String address, String avatarPath) async { final sub = Provider.of(homeContext, listen: false); - if (await isAvatarExist(address)) { - return await getAvatarLocal(address, size); - } final pubkeyV1 = await sub.addressToPubkeyB58(address); - 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; try { @@ -121,7 +203,7 @@ class CesiumPlusProvider with ChangeNotifier { .post(queryOptions[0], data: queryOptions[1], options: Options( - headers: queryOptions[2], + headers: _headers, sendTimeout: const Duration(seconds: 4), receiveTimeout: const Duration(seconds: 15), ), @@ -129,29 +211,12 @@ class CesiumPlusProvider with ChangeNotifier { .timeout( const Duration(seconds: 15), ); - // response = await http.post((Uri.parse(queryOptions[0])), - // body: queryOptions[1], headers: queryOptions[2]); + log.d(response.data); + return response.statusCode == 200; } catch (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 getLocalPath() async { @@ -188,7 +253,9 @@ class CesiumPlusProvider with ChangeNotifier { Future deleteAvatarFolder() async { final path = await getLocalPath(); final avatarFolder = Directory('$path/avatars/'); - await avatarFolder.delete(recursive: true); + if (await avatarFolder.exists()) { + await avatarFolder.delete(recursive: true); + } } } diff --git a/lib/providers/substrate_sdk.dart b/lib/providers/substrate_sdk.dart index 0c1f558..0eb1477 100644 --- a/lib/providers/substrate_sdk.dart +++ b/lib/providers/substrate_sdk.dart @@ -136,6 +136,42 @@ class SubstrateSdk with ChangeNotifier { return res?.signature ?? ''; } + Future signCsPlusDocument(String document, String address) async { + final myWallets = + Provider.of(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 ////////// //////////////////////////////////////////// @@ -453,9 +489,12 @@ class SubstrateSdk with ChangeNotifier { return Base58Encode(await addressToPubkey(address)); } - // Future pubkeyToAddress(String pubkey) async { - // await sdk.api.account.encodeAddress([pubkey]); - // } + Future pubkeyV1ToAddress(String pubkey) async { + 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 { try { @@ -681,7 +720,8 @@ class SubstrateSdk with ChangeNotifier { Future importAccount( {String mnemonic = '', String derivePath = '', - required String password}) async { + required String password, + CryptoType cryptoType = CryptoType.sr25519}) async { const keytype = KeyType.mnemonic; if (mnemonic != '') generatedMnemonic = mnemonic; @@ -695,7 +735,7 @@ class SubstrateSdk with ChangeNotifier { name: derivePath, password: password, derivePath: derivePath, - cryptoType: CryptoType.sr25519) + cryptoType: cryptoType) .catchError((e) { importIsLoading = false; notifyListeners(); @@ -752,12 +792,6 @@ class SubstrateSdk with ChangeNotifier { return seedText; } - int getDerivationNumber(String address) { - final account = getKeypair(address); - final deriveNbr = account.name!.split('//')[1]; - return int.parse(deriveNbr); - } - Future changePassword(BuildContext context, String address, String passOld, String passNew) async { final account = getKeypair(address); diff --git a/lib/screens/myWallets/wallet_options.dart b/lib/screens/myWallets/wallet_options.dart index e9eabab..18f93ad 100644 --- a/lib/screens/myWallets/wallet_options.dart +++ b/lib/screens/myWallets/wallet_options.dart @@ -286,6 +286,8 @@ class WalletOptions extends StatelessWidget { if (newPath != '') { wallet.imageCustomPath = newPath; walletBox.put(wallet.key, wallet); + // Uncomment to enable Cs+ avatar storage + // CesiumPlusProvider().setAvatar(wallet.address, newPath); } walletProvider.reload(); }, diff --git a/pubspec.lock b/pubspec.lock index 64b91f1..e63bcb1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -298,7 +298,7 @@ packages: source: hosted version: "0.3.3+7" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab diff --git a/pubspec.yaml b/pubspec.yaml index 532f288..f9e54cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: tutorial_coach_mark: ^1.2.8 confetti: ^0.7.0 url_launcher: ^6.1.11 + crypto: ^3.0.3 dev_dependencies: # flutter_launcher_icons: ^0.9.2