diff --git a/assets/images/contract-agreement.png b/assets/images/contract-agreement.png new file mode 100644 index 0000000..0fa1cfd Binary files /dev/null and b/assets/images/contract-agreement.png differ diff --git a/lib/bloc/verify/verify_bloc.dart b/lib/bloc/verify/verify_bloc.dart new file mode 100644 index 0000000..c7bb454 --- /dev/null +++ b/lib/bloc/verify/verify_bloc.dart @@ -0,0 +1,15 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +part 'verify_event.dart'; +part 'verify_state.dart'; + +class VerifyBloc extends Bloc { + VerifyBloc() : super(VerifyInitial()) { + on((event, emit) { + // TODO: implement event handler + }); + } +} diff --git a/lib/bloc/verify/verify_event.dart b/lib/bloc/verify/verify_event.dart new file mode 100644 index 0000000..3446344 --- /dev/null +++ b/lib/bloc/verify/verify_event.dart @@ -0,0 +1,4 @@ +part of 'verify_bloc.dart'; + +@immutable +abstract class VerifyEvent {} diff --git a/lib/bloc/verify/verify_state.dart b/lib/bloc/verify/verify_state.dart new file mode 100644 index 0000000..4b0a081 --- /dev/null +++ b/lib/bloc/verify/verify_state.dart @@ -0,0 +1,6 @@ +part of 'verify_bloc.dart'; + +@immutable +abstract class VerifyState {} + +class VerifyInitial extends VerifyState {} diff --git a/lib/pages/qr_result_page.dart b/lib/pages/qr_result_page.dart index 04327cb..dc1109e 100644 --- a/lib/pages/qr_result_page.dart +++ b/lib/pages/qr_result_page.dart @@ -1,81 +1,106 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:iblock/services/secure_storage_service.dart'; +import 'package:iblock/services/user_contract_service.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import './error_page.dart'; import '../widgets/forms/button.dart'; -class QRResultPage extends StatelessWidget { +class QRResultPage extends StatefulWidget { final Barcode result; const QRResultPage(this.result, {Key? key}) : super(key: key); + @override + State createState() => _QRResultPageState(); +} + +class _QRResultPageState extends State { + Map processQR(Barcode qr) { + Map data = jsonDecode(qr.code!); + return data; + } + @override Widget build(BuildContext context) { - try{ - print((jsonDecode(result.code!) as Map)['information'] - as List); + try { return SafeArea( child: Scaffold( appBar: AppBar( title: const Text('Requesting Information'), ), - body: - SizedBox( + body: SizedBox( width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: MediaQuery.of(context).size.width > MediaQuery.of(context).size.height? 1:4, - child: SingleChildScrollView( - child: Column( - children: ((jsonDecode(result.code!) - as Map)['information'] - as List) - .map((e) => Padding( - padding: const EdgeInsets.all(4.0), - child: Text( - e, - style: const TextStyle(fontSize: 14), + child: Column(crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: MediaQuery.of(context).size.width > + MediaQuery.of(context).size.height + ? 1 + : 4, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SingleChildScrollView( + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.6, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + const Expanded( + flex: 1, + child: Text( + "Following service provider request your approval for access your personal information")), + Expanded( + flex: 1, + child: Text( + processQR(widget.result)["verifier-name"], + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20), + ), + ), + const Expanded( + flex: 3, + child: SizedBox( + width: 300, + child: Image( + image: AssetImage( + 'assets/images/contract-agreement.png')), + ), + ), + ], ), - )) - .toList(), + ), ), )), - Expanded( - flex: 1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(2.0), - child: Button( - "Approve", - onPressed: () {}, - )), - Padding( - padding: const EdgeInsets.all(2.0), - child: Button( - "Decline", - onPressed: () {}, - color: Colors.red, - )), - ], - ), - ) - ], - ), - ) - // Center( - // child: Text(result.code != null ? 'Barcode Type: ${describeEnum(result.format)} Data: ${result.code}': 'No result'), - // ), - ), + Expanded(flex: 1, + child: Column(crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding(padding: const EdgeInsets.all(2.0), + child: Button("Approve", onPressed: () async{ + var data = processQR(widget.result); + var contractService = UserContractService(); + var contractAddress = await SecureStorageService.get("contract-address") as String; + var privateKey = await SecureStorageService.get("private-key") as String; + await contractService.verify(data['verifier-contract'], data['token'], contractAddress: contractAddress, privateKey: privateKey); + },)), + Padding(padding: const EdgeInsets.all(2.0), + child: Button("Decline", onPressed: () { + Navigator.popUntil( + context, ModalRoute.withName("/home")); + }, color: Colors.red,)), + ],),) + ],),) + // Center( + // child: Text(result.code != null ? 'Barcode Type: ${describeEnum(result.format)} Data: ${result.code}': 'No result'), + // ), + ),); + } catch(e){ + print(e); + return const ErrorPage( + message: "QR code not recognized!", ); } - catch(e){ - return const ErrorPage(message: "QR code not recognized!",); - } - } } diff --git a/lib/services/user_contract_service.dart b/lib/services/user_contract_service.dart index dd3ea56..3167180 100644 --- a/lib/services/user_contract_service.dart +++ b/lib/services/user_contract_service.dart @@ -103,7 +103,6 @@ class UserContractService{ params: [], sender: sender); - web3client.dispose(); return { 'Name': name[0] as String, 'Email': email[0] as String, @@ -116,6 +115,83 @@ class UserContractService{ catch(e){ throw Exception("Non authorized access"); } + finally{ + web3client.dispose(); + } + } + + Future _setUserProperty(String setFunctionName, String newValue, {required String contractAddress, required String privateKey}) async { + EthereumAddress contract = EthereumAddress.fromHex(contractAddress); + EthPrivateKey credentials = EthPrivateKey.fromHex(privateKey); + + Object abi = await _abiJSON; + + DeployedContract smartContract = DeployedContract(ContractAbi.fromJson(jsonEncode(abi), "User"), contract); + ContractFunction setFunction = smartContract.function(setFunctionName); + + Web3Client web3client = Web3Client(Config.rpcUrl, http.Client(), socketConnector: (){ + return IOWebSocketChannel.connect(Config.wsUrl).cast(); + }); + + try{ + var transactionId = await web3client.sendTransaction(credentials, Transaction.callContract(contract: smartContract, function: setFunction, parameters: [newValue])); + return transactionId; + } + catch(e){ + throw Exception("Failed due to $e"); + } + finally{ + web3client.dispose(); + } + } + + Future setName(String name, {required String contractAddress, required String privateKey}) async{ + return _setUserProperty("setName", name, contractAddress: contractAddress, privateKey: privateKey); + } + + Future setEmail(String email, {required String contractAddress, required String privateKey}) async{ + return _setUserProperty("setEmail", email, contractAddress: contractAddress, privateKey: privateKey); + } + + Future setCountry(String country, {required String contractAddress, required String privateKey}) async{ + return _setUserProperty("setCountry", country, contractAddress: contractAddress, privateKey: privateKey); + } + + Future setMobile(String mobile, {required String contractAddress, required String privateKey}) async{ + return _setUserProperty("setMobile", mobile, contractAddress: contractAddress, privateKey: privateKey); + } + + Future setGender(String gender, {required String contractAddress, required String privateKey}) async{ + return _setUserProperty("setGender", gender, contractAddress: contractAddress, privateKey: privateKey); + } + + Future verify(String verifierContractAddress, String token, {required String contractAddress, required String privateKey}) async{ + EthereumAddress contract = EthereumAddress.fromHex(contractAddress); + EthereumAddress verifierContract = EthereumAddress.fromHex(verifierContractAddress); + EthPrivateKey credentials = EthPrivateKey.fromHex(privateKey); + + Object abi = await _abiJSON; + + DeployedContract smartContract = DeployedContract(ContractAbi.fromJson(jsonEncode(abi), "User"), contract); + ContractFunction verifyFunction = smartContract.function("verify"); + + Web3Client web3client = Web3Client(Config.rpcUrl, http.Client(), socketConnector: (){ + return IOWebSocketChannel.connect(Config.wsUrl).cast(); + }); + + try{ + var transactionId = await web3client.sendTransaction( + credentials, + Transaction.callContract(contract: smartContract, function: verifyFunction, parameters: [verifierContract, contract, token]) + ); + return transactionId; + } + catch(e){ + throw Exception("Failed due to $e"); + } + finally{ + web3client.dispose(); + } } Object getAbiJson() { diff --git a/lib/widgets/qr_scanner.dart b/lib/widgets/qr_scanner.dart index 48e2136..c3e0724 100644 --- a/lib/widgets/qr_scanner.dart +++ b/lib/widgets/qr_scanner.dart @@ -94,8 +94,8 @@ class _QRScannerState extends State { setState(() { result = scanData; }); - controller.pauseCamera(); - Navigator.pushNamed(context, '/qrcode-result', arguments: scanData); + controller.pauseCamera() + .then((value) => Navigator.pushNamed(context, '/qrcode-result', arguments: scanData)); }); } diff --git a/pubspec.yaml b/pubspec.yaml index 219553b..1caf063 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,6 +82,7 @@ flutter: - assets/images/welcome2.png - assets/images/welcome3.png - assets/images/error.png + - assets/images/contract-agreement.png # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see diff --git a/test/services/user_contract_service_test.dart b/test/services/user_contract_service_test.dart index ad56939..1d078a0 100644 --- a/test/services/user_contract_service_test.dart +++ b/test/services/user_contract_service_test.dart @@ -29,4 +29,63 @@ void main(){ expect(service.getAll("0x949e1fB80027B3D9b7D33767A17a2B4ebfD1Cb73", "2ffdcdececb76f1c6ff826cbb4c0138cac3f5f2950540a1ff1a0530cd5f5063f"), throwsException); }); + + test('Test Setting Name: by the owner', () async{ + final service = UserContractService(); + await service.setName("test3", privateKey: "0xdb7a1fc3433dbe9d11b53357927dbfc80e3f9d5c211c6c29ce49acca3633dbf3", contractAddress: "0x547fda582EeEc9574BFF8Aa768786C15702AE0fD"); + + var result = await service.getAll("0x547fda582EeEc9574BFF8Aa768786C15702AE0fD", + "0xdb7a1fc3433dbe9d11b53357927dbfc80e3f9d5c211c6c29ce49acca3633dbf3"); + + expect(result, {"Name": "test3", "Email": "test@example.com", "Date of Birth": "2000/01/01", "Country": "Sri Lanka", "Phone": "+9412345678", "Gender": "Male"}); + + }); + + test('Testing verify function', () async{ + final service = UserContractService(); + String transactionId = await service.verify("0x1Ff8Ebe435cdD925EE58388c78a6b21f650247B6", "12345", privateKey: "0xdb7a1fc3433dbe9d11b53357927dbfc80e3f9d5c211c6c29ce49acca3633dbf3", contractAddress: "0x547fda582EeEc9574BFF8Aa768786C15702AE0fD"); + expect(transactionId, startsWith("0x")); + }); + + group('User contract functons', (){ + final service = UserContractService(); + late final String privateKey; + late final String contractAddress; + test('Testing deployment of user contract', () async{ + + var result = await service.deploy(name: "user1", email: "user1@iblock.com", dob: "2000/01/01", country: "Sri Lanka", mobile: "+9412345678", gender: "Male"); + + print(result); + privateKey = result['private-key'] as String; + contractAddress = result['contract-address'] as String; + }); + + test('Test fetching details from user contract: called by owner', () async{ + final service = UserContractService(); + var result = await service.getAll(contractAddress, privateKey); + print(result); + expect(result, {"Name": "user1", "Email": "user1@iblock.com", "Date of Birth": "2000/01/01", "Country": "Sri Lanka", "Phone": "+9412345678", "Gender": "Male"}); + }); + + test('Test Setting Name: by the owner', () async{ + final service = UserContractService(); + await service.setName("user2", contractAddress: contractAddress, privateKey: privateKey); + await service.setEmail("user2@iblock.com", contractAddress: contractAddress, privateKey: privateKey); + await service.setCountry("India", contractAddress: contractAddress, privateKey: privateKey); + await service.setGender("Female", contractAddress: contractAddress, privateKey: privateKey); + await service.setMobile("+123456789", contractAddress: contractAddress, privateKey: privateKey); + + + var result = await service.getAll(contractAddress, privateKey); + + expect(result, {"Name": "user2", "Email": "user2@iblock.com", "Date of Birth": "2000/01/01", "Country": "India", "Phone": "+123456789", "Gender": "Female"}); + + }); + + test('Testing verify function', () async{ + final service = UserContractService(); + String transactionId = await service.verify("0x1Ff8Ebe435cdD925EE58388c78a6b21f650247B6", "12345", privateKey: privateKey, contractAddress: contractAddress); + expect(transactionId, startsWith("0x")); + }); + }); } \ No newline at end of file