mirror of
https://github.com/selfxyz/self.git
synced 2026-01-09 06:38:09 -05:00
First lint and fmt pass
This commit is a first pass of lint and fmt, with a few manual fixes. Only manual issues left to do.
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
{
|
||||
"plugins": [
|
||||
["module:react-native-dotenv"]
|
||||
]
|
||||
}
|
||||
"plugins": [["module:react-native-dotenv"]]
|
||||
}
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
# yarnPath: .yarn/releases/yarn-4.5.0.cjs
|
||||
|
||||
26
app/App.tsx
26
app/App.tsx
@@ -1,23 +1,27 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import "react-native-get-random-values"
|
||||
import "@ethersproject/shims"
|
||||
import MainScreen from './src/screens/MainScreen';
|
||||
import { Buffer } from 'buffer';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useToastController } from '@tamagui/toast';
|
||||
import useNavigationStore from './src/stores/navigationStore';
|
||||
import { AMPLITUDE_KEY } from '@env';
|
||||
|
||||
import * as amplitude from '@amplitude/analytics-react-native';
|
||||
import { AMPLITUDE_KEY } from '@env';
|
||||
import '@ethersproject/shims';
|
||||
import { Buffer } from 'buffer';
|
||||
import 'react-native-get-random-values';
|
||||
|
||||
import { useToastController } from '@tamagui/toast';
|
||||
import { YStack } from 'tamagui';
|
||||
|
||||
import MainScreen from './src/screens/MainScreen';
|
||||
import useNavigationStore from './src/stores/navigationStore';
|
||||
import useUserStore from './src/stores/userStore';
|
||||
import { bgWhite } from './src/utils/colors';
|
||||
import { setupUniversalLinkListener } from './src/utils/qrCode'; // Adjust the import path as needed
|
||||
|
||||
global.Buffer = Buffer;
|
||||
|
||||
function App(): React.JSX.Element {
|
||||
const toast = useToastController();
|
||||
const setToast = useNavigationStore((state) => state.setToast);
|
||||
const initUserStore = useUserStore((state) => state.initUserStore);
|
||||
const setSelectedTab = useNavigationStore((state) => state.setSelectedTab);
|
||||
const setToast = useNavigationStore(state => state.setToast);
|
||||
const initUserStore = useUserStore(state => state.initUserStore);
|
||||
const setSelectedTab = useNavigationStore(state => state.setSelectedTab);
|
||||
|
||||
useEffect(() => {
|
||||
initUserStore();
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
| Requirement | Version | Installation Guide |
|
||||
|-------------|---------|--------------------|
|
||||
| nodejs | > v18 | [Install nodejs](https://nodejs.org/) |
|
||||
| circom | Latest | [Install circom](https://docs.circom.io/) |
|
||||
| snarkjs | Latest | [Install snarkjs](https://github.com/iden3/snarkjs) |
|
||||
| watchman | Latest | [Install watchman](https://facebook.github.io/watchman/) |
|
||||
|
||||
| Requirement | Version | Installation Guide |
|
||||
| ----------- | ------- | -------------------------------------------------------- |
|
||||
| nodejs | > v18 | [Install nodejs](https://nodejs.org/) |
|
||||
| circom | Latest | [Install circom](https://docs.circom.io/) |
|
||||
| snarkjs | Latest | [Install snarkjs](https://github.com/iden3/snarkjs) |
|
||||
| watchman | Latest | [Install watchman](https://facebook.github.io/watchman/) |
|
||||
|
||||
### Android
|
||||
| Requirement | Version | Installation Guide |
|
||||
|-------------|---------|--------------------|
|
||||
| Java | 17 | [Install Java](https://www.oracle.com/java/technologies/javase-jdk17-downloads.html)|
|
||||
| Android Studio | Latest | [Install Android Studio](https://developer.android.com/studio) |
|
||||
| Android SDK | Latest | [Install Android SDK](https://developer.android.com/studio#downloads) |
|
||||
| Android Ndk | 23.1.7779620 | [Install NDK](https://developer.android.com/studio) or [GPT4 guide](https://chatgpt.com/share/a6e2544b-d32a-4554-a452-402511d03ffc) |
|
||||
|
||||
| Requirement | Version | Installation Guide |
|
||||
| -------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Java | 17 | [Install Java](https://www.oracle.com/java/technologies/javase-jdk17-downloads.html) |
|
||||
| Android Studio | Latest | [Install Android Studio](https://developer.android.com/studio) |
|
||||
| Android SDK | Latest | [Install Android SDK](https://developer.android.com/studio#downloads) |
|
||||
| Android Ndk | 23.1.7779620 | [Install NDK](https://developer.android.com/studio) or [GPT4 guide](https://chatgpt.com/share/a6e2544b-d32a-4554-a452-402511d03ffc) |
|
||||
|
||||
### iOS
|
||||
| Requirement | Version | Installation Guide |
|
||||
|-------------|---------|--------------------|
|
||||
| Xcode | Latest | [Install Xcode](https://developer.apple.com/xcode/) |
|
||||
| cocoapods | Latest | [Install cocoapods](https://cocoapods.org/) |
|
||||
|
||||
| Requirement | Version | Installation Guide |
|
||||
| ----------- | ------- | --------------------------------------------------- |
|
||||
| Xcode | Latest | [Install Xcode](https://developer.apple.com/xcode/) |
|
||||
| cocoapods | Latest | [Install cocoapods](https://cocoapods.org/) |
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -38,11 +38,13 @@ First, connect your phone to your computer and allow access.
|
||||
### Android
|
||||
|
||||
Create the file `android/local.properties` with the following content:
|
||||
|
||||
```
|
||||
sdk.dir=/Users/<your-user-name>/Library/Android/sdk
|
||||
```
|
||||
|
||||
Launch the react-native server:
|
||||
|
||||
```
|
||||
yarn start
|
||||
```
|
||||
@@ -61,6 +63,7 @@ To see the Android logs you'll have to use the Android Studio Logcat.
|
||||
Open the ios project on Xcode and add your provisionning profile in Targets > OpenPassport > Signing and Capabilities
|
||||
|
||||
Then, install pods:
|
||||
|
||||
```
|
||||
cd ios
|
||||
pod install
|
||||
@@ -82,6 +85,7 @@ Adapt the input generation in `common/src/utils/generateInputs.ts`, and adapt an
|
||||
|
||||
Find your android ndk path. It should be something like `/Users/<your-user-name>/Library/Android/sdk/ndk/23.1.7779620`
|
||||
Build the android native module:
|
||||
|
||||
```
|
||||
export ANDROID_NDK="<your-android-ndk-path>"
|
||||
./scripts/build_android_module.sh
|
||||
@@ -90,6 +94,7 @@ export ANDROID_NDK="<your-android-ndk-path>"
|
||||
### iOS
|
||||
|
||||
Find your [development team id](https://chat.openai.com/share/9d52c37f-d9da-4a62-acb9-9e4ee8179f95) and run:
|
||||
|
||||
```
|
||||
export DEVELOPMENT_TEAM="<your-development-team-id>"
|
||||
./scripts/build_ios_module.sh
|
||||
@@ -105,16 +110,21 @@ export DEVELOPMENT_TEAM="<your-development-team-id>"
|
||||
cd android
|
||||
./gradlew assembleRelease
|
||||
```
|
||||
|
||||
The built apk it located at `android/app/build/outputs/apk/release/app-release.apk`
|
||||
|
||||
#### Publish on the Play Store
|
||||
|
||||
As explained [here](https://reactnative.dev/docs/signed-apk-android), first setup `android/app/my-upload-key.keystore` and the private vars in `~/.gradle/gradle.properties`, then run:
|
||||
|
||||
```
|
||||
npx react-native build-android --mode=release
|
||||
```
|
||||
|
||||
This builds `android/app/build/outputs/bundle/release/app-release.aab`.
|
||||
|
||||
Then to test the release on an android phone, delete the previous version of the app and run:
|
||||
|
||||
```
|
||||
yarn android --mode release
|
||||
```
|
||||
@@ -130,10 +140,13 @@ Don't forget to bump the build number.
|
||||
## FAQ
|
||||
|
||||
If you get something like this:
|
||||
|
||||
```
|
||||
'std::__1::system_error: open: /openpassport/app: Operation not permitted'
|
||||
```
|
||||
|
||||
You might want to try [this](https://stackoverflow.com/questions/49443341/watchman-crawl-failed-retrying-once-with-node-crawler):
|
||||
|
||||
```
|
||||
watchman watch-del-all
|
||||
watchman shutdown-server
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
module.exports = {
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
plugins: [
|
||||
["@babel/plugin-transform-private-methods", { "loose": true }],
|
||||
["module:react-native-dotenv", {
|
||||
"moduleName": "@env",
|
||||
"path": ".env",
|
||||
"blacklist": null,
|
||||
"whitelist": null,
|
||||
"safe": false,
|
||||
"allowUndefined": true
|
||||
}]
|
||||
['@babel/plugin-transform-private-methods', { loose: true }],
|
||||
[
|
||||
'module:react-native-dotenv',
|
||||
{
|
||||
moduleName: '@env',
|
||||
path: '.env',
|
||||
blacklist: null,
|
||||
whitelist: null,
|
||||
safe: false,
|
||||
allowUndefined: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
10
app/declarations.d.ts
vendored
10
app/declarations.d.ts
vendored
@@ -1,9 +1,9 @@
|
||||
declare module '@env';
|
||||
declare module '*.png' {
|
||||
const value: string;
|
||||
export = value;
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
||||
declare module '*.jpeg' {
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
||||
|
||||
@@ -2,19 +2,23 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { config } from '@tamagui/config/v2-native';
|
||||
import { ToastProvider } from '@tamagui/toast';
|
||||
import { AppRegistry, LogBox } from 'react-native';
|
||||
import { TamaguiProvider, createTamagui } from 'tamagui';
|
||||
|
||||
import App from './App';
|
||||
import { name as appName } from './app.json';
|
||||
import { TamaguiProvider, createTamagui } from 'tamagui';
|
||||
import { config } from '@tamagui/config/v2-native'
|
||||
import { ToastProvider } from '@tamagui/toast';
|
||||
const tamaguiConfig = createTamagui(config)
|
||||
|
||||
const tamaguiConfig = createTamagui(config);
|
||||
|
||||
LogBox.ignoreLogs([
|
||||
/bad setState/,
|
||||
'Warning, duplicate ID for input',
|
||||
/Warning, duplicate ID for input/
|
||||
])
|
||||
/Warning, duplicate ID for input/,
|
||||
]);
|
||||
|
||||
const Root = () => (
|
||||
<TamaguiProvider config={tamaguiConfig}>
|
||||
@@ -22,7 +26,6 @@ const Root = () => (
|
||||
<App />
|
||||
</ToastProvider>
|
||||
</TamaguiProvider>
|
||||
|
||||
);
|
||||
|
||||
AppRegistry.registerComponent(appName, () => Root);
|
||||
|
||||
17
app/index.js
17
app/index.js
@@ -2,19 +2,23 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { config } from '@tamagui/config/v2-native';
|
||||
import { ToastProvider } from '@tamagui/toast';
|
||||
import { AppRegistry, LogBox } from 'react-native';
|
||||
import { TamaguiProvider, createTamagui } from 'tamagui';
|
||||
|
||||
import App from './App';
|
||||
import { name as appName } from './app.json';
|
||||
import { TamaguiProvider, createTamagui } from 'tamagui';
|
||||
import { config } from '@tamagui/config/v2-native'
|
||||
import { ToastProvider } from '@tamagui/toast';
|
||||
const tamaguiConfig = createTamagui(config)
|
||||
|
||||
const tamaguiConfig = createTamagui(config);
|
||||
|
||||
LogBox.ignoreLogs([
|
||||
/bad setState/,
|
||||
'Warning, duplicate ID for input',
|
||||
/Warning, duplicate ID for input/
|
||||
])
|
||||
/Warning, duplicate ID for input/,
|
||||
]);
|
||||
|
||||
const Root = () => (
|
||||
<TamaguiProvider config={tamaguiConfig}>
|
||||
@@ -22,7 +26,6 @@ const Root = () => (
|
||||
<App />
|
||||
</ToastProvider>
|
||||
</TamaguiProvider>
|
||||
|
||||
);
|
||||
|
||||
AppRegistry.registerComponent(appName, () => Root);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
||||
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
||||
const path = require('path');
|
||||
|
||||
const commonPath = path.join(__dirname, '/../common');
|
||||
const extraNodeModules = {
|
||||
'common': path.resolve(__dirname + '/../common'),
|
||||
common: path.resolve(commonPath),
|
||||
};
|
||||
const watchFolders = [
|
||||
path.resolve(__dirname + '/../common')
|
||||
];
|
||||
const watchFolders = [path.resolve(commonPath)];
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
project: {
|
||||
ios: {},
|
||||
android: {},
|
||||
},
|
||||
assets: [
|
||||
'./assets/fonts',
|
||||
'./node_modules/@tamagui/font-inter/otf',
|
||||
'./node_modules/@tamagui/font-silkscreen/files',
|
||||
],
|
||||
};
|
||||
project: {
|
||||
ios: {},
|
||||
android: {},
|
||||
},
|
||||
assets: [
|
||||
'./assets/fonts',
|
||||
'./node_modules/@tamagui/font-inter/otf',
|
||||
'./node_modules/@tamagui/font-silkscreen/files',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,112 +1,160 @@
|
||||
import React, { useState } from 'react'
|
||||
import { AnimatePresence } from '@tamagui/animate-presence'
|
||||
import { ArrowLeft, Nfc } from '@tamagui/lucide-icons'
|
||||
import { Button, Image, XStack, YStack, styled, Text } from 'tamagui'
|
||||
import { bgGreen, textBlack } from '../utils/colors'
|
||||
import CustomButton from './CustomButton'
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { AnimatePresence } from '@tamagui/animate-presence';
|
||||
import { ArrowLeft, Nfc } from '@tamagui/lucide-icons';
|
||||
import { Button, Image, styled, Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import CustomButton from './CustomButton';
|
||||
|
||||
const GalleryItem = styled(YStack, {
|
||||
zIndex: 1,
|
||||
x: 0,
|
||||
opacity: 1,
|
||||
fullscreen: true,
|
||||
zIndex: 1,
|
||||
x: 0,
|
||||
opacity: 1,
|
||||
fullscreen: true,
|
||||
|
||||
variants: {
|
||||
going: {
|
||||
':number': (going) => ({
|
||||
enterStyle: {
|
||||
x: going > 0 ? 1000 : -1000,
|
||||
opacity: 0,
|
||||
},
|
||||
exitStyle: {
|
||||
zIndex: 0,
|
||||
x: going < 0 ? 1000 : -1000,
|
||||
opacity: 0,
|
||||
},
|
||||
}),
|
||||
variants: {
|
||||
going: {
|
||||
':number': going => ({
|
||||
enterStyle: {
|
||||
x: going > 0 ? 1000 : -1000,
|
||||
opacity: 0,
|
||||
},
|
||||
} as const,
|
||||
})
|
||||
exitStyle: {
|
||||
zIndex: 0,
|
||||
x: going < 0 ? 1000 : -1000,
|
||||
opacity: 0,
|
||||
},
|
||||
}),
|
||||
},
|
||||
} as const,
|
||||
});
|
||||
|
||||
const wrap = (min: number, max: number, v: number) => {
|
||||
const rangeSize = max - min
|
||||
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min
|
||||
}
|
||||
const rangeSize = max - min;
|
||||
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
|
||||
};
|
||||
|
||||
interface CarouselProps {
|
||||
images: string[];
|
||||
height?: number;
|
||||
handleNfcScan?: () => void;
|
||||
images: string[];
|
||||
height?: number;
|
||||
handleNfcScan?: () => void;
|
||||
}
|
||||
|
||||
export function Carousel({ images, height = 300, handleNfcScan }: CarouselProps) {
|
||||
const [[page, going], setPage] = useState([0, 0])
|
||||
export function Carousel({
|
||||
images,
|
||||
height = 300,
|
||||
handleNfcScan,
|
||||
}: CarouselProps) {
|
||||
const [[page, going], setPage] = useState([0, 0]);
|
||||
|
||||
const imageIndex = wrap(0, images.length, page)
|
||||
const paginate = (going: number) => {
|
||||
const newPage = page + going
|
||||
setPage([newPage, going])
|
||||
}
|
||||
const imageIndex = wrap(0, images.length, page);
|
||||
const paginate = (direction: number) => {
|
||||
const newPage = page + direction;
|
||||
setPage([newPage, direction]);
|
||||
};
|
||||
|
||||
const isLastImage = imageIndex === images.length - 1
|
||||
const slideTexts = [
|
||||
{ header: "Follow this guide carefully", subtitle: "", acknowledgment: "I'm ready to start" },
|
||||
{ header: "Open your passport to the last page", subtitle: "", acknowledgment: "I have opened my passport to the last page" },
|
||||
{ header: "Put your phone on the passport", subtitle: "Press your phone against the last page of the passport as in the image.", acknowledgment: "I have placed my phone on the passport" },
|
||||
{ header: "Start scanning", subtitle: "Press Start NFC Scan and follow the on-screen instructions.", acknowledgment: "Start scanning" },
|
||||
]
|
||||
const isLastImage = imageIndex === images.length - 1;
|
||||
const slideTexts = [
|
||||
{
|
||||
header: 'Follow this guide carefully',
|
||||
subtitle: '',
|
||||
acknowledgment: "I'm ready to start",
|
||||
},
|
||||
{
|
||||
header: 'Open your passport to the last page',
|
||||
subtitle: '',
|
||||
acknowledgment: 'I have opened my passport to the last page',
|
||||
},
|
||||
{
|
||||
header: 'Put your phone on the passport',
|
||||
subtitle:
|
||||
'Press your phone against the last page of the passport as in the image.',
|
||||
acknowledgment: 'I have placed my phone on the passport',
|
||||
},
|
||||
{
|
||||
header: 'Start scanning',
|
||||
subtitle: 'Press Start NFC Scan and follow the on-screen instructions.',
|
||||
acknowledgment: 'Start scanning',
|
||||
},
|
||||
];
|
||||
|
||||
const currentSlide = slideTexts[imageIndex] || { header: "No header", subtitle: "No subtitle for this slide", acknowledgment: "Continue" }
|
||||
const currentSlide = slideTexts[imageIndex] || {
|
||||
header: 'No header',
|
||||
subtitle: 'No subtitle for this slide',
|
||||
acknowledgment: 'Continue',
|
||||
};
|
||||
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<YStack f={1} jc='space-evenly'>
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<YStack f={1} jc="space-evenly">
|
||||
<Text textAlign="center" fontSize="$9" color={textBlack}>
|
||||
Verify your passport using{' '}
|
||||
<Text
|
||||
color={textBlack}
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
NFC
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
<Text textAlign='center' fontSize="$9" color={textBlack} >Verify your passport using <Text color={textBlack} style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>NFC</Text></Text>
|
||||
<XStack
|
||||
overflow="hidden"
|
||||
backgroundColor="#000"
|
||||
position="relative"
|
||||
height={height}
|
||||
alignItems="center"
|
||||
borderRadius="$10"
|
||||
>
|
||||
<AnimatePresence initial={false} custom={{ going }}>
|
||||
<GalleryItem key={page} animation="medium" going={going}>
|
||||
<Image
|
||||
source={{ uri: images[imageIndex] }}
|
||||
height={height}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</GalleryItem>
|
||||
</AnimatePresence>
|
||||
|
||||
<XStack
|
||||
overflow="hidden"
|
||||
backgroundColor="#000"
|
||||
position="relative"
|
||||
height={height}
|
||||
alignItems="center"
|
||||
borderRadius="$10"
|
||||
>
|
||||
<AnimatePresence initial={false} custom={{ going }}>
|
||||
<GalleryItem key={page} animation="medium" going={going}>
|
||||
<Image source={{ uri: images[imageIndex] }} height={height} resizeMode="contain" />
|
||||
</GalleryItem>
|
||||
</AnimatePresence>
|
||||
|
||||
|
||||
{imageIndex > 0 && (
|
||||
<Button
|
||||
icon={ArrowLeft}
|
||||
size="$5"
|
||||
position="absolute"
|
||||
left="$4"
|
||||
circular
|
||||
elevate
|
||||
onPress={() => paginate(-1)}
|
||||
zi={100}
|
||||
/>
|
||||
)}
|
||||
</XStack>
|
||||
|
||||
<YStack>
|
||||
<Text fontSize="$8" color={textBlack} textAlign='center'>{currentSlide.header}</Text>
|
||||
<Text color={textBlack} fontSize="$5" textAlign='center' style={{ opacity: 0.7 }} fontStyle='italic'>{currentSlide.subtitle}</Text>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
|
||||
|
||||
<CustomButton
|
||||
onPress={isLastImage ? () => handleNfcScan?.() : () => paginate(+1)}
|
||||
text={currentSlide.acknowledgment}
|
||||
Icon={isLastImage ? <Nfc /> : undefined}
|
||||
blueVariant={!isLastImage}
|
||||
{imageIndex > 0 && (
|
||||
<Button
|
||||
icon={ArrowLeft}
|
||||
size="$5"
|
||||
position="absolute"
|
||||
left="$4"
|
||||
circular
|
||||
elevate
|
||||
onPress={() => paginate(-1)}
|
||||
zi={100}
|
||||
/>
|
||||
)}
|
||||
</XStack>
|
||||
|
||||
<YStack>
|
||||
<Text fontSize="$8" color={textBlack} textAlign="center">
|
||||
{currentSlide.header}
|
||||
</Text>
|
||||
<Text
|
||||
color={textBlack}
|
||||
fontSize="$5"
|
||||
textAlign="center"
|
||||
style={{ opacity: 0.7 }}
|
||||
fontStyle="italic"
|
||||
>
|
||||
{currentSlide.subtitle}
|
||||
</Text>
|
||||
</YStack>
|
||||
)
|
||||
}
|
||||
</YStack>
|
||||
|
||||
<CustomButton
|
||||
onPress={isLastImage ? () => handleNfcScan?.() : () => paginate(+1)}
|
||||
text={currentSlide.acknowledgment}
|
||||
Icon={isLastImage ? <Nfc /> : undefined}
|
||||
blueVariant={!isLastImage}
|
||||
/>
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,47 @@
|
||||
import React from 'react';
|
||||
import { Button, Text } from 'tamagui';
|
||||
|
||||
import { bgBlue, bgGreen, textBlack } from '../utils/colors';
|
||||
|
||||
interface CustomButtonProps {
|
||||
text: string;
|
||||
onPress: () => void;
|
||||
Icon?: React.ReactNode;
|
||||
bgColor?: string;
|
||||
h?: string;
|
||||
isDisabled?: boolean;
|
||||
disabledOnPress?: () => void;
|
||||
blueVariant?: boolean;
|
||||
text: string;
|
||||
onPress: () => void;
|
||||
Icon?: React.ReactNode;
|
||||
bgColor?: string;
|
||||
h?: string;
|
||||
isDisabled?: boolean;
|
||||
disabledOnPress?: () => void;
|
||||
blueVariant?: boolean;
|
||||
}
|
||||
|
||||
const CustomButton: React.FC<CustomButtonProps> = ({ text, onPress, Icon, bgColor, h, isDisabled, disabledOnPress, blueVariant }) => {
|
||||
return (
|
||||
<Button bg={bgColor ? bgColor : blueVariant ? bgBlue : bgGreen} h={blueVariant ? "$8" : "$5"} borderRadius="$10" onPress={isDisabled ? disabledOnPress : onPress}>
|
||||
{Icon && <Button.Icon>{Icon}</Button.Icon>}
|
||||
<Text textAlign='center' fontSize={blueVariant ? "$6" : "$5"} fontWeight="bold" color={textBlack}>
|
||||
{text}
|
||||
</Text>
|
||||
</Button>
|
||||
);
|
||||
const CustomButton: React.FC<CustomButtonProps> = ({
|
||||
text,
|
||||
onPress,
|
||||
Icon,
|
||||
bgColor,
|
||||
h,
|
||||
isDisabled,
|
||||
disabledOnPress,
|
||||
blueVariant,
|
||||
}) => {
|
||||
return (
|
||||
<Button
|
||||
bg={bgColor ? bgColor : blueVariant ? bgBlue : bgGreen}
|
||||
h={blueVariant ? '$8' : '$5'}
|
||||
borderRadius="$10"
|
||||
onPress={isDisabled ? disabledOnPress : onPress}
|
||||
>
|
||||
{Icon && <Button.Icon>{Icon}</Button.Icon>}
|
||||
<Text
|
||||
textAlign="center"
|
||||
fontSize={blueVariant ? '$6' : '$5'}
|
||||
fontWeight="bold"
|
||||
color={textBlack}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default CustomButton;
|
||||
export default CustomButton;
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
import { XStack } from "tamagui";
|
||||
import { textBlack } from "../utils/colors";
|
||||
import { XStack } from 'tamagui';
|
||||
|
||||
import { textBlack } from '../utils/colors';
|
||||
|
||||
interface StepOneStepTwoProps {
|
||||
variable: string;
|
||||
step1: string;
|
||||
step2: string;
|
||||
variable: string;
|
||||
step1: string;
|
||||
step2: string;
|
||||
}
|
||||
const StepOneStepTwo = ({ variable, step1, step2 }: StepOneStepTwoProps) => {
|
||||
const isVisible = variable === step1 || variable === step2;
|
||||
const isVisible = variable === step1 || variable === step2;
|
||||
|
||||
return (
|
||||
<XStack px="$6" gap="$3" style={{ opacity: isVisible ? 1 : 0 }}>
|
||||
<XStack h="$0.25" f={1} bg={textBlack} borderRadius={100} style={{ opacity: variable === step1 ? 1 : 0.2 }} />
|
||||
<XStack h="$0.25" f={1} bg={textBlack} borderRadius={100} style={{ opacity: variable === step2 ? 1 : 0.2 }} />
|
||||
</XStack>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<XStack px="$6" gap="$3" style={{ opacity: isVisible ? 1 : 0 }}>
|
||||
<XStack
|
||||
h="$0.25"
|
||||
f={1}
|
||||
bg={textBlack}
|
||||
borderRadius={100}
|
||||
style={{ opacity: variable === step1 ? 1 : 0.2 }}
|
||||
/>
|
||||
<XStack
|
||||
h="$0.25"
|
||||
f={1}
|
||||
bg={textBlack}
|
||||
borderRadius={100}
|
||||
style={{ opacity: variable === step2 ? 1 : 0.2 }}
|
||||
/>
|
||||
</XStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepOneStepTwo;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { Toast, useToastState } from '@tamagui/toast';
|
||||
import { YStack } from '@tamagui/stacks';
|
||||
import { Toast, useToastState } from '@tamagui/toast';
|
||||
|
||||
import {
|
||||
blueColorLight,
|
||||
greenColorLight,
|
||||
redColorLight,
|
||||
blueColorLight,
|
||||
textColor1,
|
||||
} from '../utils/colors';
|
||||
|
||||
export const ToastMessage = () => {
|
||||
const toast = useToastState();
|
||||
|
||||
if (!toast || toast.isHandledNatively) return null;
|
||||
if (!toast || toast.isHandledNatively) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Toast
|
||||
@@ -18,8 +21,8 @@ export const ToastMessage = () => {
|
||||
toast.customData?.type === 'success'
|
||||
? greenColorLight
|
||||
: toast.customData?.type === 'error'
|
||||
? redColorLight
|
||||
: blueColorLight
|
||||
? redColorLight
|
||||
: blueColorLight
|
||||
}
|
||||
animation="100ms"
|
||||
enterStyle={{ y: -20, opacity: 0 }}
|
||||
@@ -30,7 +33,9 @@ export const ToastMessage = () => {
|
||||
duration={3000}
|
||||
>
|
||||
<YStack ai="center" jc="center">
|
||||
<Toast.Title fow="bold" color={'white'}>{toast?.title}</Toast.Title>
|
||||
<Toast.Title fow="bold" color={'white'}>
|
||||
{toast?.title}
|
||||
</Toast.Title>
|
||||
<Toast.Description color={textColor1}>
|
||||
{toast.message}
|
||||
</Toast.Description>
|
||||
|
||||
@@ -1,34 +1,46 @@
|
||||
import React from 'react';
|
||||
import { Text, YStack, Image } from 'tamagui';
|
||||
import { XStack } from 'tamagui';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
|
||||
import { QrCode } from '@tamagui/lucide-icons';
|
||||
import { textBlack } from '../utils/colors';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { scanQRCode } from '../utils/qrCode';
|
||||
import { Image, Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import OPENPASSPORT_LOGO from '../images/openpassport.png';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { textBlack } from '../utils/colors';
|
||||
import { scanQRCode } from '../utils/qrCode';
|
||||
|
||||
interface AppScreenProps {
|
||||
setSheetAppListOpen: (value: boolean) => void;
|
||||
setSheetRegisterIsOpen: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const AppScreen: React.FC<AppScreenProps> = ({ setSheetAppListOpen, setSheetRegisterIsOpen }) => {
|
||||
const {
|
||||
registered,
|
||||
} = useUserStore();
|
||||
|
||||
|
||||
const AppScreen: React.FC<AppScreenProps> = ({ setSheetRegisterIsOpen }) => {
|
||||
const { registered } = useUserStore();
|
||||
|
||||
return (
|
||||
<YStack f={1} >
|
||||
<YStack f={1}>
|
||||
<XStack f={1} minHeight="$1" />
|
||||
|
||||
<Image alignSelf='center' src={OPENPASSPORT_LOGO} width={400} height={150} />
|
||||
<Text mt="$2.5" textAlign='center' fontSize="$9" fontWeight='bold' color={textBlack}>OpenPassport</Text>
|
||||
<Image
|
||||
alignSelf="center"
|
||||
src={OPENPASSPORT_LOGO}
|
||||
width={400}
|
||||
height={150}
|
||||
/>
|
||||
<Text
|
||||
mt="$2.5"
|
||||
textAlign="center"
|
||||
fontSize="$9"
|
||||
fontWeight="bold"
|
||||
color={textBlack}
|
||||
>
|
||||
OpenPassport
|
||||
</Text>
|
||||
|
||||
<XStack f={1} minHeight="$1" />
|
||||
<Text textAlign='center' mb="$2" fontSize="$3" color={textBlack}>To use OpenPassport, scan the QR code displayed by an app.</Text>
|
||||
<Text textAlign="center" mb="$2" fontSize="$3" color={textBlack}>
|
||||
To use OpenPassport, scan the QR code displayed by an app.
|
||||
</Text>
|
||||
<YStack gap="$2.5">
|
||||
<CustomButton
|
||||
text="Scan QR Code"
|
||||
@@ -44,6 +56,6 @@ const AppScreen: React.FC<AppScreenProps> = ({ setSheetAppListOpen, setSheetRegi
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default AppScreen;
|
||||
export default AppScreen;
|
||||
|
||||
@@ -1,31 +1,64 @@
|
||||
import React from 'react';
|
||||
import { YStack, Text, Image } from 'tamagui';
|
||||
|
||||
import { Camera, SquarePen } from '@tamagui/lucide-icons';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import { startCameraScan } from '../utils/cameraScanner';
|
||||
import { Image, Text, YStack } from 'tamagui';
|
||||
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import PASSPORT_DRAWING from '../images/passport_drawing.png'
|
||||
import PASSPORT_DRAWING from '../images/passport_drawing.png';
|
||||
import { startCameraScan } from '../utils/cameraScanner';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
|
||||
interface CameraScreenProps {
|
||||
setSheetIsOpen: (value: boolean) => void
|
||||
setSheetIsOpen: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const CameraScreen: React.FC<CameraScreenProps> = ({ setSheetIsOpen }) => {
|
||||
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<YStack f={1} my="$6" jc="space-evenly" ai="center">
|
||||
<Text textAlign='center' fontSize="$9" color={textBlack}><Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Scan</Text> or type your passport ID</Text>
|
||||
<Text textAlign='center' mt="$4" fontSize="$6" color={textBlack}>Open your passport on the <Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>main page</Text> to scan it.</Text>
|
||||
<Text textAlign="center" fontSize="$9" color={textBlack}>
|
||||
<Text
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
Scan
|
||||
</Text>{' '}
|
||||
or type your passport ID
|
||||
</Text>
|
||||
<Text textAlign="center" mt="$4" fontSize="$6" color={textBlack}>
|
||||
Open your passport on the{' '}
|
||||
<Text
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
main page
|
||||
</Text>{' '}
|
||||
to scan it.
|
||||
</Text>
|
||||
<Image src={PASSPORT_DRAWING} style={{ width: 200, height: 250 }} />
|
||||
</YStack>
|
||||
<Text textAlign='center' mb="$2" fontSize="$4" color={textBlack}>The application is not taking a picture, only reading some fields.</Text>
|
||||
<YStack gap="$2.5" >
|
||||
<CustomButton text="Open Camera" onPress={startCameraScan} Icon={<Camera color={textBlack} size={24} />} />
|
||||
<CustomButton bgColor='#ffff' text="Manual Input" onPress={() => setSheetIsOpen(true)} Icon={<SquarePen color={textBlack} size={24} />} />
|
||||
<Text textAlign="center" mb="$2" fontSize="$4" color={textBlack}>
|
||||
The application is not taking a picture, only reading some fields.
|
||||
</Text>
|
||||
<YStack gap="$2.5">
|
||||
<CustomButton
|
||||
text="Open Camera"
|
||||
onPress={startCameraScan}
|
||||
Icon={<Camera color={textBlack} size={24} />}
|
||||
/>
|
||||
<CustomButton
|
||||
bgColor="#ffff"
|
||||
text="Manual Input"
|
||||
onPress={() => setSheetIsOpen(true)}
|
||||
Icon={<SquarePen color={textBlack} size={24} />}
|
||||
/>
|
||||
</YStack>
|
||||
|
||||
</YStack >
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CameraScreen;
|
||||
export default CameraScreen;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,25 @@
|
||||
import React, { useState, useCallback, useMemo, useRef } from 'react';
|
||||
import { YStack, XStack, Text, Select, Adapt, Sheet, Fieldset, Button, Spinner, Switch } from 'tamagui';
|
||||
import { Check, ChevronDown, ChevronUp, Cpu, Minus, Plus, X } from '@tamagui/lucide-icons';
|
||||
import { bgColor, bgGreen, bgGreen2, blueColor, borderColor, greenColorLight, redColorDark, textBlack } from '../utils/colors';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import { genMockPassportData } from '../../../common/src/utils/genMockPassportData';
|
||||
import { countryCodes } from '../../../common/src/constants/constants';
|
||||
import getCountryISO2 from "country-iso-3-to-2";
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { ChevronDown, Cpu, Minus, Plus } from '@tamagui/lucide-icons';
|
||||
import {
|
||||
Button,
|
||||
Fieldset,
|
||||
Spinner,
|
||||
Switch,
|
||||
Text,
|
||||
XStack,
|
||||
YStack,
|
||||
} from 'tamagui';
|
||||
|
||||
import { flag } from 'country-emoji';
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import getCountryISO2 from 'country-iso-3-to-2';
|
||||
|
||||
import { countryCodes } from '../../../common/src/constants/constants';
|
||||
import { genMockPassportData } from '../../../common/src/utils/genMockPassportData';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { borderColor, textBlack } from '../utils/colors';
|
||||
|
||||
interface MockDataScreenProps {
|
||||
onCountryPress: () => void;
|
||||
@@ -31,62 +41,88 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({
|
||||
const castDate = (yearsOffset: number) => {
|
||||
const date = new Date();
|
||||
date.setFullYear(date.getFullYear() + yearsOffset);
|
||||
return (date.toISOString().slice(2, 4) + date.toISOString().slice(5, 7) + date.toISOString().slice(8, 10)).toString();
|
||||
return (
|
||||
date.toISOString().slice(2, 4) +
|
||||
date.toISOString().slice(5, 7) +
|
||||
date.toISOString().slice(8, 10)
|
||||
).toString();
|
||||
};
|
||||
|
||||
const { toast } = useNavigationStore();
|
||||
const signatureAlgorithmToStrictSignatureAlgorithm = {
|
||||
"rsa sha256": "rsa_sha256_65537_2048",
|
||||
"rsa sha1": "rsa_sha1_65537_2048",
|
||||
"rsapss sha256": "rsapss_sha256_65537_2048"
|
||||
'rsa sha256': 'rsa_sha256_65537_2048',
|
||||
'rsa sha1': 'rsa_sha1_65537_2048',
|
||||
'rsapss sha256': 'rsapss_sha256_65537_2048',
|
||||
} as const;
|
||||
|
||||
const handleGenerate = useCallback(async () => {
|
||||
setIsGenerating(true);
|
||||
const randomPassportNumber = Math.random().toString(36).substring(2, 11).replace(/[^a-z0-9]/gi, '').toUpperCase();
|
||||
await new Promise(resolve => setTimeout(() => {
|
||||
let mockPassportData;
|
||||
if (isInOfacList) {
|
||||
mockPassportData = genMockPassportData(
|
||||
signatureAlgorithmToStrictSignatureAlgorithm[selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm],
|
||||
selectedCountry as keyof typeof countryCodes,
|
||||
castDate(-age),
|
||||
castDate(expiryYears),
|
||||
randomPassportNumber,
|
||||
'HENAO MONTOYA', // this name is the OFAC list
|
||||
'ARCANGEL DE JESUS'
|
||||
);
|
||||
} else {
|
||||
mockPassportData = genMockPassportData(
|
||||
signatureAlgorithmToStrictSignatureAlgorithm[selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm],
|
||||
selectedCountry as keyof typeof countryCodes,
|
||||
castDate(-age),
|
||||
castDate(expiryYears),
|
||||
randomPassportNumber,
|
||||
);
|
||||
}
|
||||
useUserStore.getState().registerPassportData(mockPassportData);
|
||||
useUserStore.getState().setRegistered(true);
|
||||
resolve(null);
|
||||
}, 0));
|
||||
const randomPassportNumber = Math.random()
|
||||
.toString(36)
|
||||
.substring(2, 11)
|
||||
.replace(/[^a-z0-9]/gi, '')
|
||||
.toUpperCase();
|
||||
await new Promise(resolve =>
|
||||
setTimeout(() => {
|
||||
let mockPassportData;
|
||||
if (isInOfacList) {
|
||||
mockPassportData = genMockPassportData(
|
||||
signatureAlgorithmToStrictSignatureAlgorithm[
|
||||
selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
|
||||
],
|
||||
selectedCountry as keyof typeof countryCodes,
|
||||
castDate(-age),
|
||||
castDate(expiryYears),
|
||||
randomPassportNumber,
|
||||
'HENAO MONTOYA', // this name is the OFAC list
|
||||
'ARCANGEL DE JESUS',
|
||||
);
|
||||
} else {
|
||||
mockPassportData = genMockPassportData(
|
||||
signatureAlgorithmToStrictSignatureAlgorithm[
|
||||
selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
|
||||
],
|
||||
selectedCountry as keyof typeof countryCodes,
|
||||
castDate(-age),
|
||||
castDate(expiryYears),
|
||||
randomPassportNumber,
|
||||
);
|
||||
}
|
||||
useUserStore.getState().registerPassportData(mockPassportData);
|
||||
useUserStore.getState().setRegistered(true);
|
||||
resolve(null);
|
||||
}, 0),
|
||||
);
|
||||
|
||||
toast.show("🤖", {
|
||||
message: "Passport generated",
|
||||
toast.show('🤖', {
|
||||
message: 'Passport generated',
|
||||
customData: {
|
||||
type: "success",
|
||||
type: 'success',
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
useNavigationStore.getState().setSelectedTab("next");
|
||||
useNavigationStore.getState().setSelectedTab('next');
|
||||
}, [selectedAlgorithm, selectedCountry, age, expiryYears, isInOfacList]);
|
||||
|
||||
return (
|
||||
<YStack f={1} gap="$4" >
|
||||
<Text my="$9" textAlign="center" fontSize="$9" color={textBlack}>Generate passport data</Text>
|
||||
<YStack f={1} gap="$4">
|
||||
<Text my="$9" textAlign="center" fontSize="$9" color={textBlack}>
|
||||
Generate passport data
|
||||
</Text>
|
||||
<XStack ai="center">
|
||||
<Text f={1} fontSize="$5">Encryption</Text>
|
||||
<Button onPress={onAlgorithmPress} p="$2" px="$3" bg="white" borderColor={borderColor} borderWidth={1} borderRadius="$4" >
|
||||
<Text f={1} fontSize="$5">
|
||||
Encryption
|
||||
</Text>
|
||||
<Button
|
||||
onPress={onAlgorithmPress}
|
||||
p="$2"
|
||||
px="$3"
|
||||
bg="white"
|
||||
borderColor={borderColor}
|
||||
borderWidth={1}
|
||||
borderRadius="$4"
|
||||
>
|
||||
<XStack ai="center" gap="$2">
|
||||
<Text fontSize="$4">{selectedAlgorithm}</Text>
|
||||
<ChevronDown size={20} />
|
||||
@@ -94,73 +130,154 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({
|
||||
</Button>
|
||||
</XStack>
|
||||
<XStack ai="center">
|
||||
<Text f={1} fontSize="$5">Nationality</Text>
|
||||
<Button onPress={onCountryPress} p="$2" px="$3" bg="white" borderColor={borderColor} borderWidth={1} borderRadius="$4">
|
||||
<Text f={1} fontSize="$5">
|
||||
Nationality
|
||||
</Text>
|
||||
<Button
|
||||
onPress={onCountryPress}
|
||||
p="$2"
|
||||
px="$3"
|
||||
bg="white"
|
||||
borderColor={borderColor}
|
||||
borderWidth={1}
|
||||
borderRadius="$4"
|
||||
>
|
||||
<XStack ai="center" gap="$2">
|
||||
<Text fontSize="$4">{countryCodes[selectedCountry as keyof typeof countryCodes]} {flag(getCountryISO2(selectedCountry))}</Text>
|
||||
<Text fontSize="$4">
|
||||
{countryCodes[selectedCountry as keyof typeof countryCodes]}{' '}
|
||||
{flag(getCountryISO2(selectedCountry))}
|
||||
</Text>
|
||||
<ChevronDown size={20} />
|
||||
</XStack>
|
||||
</Button>
|
||||
</XStack>
|
||||
|
||||
<Fieldset mt="$2" gap="$2" horizontal>
|
||||
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
|
||||
<Text
|
||||
color={textBlack}
|
||||
width={160}
|
||||
justifyContent="flex-end"
|
||||
fontSize="$5"
|
||||
>
|
||||
Age (🎂)
|
||||
</Text>
|
||||
<XStack f={1} />
|
||||
|
||||
<Button h="$3.5" w="$3.5" bg="white" jc="center" borderColor={borderColor} borderWidth={1} borderRadius="$10" onPress={() => setAge(age - 1)} disabled={age <= 0}>
|
||||
<Button
|
||||
h="$3.5"
|
||||
w="$3.5"
|
||||
bg="white"
|
||||
jc="center"
|
||||
borderColor={borderColor}
|
||||
borderWidth={1}
|
||||
borderRadius="$10"
|
||||
onPress={() => setAge(age - 1)}
|
||||
disabled={age <= 0}
|
||||
>
|
||||
<Minus />
|
||||
</Button>
|
||||
<Text textAlign='center' w="$6" color={textBlack} fontSize="$5">
|
||||
<Text textAlign="center" w="$6" color={textBlack} fontSize="$5">
|
||||
{age} yo
|
||||
</Text>
|
||||
<Button h="$3.5" w="$3.5" bg="white" jc="center" borderColor={borderColor} borderWidth={1} borderRadius="$10" onPress={() => setAge(age + 1)}>
|
||||
<Button
|
||||
h="$3.5"
|
||||
w="$3.5"
|
||||
bg="white"
|
||||
jc="center"
|
||||
borderColor={borderColor}
|
||||
borderWidth={1}
|
||||
borderRadius="$10"
|
||||
onPress={() => setAge(age + 1)}
|
||||
>
|
||||
<Plus />
|
||||
</Button>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset gap="$2" horizontal>
|
||||
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
|
||||
<Text
|
||||
color={textBlack}
|
||||
width={160}
|
||||
justifyContent="flex-end"
|
||||
fontSize="$5"
|
||||
>
|
||||
Passport expires in
|
||||
</Text>
|
||||
<XStack f={1} />
|
||||
|
||||
<Button h="$3.5" w="$3.5" bg="white" jc="center" borderColor={borderColor} borderWidth={1} borderRadius="$10" onPress={() => setExpiryYears(expiryYears - 1)} disabled={expiryYears <= 0}>
|
||||
<Button
|
||||
h="$3.5"
|
||||
w="$3.5"
|
||||
bg="white"
|
||||
jc="center"
|
||||
borderColor={borderColor}
|
||||
borderWidth={1}
|
||||
borderRadius="$10"
|
||||
onPress={() => setExpiryYears(expiryYears - 1)}
|
||||
disabled={expiryYears <= 0}
|
||||
>
|
||||
<Minus />
|
||||
</Button>
|
||||
<Text textAlign='center' w="$6" color={textBlack} fontSize="$5">
|
||||
<Text textAlign="center" w="$6" color={textBlack} fontSize="$5">
|
||||
{expiryYears} years
|
||||
</Text>
|
||||
<Button h="$3.5" w="$3.5" bg="white" jc="center" borderColor={borderColor} borderWidth={1} borderRadius="$10" onPress={() => setExpiryYears(expiryYears + 1)}>
|
||||
<Button
|
||||
h="$3.5"
|
||||
w="$3.5"
|
||||
bg="white"
|
||||
jc="center"
|
||||
borderColor={borderColor}
|
||||
borderWidth={1}
|
||||
borderRadius="$10"
|
||||
onPress={() => setExpiryYears(expiryYears + 1)}
|
||||
>
|
||||
<Plus />
|
||||
</Button>
|
||||
</Fieldset>
|
||||
|
||||
<YStack >
|
||||
<YStack>
|
||||
<Fieldset mt="$2" gap="$2" horizontal>
|
||||
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
|
||||
<Text
|
||||
color={textBlack}
|
||||
width={160}
|
||||
justifyContent="flex-end"
|
||||
fontSize="$5"
|
||||
>
|
||||
Is in OFAC list
|
||||
</Text>
|
||||
<XStack f={1} />
|
||||
<Switch size="$3.5" checked={isInOfacList} onCheckedChange={() => setIsInOfacList(!isInOfacList)} bg={isInOfacList ? "$green7Light" : "$gray4"}>
|
||||
<Switch
|
||||
size="$3.5"
|
||||
checked={isInOfacList}
|
||||
onCheckedChange={() => setIsInOfacList(!isInOfacList)}
|
||||
bg={isInOfacList ? '$green7Light' : '$gray4'}
|
||||
>
|
||||
<Switch.Thumb animation="quick" bc="white" />
|
||||
</Switch>
|
||||
|
||||
|
||||
</Fieldset>
|
||||
<Text mt="$2" color="$red10" justifyContent="flex-end" fontSize="$3" style={{ opacity: isInOfacList ? 1 : 0 }}>
|
||||
OFAC list is a list of people who are suspected of being involved in terrorism or other illegal activities.
|
||||
<Text
|
||||
mt="$2"
|
||||
color="$red10"
|
||||
justifyContent="flex-end"
|
||||
fontSize="$3"
|
||||
style={{ opacity: isInOfacList ? 1 : 0 }}
|
||||
>
|
||||
OFAC list is a list of people who are suspected of being involved in
|
||||
terrorism or other illegal activities.
|
||||
</Text>
|
||||
</YStack>
|
||||
|
||||
<YStack f={1} />
|
||||
|
||||
<YStack >
|
||||
<YStack>
|
||||
<Text mb="$2" textAlign="center" fontSize="$4" color={textBlack}>
|
||||
These passport data are only for testing purposes.
|
||||
</Text>
|
||||
<CustomButton onPress={handleGenerate} text="Generate passport data" Icon={isGenerating ? <Spinner /> : <Cpu color={textBlack} />} isDisabled={isGenerating} />
|
||||
<CustomButton
|
||||
onPress={handleGenerate}
|
||||
text="Generate passport data"
|
||||
Icon={isGenerating ? <Spinner /> : <Cpu color={textBlack} />}
|
||||
isDisabled={isGenerating}
|
||||
/>
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
|
||||
@@ -1,98 +1,108 @@
|
||||
import React from 'react';
|
||||
import { YStack, XStack, Text, Image, useWindowDimensions, Fieldset } from 'tamagui';
|
||||
import { ArrowRight, Info } from '@tamagui/lucide-icons';
|
||||
import { getFirstName, maskString } from '../utils/utils';
|
||||
|
||||
import { ArrowRight } from '@tamagui/lucide-icons';
|
||||
import { Fieldset, Image, Text, useWindowDimensions, YStack } from 'tamagui';
|
||||
|
||||
import { attributeToPosition } from '../../../common/src/constants/constants';
|
||||
import USER_PROFILE from '../images/user_profile.png'
|
||||
import { bgGreen, borderColor, componentBgColor, textBlack, textColor1, textColor2 } from '../utils/colors';
|
||||
import { formatAttribute } from '../utils/utils';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import USER_PROFILE from '../images/user_profile.png';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import { formatAttribute, getFirstName, maskString } from '../utils/utils';
|
||||
|
||||
const NextScreen: React.FC = () => {
|
||||
|
||||
const { height } = useWindowDimensions();
|
||||
const handleNext = () => {
|
||||
setRegistered(true);
|
||||
setSelectedTab("app");
|
||||
}
|
||||
const {
|
||||
hideData,
|
||||
setSelectedTab
|
||||
setSelectedTab('app');
|
||||
};
|
||||
const { hideData, setSelectedTab } = useNavigationStore();
|
||||
|
||||
} = useNavigationStore()
|
||||
|
||||
const {
|
||||
passportData,
|
||||
setRegistered
|
||||
} = useUserStore();
|
||||
const { passportData, setRegistered } = useUserStore();
|
||||
|
||||
const disclosureOptions: any = {
|
||||
gender: "optional",
|
||||
nationality: "optional",
|
||||
expiry_date: "optional",
|
||||
date_of_birth: "optional",
|
||||
gender: 'optional',
|
||||
nationality: 'optional',
|
||||
expiry_date: 'optional',
|
||||
date_of_birth: 'optional',
|
||||
};
|
||||
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<YStack alignSelf='center' my="$3">
|
||||
{hideData
|
||||
? <Image
|
||||
<YStack alignSelf="center" my="$3">
|
||||
{hideData ? (
|
||||
<Image
|
||||
w={height > 750 ? 150 : 100}
|
||||
h={height > 750 ? 190 : 80}
|
||||
borderRadius={height > 800 ? "$7" : "$6"}
|
||||
borderRadius={height > 800 ? '$7' : '$6'}
|
||||
source={{
|
||||
uri: USER_PROFILE,
|
||||
}}
|
||||
/>
|
||||
: <Image
|
||||
) : (
|
||||
<Image
|
||||
w={height > 750 ? 190 : 130}
|
||||
h={height > 750 ? 190 : 130}
|
||||
borderRadius={height > 750 ? "$7" : "$6"}
|
||||
|
||||
borderRadius={height > 750 ? '$7' : '$6'}
|
||||
source={{
|
||||
uri: passportData.mockUser ? USER_PROFILE : passportData.photoBase64 ?? USER_PROFILE,
|
||||
uri: passportData.mockUser
|
||||
? USER_PROFILE
|
||||
: passportData.photoBase64 ?? USER_PROFILE,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</YStack>
|
||||
<Text color={textBlack} fontSize="$9" mt="$8" >
|
||||
Hi{" "}
|
||||
<Text color={textBlack} fontSize="$9" style={{
|
||||
textDecorationLine: "underline", textDecorationColor: bgGreen
|
||||
}}>{
|
||||
hideData
|
||||
? maskString(getFirstName(passportData.mrz))
|
||||
: getFirstName(passportData.mrz)
|
||||
}</Text>
|
||||
|
||||
<Text color={textBlack} fontSize="$9" mt="$8">
|
||||
Hi{' '}
|
||||
<Text
|
||||
color={textBlack}
|
||||
fontSize="$9"
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
{hideData
|
||||
? maskString(getFirstName(passportData.mrz))
|
||||
: getFirstName(passportData.mrz)}
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
<YStack gap="$2.5" mt="$6">
|
||||
{Object.keys(disclosureOptions).map((key) => {
|
||||
{Object.keys(disclosureOptions).map(key => {
|
||||
const key_ = key;
|
||||
const indexes = attributeToPosition[key_ as keyof typeof attributeToPosition];
|
||||
const keyFormatted = key_.replace(/_/g, ' ').split(' ').map((word: string) => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
const mrzAttribute = passportData.mrz.slice(indexes[0], indexes[1] + 1);
|
||||
const indexes =
|
||||
attributeToPosition[key_ as keyof typeof attributeToPosition];
|
||||
const keyFormatted = key_
|
||||
.replace(/_/g, ' ')
|
||||
.split(' ')
|
||||
.map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
const mrzAttribute = passportData.mrz.slice(
|
||||
indexes[0],
|
||||
indexes[1] + 1,
|
||||
);
|
||||
const mrzAttributeFormatted = formatAttribute(key_, mrzAttribute);
|
||||
|
||||
return (
|
||||
<Fieldset horizontal key={key} gap="$3" alignItems='center'>
|
||||
<Text color={textBlack} w="$14" justifyContent="flex-end" fontSize="$6" style={{
|
||||
opacity: 0.7
|
||||
}}>
|
||||
{keyFormatted}:
|
||||
</Text>
|
||||
<Fieldset horizontal key={key} gap="$3" alignItems="center">
|
||||
<Text
|
||||
color={textBlack}
|
||||
w="$14"
|
||||
justifyContent="flex-end"
|
||||
fontSize="$6"
|
||||
|
||||
style={{
|
||||
opacity: 0.7,
|
||||
}}
|
||||
>
|
||||
{hideData ? maskString(mrzAttributeFormatted) : mrzAttributeFormatted}
|
||||
{keyFormatted}:
|
||||
</Text>
|
||||
<Text color={textBlack} fontSize="$6">
|
||||
{hideData
|
||||
? maskString(mrzAttributeFormatted)
|
||||
: mrzAttributeFormatted}
|
||||
</Text>
|
||||
|
||||
</Fieldset>
|
||||
);
|
||||
})}
|
||||
@@ -100,12 +110,15 @@ const NextScreen: React.FC = () => {
|
||||
|
||||
<YStack f={1} />
|
||||
|
||||
|
||||
<YStack f={1} />
|
||||
|
||||
<CustomButton onPress={handleNext} text="Next" Icon={<ArrowRight color={textBlack} />} />
|
||||
</YStack >
|
||||
<CustomButton
|
||||
onPress={handleNext}
|
||||
text="Next"
|
||||
Icon={<ArrowRight color={textBlack} />}
|
||||
/>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default NextScreen;
|
||||
export default NextScreen;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import React, { useState } from 'react';
|
||||
import { YStack, XStack, ScrollView } from 'tamagui';
|
||||
import { Carousel } from '../components/Carousel';
|
||||
import US_PASSPORT from '../images/us_passport.jpeg'
|
||||
import US_PASSPORT_LASTPAGE from '../images/passport_lastpage_graybg.jpeg'
|
||||
import US_PASSPORT_LASTPAGE_IOS from '../images/passport_lastpage_iphone.jpeg'
|
||||
import US_PASSPORT_LASTPAGE_ANDROID from '../images/passport_lastpage_android.jpeg'
|
||||
import PHONE_SCANBUTTON from "../images/phone_scanbutton.jpeg"
|
||||
|
||||
import Dialog from "react-native-dialog";
|
||||
import { Linking, Platform } from 'react-native';
|
||||
import Dialog from 'react-native-dialog';
|
||||
import NfcManager from 'react-native-nfc-manager';
|
||||
import { Platform, Linking } from 'react-native';
|
||||
|
||||
import { ScrollView, XStack, YStack } from 'tamagui';
|
||||
|
||||
import { Carousel } from '../components/Carousel';
|
||||
|
||||
import US_PASSPORT_LASTPAGE_ANDROID from '../images/passport_lastpage_android.jpeg';
|
||||
import US_PASSPORT_LASTPAGE from '../images/passport_lastpage_graybg.jpeg';
|
||||
import US_PASSPORT_LASTPAGE_IOS from '../images/passport_lastpage_iphone.jpeg';
|
||||
import PHONE_SCANBUTTON from '../images/phone_scanbutton.jpeg';
|
||||
import US_PASSPORT from '../images/us_passport.jpeg';
|
||||
|
||||
interface NfcScreenProps {
|
||||
handleNFCScan: () => void;
|
||||
@@ -19,7 +22,14 @@ const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
|
||||
const [dialogVisible, setDialogVisible] = useState(false);
|
||||
const [dialogMessage, setDialogMessage] = useState('');
|
||||
const [isNfcSupported, setIsNfcSupported] = useState(true);
|
||||
const carouselImages = [US_PASSPORT, US_PASSPORT_LASTPAGE, Platform.OS === 'ios' ? US_PASSPORT_LASTPAGE_IOS : US_PASSPORT_LASTPAGE_ANDROID, PHONE_SCANBUTTON,];
|
||||
const carouselImages = [
|
||||
US_PASSPORT,
|
||||
US_PASSPORT_LASTPAGE,
|
||||
Platform.OS === 'ios'
|
||||
? US_PASSPORT_LASTPAGE_IOS
|
||||
: US_PASSPORT_LASTPAGE_ANDROID,
|
||||
PHONE_SCANBUTTON,
|
||||
];
|
||||
|
||||
const openNfcSettings = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
@@ -35,14 +45,18 @@ const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
|
||||
if (isSupported) {
|
||||
const isEnabled = await NfcManager.isEnabled();
|
||||
if (!isEnabled) {
|
||||
setDialogMessage('NFC is not enabled. Would you like to enable it in settings?');
|
||||
setDialogMessage(
|
||||
'NFC is not enabled. Would you like to enable it in settings?',
|
||||
);
|
||||
setDialogVisible(true);
|
||||
setIsNfcSupported(true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
setDialogMessage("Sorry, your device doesn't seem to have an NFC reader.");
|
||||
setDialogMessage(
|
||||
"Sorry, your device doesn't seem to have an NFC reader.",
|
||||
);
|
||||
setDialogVisible(true);
|
||||
setIsNfcSupported(false);
|
||||
return false;
|
||||
@@ -58,7 +72,7 @@ const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
|
||||
|
||||
return (
|
||||
<ScrollView flex={1} contentContainerStyle={{ flexGrow: 1 }}>
|
||||
<YStack f={1} >
|
||||
<YStack f={1}>
|
||||
<Carousel
|
||||
images={carouselImages}
|
||||
height={300}
|
||||
@@ -66,9 +80,7 @@ const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
|
||||
/>
|
||||
<Dialog.Container visible={dialogVisible}>
|
||||
<Dialog.Title>NFC Status</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
{dialogMessage}
|
||||
</Dialog.Description>
|
||||
<Dialog.Description>{dialogMessage}</Dialog.Description>
|
||||
{isNfcSupported ? (
|
||||
<XStack>
|
||||
<XStack f={1} />
|
||||
@@ -83,4 +95,4 @@ const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default NfcScreen;
|
||||
export default NfcScreen;
|
||||
|
||||
@@ -1,56 +1,84 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { YStack, XStack, Text, Spinner, Progress } from 'tamagui';
|
||||
import { CheckCircle } from '@tamagui/lucide-icons';
|
||||
import { DEVELOPMENT_MODE, max_cert_bytes, } from '../../../common/src/constants/constants';
|
||||
import { bgGreen, greenColorLight, separatorColor, textBlack } from '../utils/colors';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import { DisclosureOptions, OpenPassportApp } from '../../../common/src/utils/appType';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import { generateProof } from '../utils/prover';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import io, { Socket } from 'socket.io-client';
|
||||
import { getCircuitNameOld, parseCertificateSimple } from '../../../common/src/utils/certificate_parsing/parseCertificateSimple';
|
||||
import { CircuitName } from '../utils/zkeyDownload';
|
||||
import { generateCircuitInputsInApp } from '../utils/generateInputsInApp';
|
||||
import { Progress, Spinner, Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import {
|
||||
DEVELOPMENT_MODE,
|
||||
max_cert_bytes,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import {
|
||||
DisclosureOptions,
|
||||
OpenPassportApp,
|
||||
} from '../../../common/src/utils/appType';
|
||||
import {
|
||||
getCircuitNameOld,
|
||||
parseCertificateSimple,
|
||||
} from '../../../common/src/utils/certificate_parsing/parseCertificateSimple';
|
||||
import {
|
||||
generateCircuitInputsDSC,
|
||||
getCSCAFromSKI,
|
||||
sendCSCARequest,
|
||||
} from '../../../common/src/utils/csca';
|
||||
import { buildAttestation } from '../../../common/src/utils/openPassportAttestation';
|
||||
import { generateCircuitInputsDSC, getCSCAFromSKI } from '../../../common/src/utils/csca';
|
||||
import { sendCSCARequest } from '../../../common/src/utils/csca';
|
||||
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import {
|
||||
bgGreen,
|
||||
greenColorLight,
|
||||
separatorColor,
|
||||
textBlack,
|
||||
} from '../utils/colors';
|
||||
import { generateCircuitInputsInApp } from '../utils/generateInputsInApp';
|
||||
import { generateProof } from '../utils/prover';
|
||||
import { CircuitName } from '../utils/zkeyDownload';
|
||||
|
||||
interface ProveScreenProps {
|
||||
setSheetRegisterIsOpen: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) => {
|
||||
const ProveScreen: React.FC<ProveScreenProps> = ({
|
||||
setSheetRegisterIsOpen,
|
||||
}) => {
|
||||
const [generatingProof, setGeneratingProof] = useState(false);
|
||||
const selectedApp = useNavigationStore(state => state.selectedApp) as OpenPassportApp;
|
||||
const disclosureOptions = selectedApp.mode === 'register' ? {} : (selectedApp.args as any).disclosureOptions || {};
|
||||
const {
|
||||
toast,
|
||||
setSelectedTab,
|
||||
isZkeyDownloading,
|
||||
zkeyDownloadedPercentage
|
||||
} = useNavigationStore()
|
||||
const selectedApp = useNavigationStore(
|
||||
state => state.selectedApp,
|
||||
) as OpenPassportApp;
|
||||
const disclosureOptions =
|
||||
selectedApp.mode === 'register'
|
||||
? {}
|
||||
: (selectedApp.args as any).disclosureOptions || {};
|
||||
const { toast, setSelectedTab, isZkeyDownloading, zkeyDownloadedPercentage } =
|
||||
useNavigationStore();
|
||||
|
||||
const {
|
||||
setProofVerificationResult,
|
||||
registered,
|
||||
passportData,
|
||||
} = useUserStore()
|
||||
const { setProofVerificationResult, registered, passportData } =
|
||||
useUserStore();
|
||||
|
||||
if (!passportData) {
|
||||
return <Text mt="$10" fontSize="$9" color={textBlack} textAlign='center' >No passport data</Text>;
|
||||
return (
|
||||
<Text mt="$10" fontSize="$9" color={textBlack} textAlign="center">
|
||||
No passport data
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const { signatureAlgorithm, authorityKeyIdentifier } = parseCertificateSimple(passportData.dsc);
|
||||
const { signatureAlgorithm, authorityKeyIdentifier } = parseCertificateSimple(
|
||||
passportData.dsc,
|
||||
);
|
||||
const parsedPassportData = parsePassportData(passportData);
|
||||
const { secret, dscSecret } = useUserStore.getState();
|
||||
const circuitName = getCircuitNameOld(selectedApp.mode, signatureAlgorithm, parsedPassportData.signedAttrHashFunction);
|
||||
const circuitName = getCircuitNameOld(
|
||||
selectedApp.mode,
|
||||
signatureAlgorithm,
|
||||
parsedPassportData.signedAttrHashFunction,
|
||||
);
|
||||
|
||||
const waitForSocketConnection = (socket: Socket): Promise<void> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
if (socket.connected) {
|
||||
resolve();
|
||||
} else {
|
||||
@@ -68,7 +96,7 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
newSocket = io(selectedApp.websocketUrl, {
|
||||
path: '/websocket',
|
||||
transports: ['websocket'],
|
||||
query: { sessionId: selectedApp.sessionId, clientType: 'mobile' }
|
||||
query: { sessionId: selectedApp.sessionId, clientType: 'mobile' },
|
||||
});
|
||||
|
||||
newSocket.on('connect', () => {
|
||||
@@ -79,50 +107,49 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
console.log('Disconnected from WebSocket server');
|
||||
});
|
||||
|
||||
newSocket.on('connect_error', (error) => {
|
||||
newSocket.on('connect_error', error => {
|
||||
console.error('Connection error:', error);
|
||||
toast.show("Error", {
|
||||
message: "Failed to connect to WebSocket server",
|
||||
toast.show('Error', {
|
||||
message: 'Failed to connect to WebSocket server',
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
newSocket.on('proof_verification_result', (result) => {
|
||||
newSocket.on('proof_verification_result', result => {
|
||||
setProofVerificationResult(JSON.parse(result));
|
||||
console.log("result", result);
|
||||
console.log('result', result);
|
||||
if (JSON.parse(result).valid) {
|
||||
toast.show("✅", {
|
||||
message: "Identity verified",
|
||||
toast.show('✅', {
|
||||
message: 'Identity verified',
|
||||
customData: {
|
||||
type: "success",
|
||||
type: 'success',
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
setSelectedTab("valid");
|
||||
setSelectedTab('valid');
|
||||
}, 700);
|
||||
} else {
|
||||
toast.show("❌", {
|
||||
message: "Verification failed",
|
||||
toast.show('❌', {
|
||||
message: 'Verification failed',
|
||||
customData: {
|
||||
type: "info",
|
||||
type: 'info',
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
setSelectedTab("wrong");
|
||||
setSelectedTab('wrong');
|
||||
}, 700);
|
||||
}
|
||||
});
|
||||
|
||||
setSocket(newSocket);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error setting up WebSocket:', error);
|
||||
toast.show("❌", {
|
||||
message: "Failed to set up connection",
|
||||
toast.show('❌', {
|
||||
message: 'Failed to set up connection',
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -146,9 +173,14 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
await waitForSocketConnection(socket);
|
||||
setIsConnecting(false);
|
||||
|
||||
socket.emit('proof_generation_start', { sessionId: selectedApp.sessionId });
|
||||
socket.emit('proof_generation_start', {
|
||||
sessionId: selectedApp.sessionId,
|
||||
});
|
||||
|
||||
const inputs = await generateCircuitInputsInApp(passportData, selectedApp);
|
||||
const inputs = await generateCircuitInputsInApp(
|
||||
passportData,
|
||||
selectedApp,
|
||||
);
|
||||
let attestation;
|
||||
let proof;
|
||||
let dscProof;
|
||||
@@ -156,18 +188,22 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
switch (selectedApp.mode) {
|
||||
case 'prove_onchain':
|
||||
case 'register':
|
||||
const cscaInputs = generateCircuitInputsDSC(dscSecret as string, passportData.dsc, max_cert_bytes, selectedApp.devMode);
|
||||
const cscaInputs = generateCircuitInputsDSC(
|
||||
dscSecret as string,
|
||||
passportData.dsc,
|
||||
max_cert_bytes,
|
||||
selectedApp.devMode,
|
||||
);
|
||||
[dscProof, proof] = await Promise.all([
|
||||
sendCSCARequest(
|
||||
cscaInputs
|
||||
),
|
||||
generateProof(
|
||||
circuitName,
|
||||
inputs,
|
||||
)
|
||||
sendCSCARequest(cscaInputs),
|
||||
generateProof(circuitName, inputs),
|
||||
]);
|
||||
const cscaPem = getCSCAFromSKI(authorityKeyIdentifier, DEVELOPMENT_MODE);
|
||||
const { signatureAlgorithm: signatureAlgorithmDsc } = parseCertificateSimple(cscaPem);
|
||||
const cscaPem = getCSCAFromSKI(
|
||||
authorityKeyIdentifier,
|
||||
DEVELOPMENT_MODE,
|
||||
);
|
||||
const { signatureAlgorithm: signatureAlgorithmDsc } =
|
||||
parseCertificateSimple(cscaPem);
|
||||
attestation = buildAttestation({
|
||||
mode: selectedApp.mode,
|
||||
proof: proof.proof,
|
||||
@@ -182,10 +218,7 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
});
|
||||
break;
|
||||
default:
|
||||
proof = await generateProof(
|
||||
circuitName,
|
||||
inputs,
|
||||
)
|
||||
proof = await generateProof(circuitName, inputs);
|
||||
attestation = buildAttestation({
|
||||
userIdType: selectedApp.userIdType,
|
||||
mode: selectedApp.mode,
|
||||
@@ -197,19 +230,23 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
});
|
||||
break;
|
||||
}
|
||||
console.log("\x1b[90mattestation\x1b[0m", attestation);
|
||||
socket.emit('proof_generated', { sessionId: selectedApp.sessionId, proof: attestation });
|
||||
|
||||
console.log('\x1b[90mattestation\x1b[0m', attestation);
|
||||
socket.emit('proof_generated', {
|
||||
sessionId: selectedApp.sessionId,
|
||||
proof: attestation,
|
||||
});
|
||||
} catch (error) {
|
||||
toast.show("Error", {
|
||||
toast.show('Error', {
|
||||
message: String(error),
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
});
|
||||
console.error('Error in handleProve:', error);
|
||||
if (socket) {
|
||||
socket.emit('proof_generation_failed', { sessionId: selectedApp.sessionId });
|
||||
socket.emit('proof_generation_failed', {
|
||||
sessionId: selectedApp.sessionId,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setGeneratingProof(false);
|
||||
@@ -217,25 +254,25 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
}
|
||||
};
|
||||
|
||||
const disclosureFieldsToText = (key: keyof DisclosureOptions, option: any) => {
|
||||
const disclosureFieldsToText = (
|
||||
key: keyof DisclosureOptions,
|
||||
option: any,
|
||||
) => {
|
||||
if (key === 'ofac') {
|
||||
return (option == true)
|
||||
? `My name is not present in the OFAC list.`
|
||||
: '';
|
||||
}
|
||||
else if (option.enabled) {
|
||||
return option == true ? 'My name is not present in the OFAC list.' : '';
|
||||
} else if (option.enabled) {
|
||||
switch (key) {
|
||||
case 'minimumAge':
|
||||
return `I am older than ${option.value} years old.`;
|
||||
case 'nationality':
|
||||
return option.value === 'Any'
|
||||
? `The issuer country of my passport.`
|
||||
? 'The issuer country of my passport.'
|
||||
: `I have a valid passport from ${option.value}.`;
|
||||
case 'excludedCountries':
|
||||
return option.value.length > 0
|
||||
? `I am not part of the following countries: ${option.value
|
||||
|
||||
.join(', ')}.`
|
||||
? `I am not part of the following countries: ${option.value.join(
|
||||
', ',
|
||||
)}.`
|
||||
: '';
|
||||
default:
|
||||
return '';
|
||||
@@ -245,7 +282,7 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
};
|
||||
|
||||
const hasEnabledDisclosureOptions = Object.values(disclosureOptions).some(
|
||||
(option: any) => option.enabled
|
||||
(option: any) => option.enabled,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -253,22 +290,45 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
{hasEnabledDisclosureOptions ? (
|
||||
<YStack mt="$4">
|
||||
<Text fontSize="$9">
|
||||
<Text fow="bold" style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>
|
||||
<Text
|
||||
fow="bold"
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
{selectedApp.appName}
|
||||
</Text>{' '}
|
||||
is requesting you to prove the following information.
|
||||
</Text>
|
||||
<Text mt="$3" fontSize="$8" color={textBlack} style={{ opacity: 0.9 }}>
|
||||
<Text
|
||||
mt="$3"
|
||||
fontSize="$8"
|
||||
color={textBlack}
|
||||
style={{ opacity: 0.9 }}
|
||||
>
|
||||
No{' '}
|
||||
<Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>
|
||||
<Text
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
other
|
||||
</Text>{' '}
|
||||
information than the one selected below will be shared with {selectedApp.appName}.
|
||||
information than the one selected below will be shared with{' '}
|
||||
{selectedApp.appName}.
|
||||
</Text>
|
||||
</YStack>
|
||||
) : (
|
||||
<Text fontSize="$9">
|
||||
<Text fow="bold" style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>
|
||||
<Text
|
||||
fow="bold"
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
{selectedApp.appName}
|
||||
</Text>{' '}
|
||||
is requesting you to prove you own a valid passport.
|
||||
@@ -276,51 +336,84 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
)}
|
||||
|
||||
<YStack mt="$6">
|
||||
{Object.entries(disclosureOptions).map(([key, option]: [string, any]) => {
|
||||
const text = disclosureFieldsToText(key as keyof DisclosureOptions, option);
|
||||
return text ? (
|
||||
<XStack key={key} gap="$3" mb="$3" ml="$3">
|
||||
<CheckCircle size={16} mt="$1.5" />
|
||||
<Text fontSize="$7" color={textBlack} w="85%">
|
||||
{text}
|
||||
</Text>
|
||||
</XStack>
|
||||
) : null;
|
||||
})}
|
||||
{Object.entries(disclosureOptions).map(
|
||||
([key, option]: [string, any]) => {
|
||||
const text = disclosureFieldsToText(
|
||||
key as keyof DisclosureOptions,
|
||||
option,
|
||||
);
|
||||
return text ? (
|
||||
<XStack key={key} gap="$3" mb="$3" ml="$3">
|
||||
<CheckCircle size={16} mt="$1.5" />
|
||||
<Text fontSize="$7" color={textBlack} w="85%">
|
||||
{text}
|
||||
</Text>
|
||||
</XStack>
|
||||
) : null;
|
||||
},
|
||||
)}
|
||||
</YStack>
|
||||
|
||||
<XStack f={1} />
|
||||
|
||||
{isZkeyDownloading[circuitName as CircuitName] && <YStack alignItems='center' gap="$2" mb="$3" mx="$8">
|
||||
<Text style={{ fontStyle: 'italic' }}>downloading files...</Text>
|
||||
<Progress
|
||||
key={circuitName}
|
||||
size="$1"
|
||||
value={zkeyDownloadedPercentage}
|
||||
>
|
||||
<Progress.Indicator
|
||||
animation="bouncy"
|
||||
backgroundColor={greenColorLight}
|
||||
/>
|
||||
</Progress>
|
||||
</YStack>}
|
||||
|
||||
{isZkeyDownloading[circuitName as CircuitName] && (
|
||||
<YStack alignItems="center" gap="$2" mb="$3" mx="$8">
|
||||
<Text style={{ fontStyle: 'italic' }}>downloading files...</Text>
|
||||
<Progress
|
||||
key={circuitName}
|
||||
size="$1"
|
||||
value={zkeyDownloadedPercentage}
|
||||
>
|
||||
<Progress.Indicator
|
||||
animation="bouncy"
|
||||
backgroundColor={greenColorLight}
|
||||
/>
|
||||
</Progress>
|
||||
</YStack>
|
||||
)}
|
||||
|
||||
<CustomButton
|
||||
Icon={isZkeyDownloading[circuitName as CircuitName] ? <Spinner /> : isConnecting ? <Spinner /> : generatingProof ? <Spinner /> : <CheckCircle />}
|
||||
isDisabled={isZkeyDownloading[circuitName as CircuitName] || isConnecting || generatingProof}
|
||||
text={isZkeyDownloading[circuitName as CircuitName] ? "Downloading files..." : isConnecting ? "Connecting..." : generatingProof ? "Generating Proof..." : "Verify"}
|
||||
Icon={
|
||||
isZkeyDownloading[circuitName as CircuitName] ? (
|
||||
<Spinner />
|
||||
) : isConnecting ? (
|
||||
<Spinner />
|
||||
) : generatingProof ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<CheckCircle />
|
||||
)
|
||||
}
|
||||
isDisabled={
|
||||
isZkeyDownloading[circuitName as CircuitName] ||
|
||||
isConnecting ||
|
||||
generatingProof
|
||||
}
|
||||
text={
|
||||
isZkeyDownloading[circuitName as CircuitName]
|
||||
? 'Downloading files...'
|
||||
: isConnecting
|
||||
? 'Connecting...'
|
||||
: generatingProof
|
||||
? 'Generating Proof...'
|
||||
: 'Verify'
|
||||
}
|
||||
onPress={registered ? handleProve : () => setSheetRegisterIsOpen(true)}
|
||||
bgColor={isConnecting || generatingProof ? separatorColor : bgGreen}
|
||||
disabledOnPress={() => toast.show('⏳', {
|
||||
message: isZkeyDownloading[circuitName as CircuitName] ? "⏳ Downloading files..." : isConnecting ? "Connecting to server..." : "Proof is generating",
|
||||
customData: {
|
||||
type: "info",
|
||||
},
|
||||
})}
|
||||
disabledOnPress={() =>
|
||||
toast.show('⏳', {
|
||||
message: isZkeyDownloading[circuitName as CircuitName]
|
||||
? '⏳ Downloading files...'
|
||||
: isConnecting
|
||||
? 'Connecting to server...'
|
||||
: 'Proof is generating',
|
||||
customData: {
|
||||
type: 'info',
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
</YStack >
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import useUserStore from '../stores/userStore';
|
||||
|
||||
import { Spinner, Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import { YStack, Text, Spinner, XStack } from 'tamagui';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { textBlack } from '../utils/colors';
|
||||
|
||||
const SplashScreen = () => {
|
||||
const { userLoaded, passportData } = useUserStore();
|
||||
const { setSelectedTab } = useNavigationStore();
|
||||
useEffect(() => {
|
||||
if (userLoaded) {
|
||||
if (passportData && passportData.dg2Hash && !passportData.mockUser) {
|
||||
setSelectedTab('app');
|
||||
} else {
|
||||
setSelectedTab('start');
|
||||
}
|
||||
}
|
||||
}, [userLoaded]);
|
||||
return (
|
||||
<YStack ai="center" f={1} gap="$8" mt="$18" mb="$8">
|
||||
<Text fontSize="$9">OpenPassport</Text>
|
||||
<XStack f={1} />
|
||||
<Spinner color={textBlack} />
|
||||
</YStack>
|
||||
);
|
||||
const { userLoaded, passportData } = useUserStore();
|
||||
const { setSelectedTab } = useNavigationStore();
|
||||
useEffect(() => {
|
||||
if (userLoaded) {
|
||||
if (passportData && passportData.dg2Hash && !passportData.mockUser) {
|
||||
setSelectedTab('app');
|
||||
} else {
|
||||
setSelectedTab('start');
|
||||
}
|
||||
}
|
||||
}, [userLoaded]); // eslint-disable-line
|
||||
return (
|
||||
<YStack ai="center" f={1} gap="$8" mt="$18" mb="$8">
|
||||
<Text fontSize="$9">OpenPassport</Text>
|
||||
<XStack f={1} />
|
||||
<Spinner color={textBlack} />
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default SplashScreen;
|
||||
|
||||
@@ -1,43 +1,47 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { YStack, Text, XStack, Image } from 'tamagui';
|
||||
import { ArrowRight, ShieldCheck } from '@tamagui/lucide-icons';
|
||||
import { bgGreen, bgWhite, textBlack } from '../utils/colors';
|
||||
import OPENPASSPORT_LOGO from '../images/openpassport.png';
|
||||
|
||||
import { ArrowRight } from '@tamagui/lucide-icons';
|
||||
import { Image, Text, YStack } from 'tamagui';
|
||||
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import OPENPASSPORT_LOGO from '../images/openpassport.png';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
|
||||
|
||||
import { textBlack } from '../utils/colors';
|
||||
|
||||
const StartScreen: React.FC = () => {
|
||||
const { setSelectedTab } = useNavigationStore();
|
||||
|
||||
const {
|
||||
setSelectedTab
|
||||
} = useNavigationStore();
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<YStack f={1} mt="$6" mb="$2.5" gap="$0" ai="center" jc="space-between">
|
||||
<Text fontSize={38} color={textBlack} textAlign="center">
|
||||
Welcome to OpenPassport.
|
||||
</Text>
|
||||
<Image src={OPENPASSPORT_LOGO} width={400} height={300} />
|
||||
<Text textAlign="center" fontSize="$4" color={textBlack}>
|
||||
No information will be shared without your explicit consent.
|
||||
</Text>
|
||||
</YStack>
|
||||
|
||||
return (
|
||||
<YStack f={1} >
|
||||
|
||||
<YStack f={1} mt="$6" mb="$2.5" gap="$0" ai="center" jc="space-between" >
|
||||
|
||||
<Text fontSize={38} color={textBlack} textAlign='center'>Welcome to OpenPassport.</Text>
|
||||
<Image src={OPENPASSPORT_LOGO} width={400} height={300} />
|
||||
<Text textAlign='center' fontSize="$4" color={textBlack}>No information will be shared without your explicit consent.</Text>
|
||||
|
||||
</YStack>
|
||||
|
||||
<YStack gap="$2.5">
|
||||
<CustomButton Icon={<ArrowRight />} text="Use my passport" onPress={() => {
|
||||
setSelectedTab("scan");
|
||||
}} />
|
||||
<CustomButton bgColor="white" Icon={<ArrowRight />} text="Use a mock passport" onPress={() => {
|
||||
setSelectedTab("mock");
|
||||
}} />
|
||||
|
||||
</YStack>
|
||||
|
||||
</YStack >
|
||||
);
|
||||
<YStack gap="$2.5">
|
||||
<CustomButton
|
||||
Icon={<ArrowRight />}
|
||||
text="Use my passport"
|
||||
onPress={() => {
|
||||
setSelectedTab('scan');
|
||||
}}
|
||||
/>
|
||||
<CustomButton
|
||||
bgColor="white"
|
||||
Icon={<ArrowRight />}
|
||||
text="Use a mock passport"
|
||||
onPress={() => {
|
||||
setSelectedTab('mock');
|
||||
}}
|
||||
/>
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default StartScreen;
|
||||
|
||||
@@ -1,76 +1,140 @@
|
||||
import React from 'react';
|
||||
import { YStack, Text, XStack, Separator, ScrollView } from 'tamagui';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { textBlack, separatorColor } from '../utils/colors';
|
||||
|
||||
import { ScrollView, Separator, Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { separatorColor, textBlack } from '../utils/colors';
|
||||
|
||||
const UserInfo: React.FC = () => {
|
||||
const { passportData } = useUserStore();
|
||||
const passportMetaData = passportData ? parsePassportData(passportData) : null;
|
||||
const { passportData } = useUserStore();
|
||||
const passportMetaData = passportData
|
||||
? parsePassportData(passportData)
|
||||
: null;
|
||||
|
||||
const InfoRow = ({ label, value }: { label: string; value: string | number }) => (
|
||||
<XStack py="$2" justifyContent="space-between">
|
||||
<Text color={textBlack} fontSize="$5">{label}</Text>
|
||||
<Text color={textBlack} fontSize="$5">{value}</Text>
|
||||
</XStack>
|
||||
);
|
||||
const InfoRow = ({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}) => (
|
||||
<XStack py="$2" justifyContent="space-between">
|
||||
<Text color={textBlack} fontSize="$5">
|
||||
{label}
|
||||
</Text>
|
||||
<Text color={textBlack} fontSize="$5">
|
||||
{value}
|
||||
</Text>
|
||||
</XStack>
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<YStack f={1} p="$0" gap="$2" jc="flex-start" py="$2" >
|
||||
<Text fontSize="$8" color={textBlack} mb="$4">Passport Data Info</Text>
|
||||
<Separator borderColor={separatorColor} />
|
||||
return (
|
||||
<ScrollView>
|
||||
<YStack f={1} p="$0" gap="$2" jc="flex-start" py="$2">
|
||||
<Text fontSize="$8" color={textBlack} mb="$4">
|
||||
Passport Data Info
|
||||
</Text>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="Data Groups" value={passportMetaData?.dataGroups || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="Data Groups"
|
||||
value={passportMetaData?.dataGroups || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="DG1 Hash Function" value={passportMetaData?.dg1HashFunction || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="DG1 Hash Function"
|
||||
value={passportMetaData?.dg1HashFunction || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="DG1 Hash Offset" value={passportMetaData?.dg1HashOffset || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="DG1 Hash Offset"
|
||||
value={passportMetaData?.dg1HashOffset || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="eContent Size" value={passportMetaData?.eContentSize || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="eContent Size"
|
||||
value={passportMetaData?.eContentSize || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="eContent Hash Function" value={passportMetaData?.eContentHashFunction || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="eContent Hash Function"
|
||||
value={passportMetaData?.eContentHashFunction || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="eContent Hash Offset" value={passportMetaData?.eContentHashOffset || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="eContent Hash Offset"
|
||||
value={passportMetaData?.eContentHashOffset || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="Signed Attributes Size" value={passportMetaData?.signedAttrSize || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="Signed Attributes Size"
|
||||
value={passportMetaData?.signedAttrSize || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="Signed Attributes Hash Function" value={passportMetaData?.signedAttrHashFunction || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="Signed Attributes Hash Function"
|
||||
value={passportMetaData?.signedAttrHashFunction || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="Signature Algorithm" value={passportMetaData?.signatureAlgorithm || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="Signature Algorithm"
|
||||
value={passportMetaData?.signatureAlgorithm || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="Signature Algorithm Details" value={passportMetaData?.curveOrExponent || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="Signature Algorithm Details"
|
||||
value={passportMetaData?.curveOrExponent || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="Signature Algorithm Bits" value={passportMetaData?.signatureAlgorithmBits || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="Signature Algorithm Bits"
|
||||
value={passportMetaData?.signatureAlgorithmBits || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="CSCA Found" value={passportMetaData?.cscaFound ? 'Yes' : 'No'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="CSCA Found"
|
||||
value={passportMetaData?.cscaFound ? 'Yes' : 'No'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="CSCA Hash Function" value={passportMetaData?.cscaHashFunction || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="CSCA Hash Function"
|
||||
value={passportMetaData?.cscaHashFunction || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="CSCA Signature Algorithm" value={passportMetaData?.cscaSignature || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="CSCA Signature Algorithm"
|
||||
value={passportMetaData?.cscaSignature || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="CSCA Signature Algorithm Details" value={passportMetaData?.cscaCurveOrExponent || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
<InfoRow
|
||||
label="CSCA Signature Algorithm Details"
|
||||
value={passportMetaData?.cscaCurveOrExponent || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
|
||||
<InfoRow label="CSCA Signature Algorithm Bits" value={passportMetaData?.cscaSignatureAlgorithmBits || 'None'} />
|
||||
<Separator borderColor={separatorColor} />
|
||||
</YStack>
|
||||
</ScrollView>
|
||||
);
|
||||
<InfoRow
|
||||
label="CSCA Signature Algorithm Bits"
|
||||
value={passportMetaData?.cscaSignatureAlgorithmBits || 'None'}
|
||||
/>
|
||||
<Separator borderColor={separatorColor} />
|
||||
</YStack>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInfo;
|
||||
|
||||
@@ -1,23 +1,39 @@
|
||||
import React from 'react';
|
||||
import { YStack, Text, XStack } from 'tamagui';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import { scanQRCode } from '../utils/qrCode';
|
||||
import { QrCode } from '@tamagui/lucide-icons';
|
||||
|
||||
import { QrCode } from '@tamagui/lucide-icons';
|
||||
import { Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import { scanQRCode } from '../utils/qrCode';
|
||||
|
||||
const SuccessScreen: React.FC = () => {
|
||||
return (
|
||||
<YStack f={1} >
|
||||
<YStack f={1}>
|
||||
<YStack f={1} mt="$8">
|
||||
<Text ml="$1" fontSize="$10" color={textBlack}><Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Success</Text>, the proof has been verified</Text>
|
||||
<Text ml="$1" fontSize="$10" color={textBlack}>
|
||||
<Text
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
Success
|
||||
</Text>
|
||||
, the proof has been verified
|
||||
</Text>
|
||||
<XStack f={1} />
|
||||
</YStack>
|
||||
|
||||
<CustomButton Icon={<QrCode size={18} color={textBlack} />} text="Scan another QR code" onPress={() => { scanQRCode() }} />
|
||||
|
||||
<CustomButton
|
||||
Icon={<QrCode size={18} color={textBlack} />}
|
||||
text="Scan another QR code"
|
||||
onPress={() => {
|
||||
scanQRCode();
|
||||
}}
|
||||
/>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default SuccessScreen;
|
||||
export default SuccessScreen;
|
||||
|
||||
@@ -1,28 +1,53 @@
|
||||
import React from 'react';
|
||||
import { YStack, Text, XStack } from 'tamagui';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import { QrCode } from '@tamagui/lucide-icons';
|
||||
import React from 'react';
|
||||
import { Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
import CustomButton from '../components/CustomButton';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import { scanQRCode } from '../utils/qrCode';
|
||||
|
||||
const WrongProofScreen: React.FC = () => {
|
||||
const { proofVerificationResult } = useUserStore();
|
||||
|
||||
const formatFieldName = (field: string) => {
|
||||
return field.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
return field
|
||||
.split('_')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
const fieldsToCheck = [
|
||||
'scope', 'merkle_root_commitment', 'merkle_root_csca', 'attestation_id', 'current_date', 'issuing_state',
|
||||
'name', 'passport_number', 'nationality', 'date_of_birth', 'gender',
|
||||
'expiry_date', 'older_than', 'owner_of', 'blinded_dsc_commitment', 'proof',
|
||||
'dscProof', 'dsc', 'pubKey', 'ofac', 'forbidden_countries_list'
|
||||
'scope',
|
||||
'merkle_root_commitment',
|
||||
'merkle_root_csca',
|
||||
'attestation_id',
|
||||
'current_date',
|
||||
'issuing_state',
|
||||
'name',
|
||||
'passport_number',
|
||||
'nationality',
|
||||
'date_of_birth',
|
||||
'gender',
|
||||
'expiry_date',
|
||||
'older_than',
|
||||
'owner_of',
|
||||
'blinded_dsc_commitment',
|
||||
'proof',
|
||||
'dscProof',
|
||||
'dsc',
|
||||
'pubKey',
|
||||
'ofac',
|
||||
'forbidden_countries_list',
|
||||
];
|
||||
|
||||
const failedConditions = [];
|
||||
for (const field of fieldsToCheck) {
|
||||
console.log(`Checking field ${field}: ${JSON.stringify((proofVerificationResult as any)[field])}`);
|
||||
console.log(
|
||||
`Checking field ${field}: ${JSON.stringify(
|
||||
(proofVerificationResult as any)[field],
|
||||
)}`,
|
||||
);
|
||||
if ((proofVerificationResult as any)[field] === false) {
|
||||
failedConditions.push(formatFieldName(field));
|
||||
}
|
||||
@@ -31,10 +56,18 @@ const WrongProofScreen: React.FC = () => {
|
||||
console.log('Failed conditions:', JSON.stringify(failedConditions));
|
||||
|
||||
return (
|
||||
<YStack f={1} >
|
||||
<YStack f={1} mt="$4" >
|
||||
<YStack f={1}>
|
||||
<YStack f={1} mt="$4">
|
||||
<Text ml="$1" fontSize={34} color={textBlack}>
|
||||
<Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Oops</Text>, the proof is not valid.
|
||||
<Text
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
Oops
|
||||
</Text>
|
||||
, the proof is not valid.
|
||||
</Text>
|
||||
{(proofVerificationResult as any).error ? (
|
||||
<Text ml="$2" mt="$3" fontSize="$8" color={textBlack}>
|
||||
@@ -43,25 +76,55 @@ const WrongProofScreen: React.FC = () => {
|
||||
) : (
|
||||
<>
|
||||
<Text ml="$2" mt="$3" fontSize="$8" color={textBlack}>
|
||||
Some of the <Text >conditions</Text> have not been satisfied:
|
||||
Some of the <Text>conditions</Text> have not been satisfied:
|
||||
</Text>
|
||||
<YStack ml="$4" mt="$5">
|
||||
{failedConditions.map((condition, index) => (
|
||||
<Text key={index} fontSize="$7" color={textBlack} >
|
||||
· <Text key={index} style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>{condition}</Text>
|
||||
<Text key={index} fontSize="$7" color={textBlack}>
|
||||
·{' '}
|
||||
<Text
|
||||
key={index}
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
{condition}
|
||||
</Text>
|
||||
</Text>
|
||||
))}
|
||||
</YStack>
|
||||
</>
|
||||
)}
|
||||
<Text ml="$2" mt="$8" fontSize="$7" color={textBlack} style={{ opacity: 0.7 }}>
|
||||
<Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Check again</Text> your eligibility, if you are sure to be eligible to this verification please contact OpenPassport support.
|
||||
<Text
|
||||
ml="$2"
|
||||
mt="$8"
|
||||
fontSize="$7"
|
||||
color={textBlack}
|
||||
style={{ opacity: 0.7 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: bgGreen,
|
||||
}}
|
||||
>
|
||||
Check again
|
||||
</Text>{' '}
|
||||
your eligibility, if you are sure to be eligible to this verification
|
||||
please contact OpenPassport support.
|
||||
</Text>
|
||||
<XStack f={1} />
|
||||
<CustomButton Icon={<QrCode size={18} color={textBlack} />} text="Scan another QR code" onPress={() => { scanQRCode() }} />
|
||||
<CustomButton
|
||||
Icon={<QrCode size={18} color={textBlack} />}
|
||||
text="Scan another QR code"
|
||||
onPress={() => {
|
||||
scanQRCode();
|
||||
}}
|
||||
/>
|
||||
</YStack>
|
||||
</YStack >
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default WrongProofScreen;
|
||||
export default WrongProofScreen;
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
import { create } from 'zustand'
|
||||
import { IsZkeyDownloading, ShowWarningModalProps } from '../utils/zkeyDownload';
|
||||
import { useToastController } from '@tamagui/toast';
|
||||
import { create } from 'zustand';
|
||||
import { OpenPassportApp } from '../../../common/src/utils/appType';
|
||||
import {
|
||||
IsZkeyDownloading,
|
||||
ShowWarningModalProps,
|
||||
} from '../utils/zkeyDownload';
|
||||
|
||||
interface NavigationState {
|
||||
isZkeyDownloading: IsZkeyDownloading
|
||||
showWarningModal: ShowWarningModalProps
|
||||
hideData: boolean
|
||||
toast: ReturnType<typeof useToastController>
|
||||
selectedTab: string
|
||||
setSelectedTab: (tab: string) => void
|
||||
selectedApp: OpenPassportApp | null
|
||||
setSelectedApp: (app: OpenPassportApp | null) => void
|
||||
showRegistrationErrorSheet: boolean
|
||||
registrationErrorMessage: string
|
||||
isZkeyDownloading: IsZkeyDownloading;
|
||||
showWarningModal: ShowWarningModalProps;
|
||||
hideData: boolean;
|
||||
toast: ReturnType<typeof useToastController>;
|
||||
selectedTab: string;
|
||||
setSelectedTab: (tab: string) => void;
|
||||
selectedApp: OpenPassportApp | null;
|
||||
setSelectedApp: (app: OpenPassportApp | null) => void;
|
||||
showRegistrationErrorSheet: boolean;
|
||||
registrationErrorMessage: string;
|
||||
setToast: (toast: ReturnType<typeof useToastController>) => void;
|
||||
update: (patch: any) => void
|
||||
nfcSheetIsOpen: boolean
|
||||
setNfcSheetIsOpen: (isOpen: boolean) => void
|
||||
zkeyDownloadedPercentage: number
|
||||
setZkeyDownloadedPercentage: (percentage: number) => void
|
||||
update: (patch: any) => void;
|
||||
nfcSheetIsOpen: boolean;
|
||||
setNfcSheetIsOpen: (isOpen: boolean) => void;
|
||||
zkeyDownloadedPercentage: number;
|
||||
setZkeyDownloadedPercentage: (percentage: number) => void;
|
||||
}
|
||||
|
||||
const useNavigationStore = create<NavigationState>((set, get) => ({
|
||||
zkeyDownloadedPercentage: 100,
|
||||
setZkeyDownloadedPercentage: (percentage: number) => set({ zkeyDownloadedPercentage: percentage }),
|
||||
setZkeyDownloadedPercentage: (percentage: number) =>
|
||||
set({ zkeyDownloadedPercentage: percentage }),
|
||||
isZkeyDownloading: {
|
||||
prove_rsa_65537_sha1: false,
|
||||
prove_rsa_65537_sha256: false,
|
||||
@@ -33,32 +37,32 @@ const useNavigationStore = create<NavigationState>((set, get) => ({
|
||||
},
|
||||
showWarningModal: {
|
||||
show: false,
|
||||
circuit: "",
|
||||
circuit: '',
|
||||
size: 0,
|
||||
},
|
||||
hideData: false,
|
||||
|
||||
showRegistrationErrorSheet: false,
|
||||
registrationErrorMessage: "",
|
||||
registrationErrorMessage: '',
|
||||
|
||||
toast: null as unknown as ReturnType<typeof useToastController>,
|
||||
|
||||
selectedTab: "scan",
|
||||
selectedTab: 'scan',
|
||||
selectedApp: null,
|
||||
|
||||
setToast: (toast) => set({ toast }),
|
||||
setSelectedApp: (app) => set({ selectedApp: app }),
|
||||
setToast: toast => set({ toast }),
|
||||
setSelectedApp: app => set({ selectedApp: app }),
|
||||
|
||||
setSelectedTab: (tab) => set({ selectedTab: tab }),
|
||||
setSelectedTab: tab => set({ selectedTab: tab }),
|
||||
|
||||
update: (patch) => {
|
||||
update: patch => {
|
||||
set({
|
||||
...get(),
|
||||
...patch,
|
||||
});
|
||||
},
|
||||
nfcSheetIsOpen: false,
|
||||
setNfcSheetIsOpen: (isOpen) => set({ nfcSheetIsOpen: isOpen }),
|
||||
}))
|
||||
setNfcSheetIsOpen: isOpen => set({ nfcSheetIsOpen: isOpen }),
|
||||
}));
|
||||
|
||||
export default useNavigationStore
|
||||
export default useNavigationStore;
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
import { create } from 'zustand'
|
||||
import {
|
||||
DEFAULT_PNUMBER,
|
||||
DEFAULT_DOB,
|
||||
DEFAULT_DOE,
|
||||
} from '@env';
|
||||
import { PassportData, Proof } from '../../../common/src/utils/types';
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
import { loadPassportData, loadSecret, loadSecretOrCreateIt, storePassportData } from '../utils/keychain';
|
||||
import { DEFAULT_DOB, DEFAULT_DOE, DEFAULT_PNUMBER } from '@env';
|
||||
import { resetGenericPassword } from 'react-native-keychain';
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { generateDscSecret } from '../../../common/src/utils/csca';
|
||||
import { PassportData, Proof } from '../../../common/src/utils/types';
|
||||
import {
|
||||
loadPassportData,
|
||||
loadSecretOrCreateIt,
|
||||
storePassportData,
|
||||
} from '../utils/keychain';
|
||||
|
||||
interface UserState {
|
||||
passportNumber: string
|
||||
dateOfBirth: string
|
||||
dateOfExpiry: string
|
||||
countryCode: string
|
||||
registered: boolean
|
||||
passportData: PassportData | null
|
||||
secret: string
|
||||
cscaProof: Proof | null
|
||||
localProof: Proof | null
|
||||
dscSecret: string | null
|
||||
userLoaded: boolean
|
||||
initUserStore: () => void
|
||||
registerPassportData: (passportData: PassportData) => Promise<void>
|
||||
clearPassportDataFromStorage: () => void
|
||||
clearSecretFromStorage: () => void
|
||||
clearProofsFromStorage: () => void
|
||||
update: (patch: any) => void
|
||||
deleteMrzFields: () => void
|
||||
setRegistered: (registered: boolean) => void
|
||||
setDscSecret: (dscSecret: string) => void
|
||||
setUserLoaded: (userLoaded: boolean) => void
|
||||
proofVerificationResult: string,
|
||||
setProofVerificationResult: (proofVerificationResult: string) => void
|
||||
passportNumber: string;
|
||||
dateOfBirth: string;
|
||||
dateOfExpiry: string;
|
||||
countryCode: string;
|
||||
registered: boolean;
|
||||
passportData: PassportData | null;
|
||||
secret: string;
|
||||
cscaProof: Proof | null;
|
||||
localProof: Proof | null;
|
||||
dscSecret: string | null;
|
||||
userLoaded: boolean;
|
||||
initUserStore: () => void;
|
||||
registerPassportData: (passportData: PassportData) => Promise<void>;
|
||||
clearPassportDataFromStorage: () => void;
|
||||
clearSecretFromStorage: () => void;
|
||||
clearProofsFromStorage: () => void;
|
||||
update: (patch: any) => void;
|
||||
deleteMrzFields: () => void;
|
||||
setRegistered: (registered: boolean) => void;
|
||||
setDscSecret: (dscSecret: string) => void;
|
||||
setUserLoaded: (userLoaded: boolean) => void;
|
||||
proofVerificationResult: string;
|
||||
setProofVerificationResult: (proofVerificationResult: string) => void;
|
||||
}
|
||||
|
||||
const useUserStore = create<UserState>((set, get) => ({
|
||||
userLoaded: false,
|
||||
passportNumber: DEFAULT_PNUMBER ?? "",
|
||||
dateOfBirth: DEFAULT_DOB ?? "",
|
||||
dateOfExpiry: DEFAULT_DOE ?? "",
|
||||
countryCode: "",
|
||||
passportNumber: DEFAULT_PNUMBER ?? '',
|
||||
dateOfBirth: DEFAULT_DOB ?? '',
|
||||
dateOfExpiry: DEFAULT_DOE ?? '',
|
||||
countryCode: '',
|
||||
dscSecret: null,
|
||||
registered: false,
|
||||
passportData: null,
|
||||
secret: "",
|
||||
secret: '',
|
||||
cscaProof: null,
|
||||
localProof: null,
|
||||
setRegistered: (registered: boolean) => {
|
||||
@@ -56,7 +57,7 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
setUserLoaded: (userLoaded: boolean) => {
|
||||
set({ userLoaded });
|
||||
},
|
||||
proofVerificationResult: "null",
|
||||
proofVerificationResult: 'null',
|
||||
setProofVerificationResult: (proofVerificationResult: string) => {
|
||||
set({ proofVerificationResult });
|
||||
},
|
||||
@@ -75,7 +76,7 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
|
||||
const passportDataString = await loadPassportData();
|
||||
if (!passportDataString) {
|
||||
console.log("No passport data found, starting onboarding flow")
|
||||
console.log('No passport data found, starting onboarding flow');
|
||||
set({
|
||||
userLoaded: true,
|
||||
});
|
||||
@@ -83,11 +84,13 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
}
|
||||
|
||||
// const isAlreadyRegistered = await isCommitmentRegistered(secret, JSON.parse(passportData));
|
||||
const isAlreadyRegistered = true
|
||||
const passportData: PassportData = JSON.parse(passportDataString)
|
||||
const isAlreadyRegistered = true;
|
||||
const passportData: PassportData = JSON.parse(passportDataString);
|
||||
|
||||
if (!isAlreadyRegistered) {
|
||||
console.log("not registered but passport data found, skipping to nextScreen")
|
||||
console.log(
|
||||
'not registered but passport data found, skipping to nextScreen',
|
||||
);
|
||||
set({
|
||||
passportData: passportData,
|
||||
userLoaded: true,
|
||||
@@ -96,7 +99,9 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("registered and passport data found, skipping to app selection screen")
|
||||
console.log(
|
||||
'registered and passport data found, skipping to app selection screen',
|
||||
);
|
||||
set({
|
||||
passportData: passportData,
|
||||
registered: true,
|
||||
@@ -107,19 +112,21 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
// When reading passport for the first time:
|
||||
// - Check presence of secret. If there is none, create one and store it
|
||||
// - Store the passportData and try registering the commitment in the background
|
||||
registerPassportData: async (passportData) => {
|
||||
registerPassportData: async passportData => {
|
||||
const alreadyStoredPassportData = await loadPassportData();
|
||||
|
||||
if (alreadyStoredPassportData) {
|
||||
console.log("a passportData is already stored, replacing it with the new one")
|
||||
console.log(
|
||||
'a passportData is already stored, replacing it with the new one',
|
||||
);
|
||||
}
|
||||
|
||||
await storePassportData(passportData)
|
||||
await storePassportData(passportData);
|
||||
set({ passportData });
|
||||
},
|
||||
|
||||
clearPassportDataFromStorage: async () => {
|
||||
await Keychain.resetGenericPassword({ service: "passportData" });
|
||||
await resetGenericPassword({ service: 'passportData' });
|
||||
},
|
||||
|
||||
clearProofsFromStorage: async () => {
|
||||
@@ -128,21 +135,22 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
},
|
||||
|
||||
clearSecretFromStorage: async () => {
|
||||
await Keychain.resetGenericPassword({ service: "secret" });
|
||||
await resetGenericPassword({ service: 'secret' });
|
||||
},
|
||||
|
||||
update: (patch) => {
|
||||
update: patch => {
|
||||
set({
|
||||
...get(),
|
||||
...patch,
|
||||
});
|
||||
},
|
||||
|
||||
deleteMrzFields: () => set({
|
||||
passportNumber: "",
|
||||
dateOfBirth: "",
|
||||
dateOfExpiry: "",
|
||||
}),
|
||||
}))
|
||||
deleteMrzFields: () =>
|
||||
set({
|
||||
passportNumber: '',
|
||||
dateOfBirth: '',
|
||||
dateOfExpiry: '',
|
||||
}),
|
||||
}));
|
||||
|
||||
export default useUserStore
|
||||
export default useUserStore;
|
||||
|
||||
6
app/src/types/country-iso-3-to-2.d.ts
vendored
6
app/src/types/country-iso-3-to-2.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
declare module 'country-iso-3-to-2' {
|
||||
function getCountryISO2(iso3: string): string;
|
||||
export = getCountryISO2;
|
||||
}
|
||||
function getCountryISO2(iso3: string): string;
|
||||
export = getCountryISO2;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
import { formatDateToYYMMDD, extractMRZInfo } from './utils';
|
||||
import * as amplitude from '@amplitude/analytics-react-native';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { extractMRZInfo, formatDateToYYMMDD } from './utils';
|
||||
|
||||
export const startCameraScan = async () => {
|
||||
const { toast, setSelectedTab } = useNavigationStore.getState();
|
||||
@@ -10,22 +11,24 @@ export const startCameraScan = async () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
try {
|
||||
const result = await NativeModules.MRZScannerModule.startScanning();
|
||||
console.log("Scan result:", result);
|
||||
console.log(`Document Number: ${result.documentNumber}, Expiry Date: ${result.expiryDate}, Birth Date: ${result.birthDate}`);
|
||||
console.log('Scan result:', result);
|
||||
console.log(
|
||||
`Document Number: ${result.documentNumber}, Expiry Date: ${result.expiryDate}, Birth Date: ${result.birthDate}`,
|
||||
);
|
||||
|
||||
useUserStore.setState({
|
||||
passportNumber: result.documentNumber,
|
||||
dateOfBirth: formatDateToYYMMDD(result.birthDate),
|
||||
dateOfExpiry: formatDateToYYMMDD(result.expiryDate),
|
||||
})
|
||||
});
|
||||
|
||||
setSelectedTab("nfc");
|
||||
toast.show("✔︎", {
|
||||
setSelectedTab('nfc');
|
||||
toast.show('✔︎', {
|
||||
message: 'Scan successful',
|
||||
customData: {
|
||||
type: "success",
|
||||
type: 'success',
|
||||
},
|
||||
})
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
amplitude.track('camera_scan_error', { error: e });
|
||||
@@ -34,21 +37,22 @@ export const startCameraScan = async () => {
|
||||
NativeModules.CameraActivityModule.startCameraActivity()
|
||||
.then((mrzInfo: string) => {
|
||||
try {
|
||||
const { documentNumber, birthDate, expiryDate } = extractMRZInfo(mrzInfo);
|
||||
const { documentNumber, birthDate, expiryDate } =
|
||||
extractMRZInfo(mrzInfo);
|
||||
|
||||
useUserStore.setState({
|
||||
passportNumber: documentNumber,
|
||||
dateOfBirth: birthDate,
|
||||
dateOfExpiry: expiryDate,
|
||||
})
|
||||
});
|
||||
|
||||
setSelectedTab("nfc");
|
||||
toast.show("✔︎", {
|
||||
setSelectedTab('nfc');
|
||||
toast.show('✔︎', {
|
||||
message: 'Scan successful',
|
||||
customData: {
|
||||
type: "success",
|
||||
type: 'success',
|
||||
},
|
||||
})
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('Invalid MRZ format:', error.message);
|
||||
amplitude.track('invalid_mrz_format', { error: error.message });
|
||||
@@ -59,4 +63,4 @@ export const startCameraScan = async () => {
|
||||
amplitude.track('camera_scan_error', { error: error.message });
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
export const borderColor = "#343434";
|
||||
export const bgColor = "#161616";
|
||||
export const componentBgColor = "#1c1c1c";
|
||||
export const componentBgColor2 = "#232323";
|
||||
export const blueColor = "#3185FC";
|
||||
export const textColor1 = "#ededed";
|
||||
export const textColor2 = "#a0a0a0";
|
||||
export const blueColorDark = "#11233e"
|
||||
export const blueColorLight = "#0090ff"
|
||||
export const greenColorDark = "#10291e"
|
||||
export const greenColorLight = "#30a46b"
|
||||
export const redColorDark = "#3c181a"
|
||||
export const redColorLight = "#e5484d"
|
||||
export const yellowColorDark = "#2d2200"
|
||||
export const yellowColorLight = "#f5d90a"
|
||||
export const bgWhite = "#F5F5F5"
|
||||
export const textBlack = "#333333"
|
||||
export const bgGreen2 = "#94FBAB"
|
||||
export const bgGreen = "#AEECEF"
|
||||
export const bgBlue = "#69DFFF"
|
||||
export const separatorColor = "#E0E0E0"
|
||||
export const borderColor = '#343434';
|
||||
export const bgColor = '#161616';
|
||||
export const componentBgColor = '#1c1c1c';
|
||||
export const componentBgColor2 = '#232323';
|
||||
export const blueColor = '#3185FC';
|
||||
export const textColor1 = '#ededed';
|
||||
export const textColor2 = '#a0a0a0';
|
||||
export const blueColorDark = '#11233e';
|
||||
export const blueColorLight = '#0090ff';
|
||||
export const greenColorDark = '#10291e';
|
||||
export const greenColorLight = '#30a46b';
|
||||
export const redColorDark = '#3c181a';
|
||||
export const redColorLight = '#e5484d';
|
||||
export const yellowColorDark = '#2d2200';
|
||||
export const yellowColorLight = '#f5d90a';
|
||||
export const bgWhite = '#F5F5F5';
|
||||
export const textBlack = '#333333';
|
||||
export const bgGreen2 = '#94FBAB';
|
||||
export const bgGreen = '#AEECEF';
|
||||
export const bgBlue = '#69DFFF';
|
||||
export const separatorColor = '#E0E0E0';
|
||||
|
||||
@@ -1,58 +1,61 @@
|
||||
import axios from 'axios';
|
||||
import { contribute_publicKey } from '../../../common/src/constants/constants';
|
||||
import forge from 'node-forge';
|
||||
|
||||
import { contribute_publicKey } from '../../../common/src/constants/constants';
|
||||
|
||||
export async function contribute(passportData: any): Promise<void> {
|
||||
console.log('Contributing...');
|
||||
const textToEncrypt = JSON.stringify(passportData);
|
||||
console.log('Text to Encrypt:', textToEncrypt);
|
||||
|
||||
console.log("Contributing...")
|
||||
const textToEncrypt = JSON.stringify(passportData);
|
||||
console.log("Text to Encrypt:", textToEncrypt);
|
||||
try {
|
||||
const aesKey = forge.random.getBytesSync(32);
|
||||
const iv = forge.random.getBytesSync(16);
|
||||
|
||||
try {
|
||||
const aesKey = forge.random.getBytesSync(32);
|
||||
const iv = forge.random.getBytesSync(16);
|
||||
console.log('Generated AES Key (base64):', forge.util.encode64(aesKey));
|
||||
console.log('Generated IV (base64):', forge.util.encode64(iv));
|
||||
|
||||
console.log("Generated AES Key (base64):", forge.util.encode64(aesKey));
|
||||
console.log("Generated IV (base64):", forge.util.encode64(iv));
|
||||
const cipher = forge.cipher.createCipher('AES-CBC', aesKey);
|
||||
cipher.start({ iv: iv });
|
||||
cipher.update(forge.util.createBuffer(textToEncrypt, 'utf8'));
|
||||
cipher.finish();
|
||||
const encryptedData = cipher.output.getBytes();
|
||||
|
||||
const cipher = forge.cipher.createCipher('AES-CBC', aesKey);
|
||||
cipher.start({ iv: iv });
|
||||
cipher.update(forge.util.createBuffer(textToEncrypt, 'utf8'));
|
||||
cipher.finish();
|
||||
const encryptedData = cipher.output.getBytes();
|
||||
console.log('Encrypted Data (base64):', forge.util.encode64(encryptedData));
|
||||
|
||||
console.log("Encrypted Data (base64):", forge.util.encode64(encryptedData));
|
||||
const publicKey = forge.pki.publicKeyFromPem(contribute_publicKey);
|
||||
const encryptedAesKey = publicKey.encrypt(aesKey, 'RSA-OAEP', {
|
||||
md: forge.md.sha256.create(),
|
||||
mgf1: {
|
||||
md: forge.md.sha256.create(),
|
||||
},
|
||||
});
|
||||
|
||||
const publicKey = forge.pki.publicKeyFromPem(contribute_publicKey);
|
||||
const encryptedAesKey = publicKey.encrypt(aesKey, 'RSA-OAEP', {
|
||||
md: forge.md.sha256.create(),
|
||||
mgf1: {
|
||||
md: forge.md.sha256.create()
|
||||
}
|
||||
});
|
||||
const aesKeyBase64 = forge.util.encode64(encryptedAesKey);
|
||||
const ivBase64 = forge.util.encode64(iv);
|
||||
const encryptedDataBase64 = forge.util.encode64(encryptedData);
|
||||
|
||||
const aesKeyBase64 = forge.util.encode64(encryptedAesKey);
|
||||
const ivBase64 = forge.util.encode64(iv);
|
||||
const encryptedDataBase64 = forge.util.encode64(encryptedData);
|
||||
console.log('Encrypted AES Key (base64):', aesKeyBase64);
|
||||
console.log('Encrypted Data (base64):', encryptedDataBase64);
|
||||
|
||||
console.log("Encrypted AES Key (base64):", aesKeyBase64);
|
||||
console.log("Encrypted Data (base64):", encryptedDataBase64);
|
||||
const data = {
|
||||
aesKey: aesKeyBase64,
|
||||
iv: ivBase64,
|
||||
encryptedData: encryptedDataBase64,
|
||||
};
|
||||
|
||||
const data = {
|
||||
aesKey: aesKeyBase64,
|
||||
iv: ivBase64,
|
||||
encryptedData: encryptedDataBase64
|
||||
};
|
||||
console.log('Data to be sent:', JSON.stringify(data));
|
||||
|
||||
console.log("Data to be sent:", JSON.stringify(data));
|
||||
|
||||
const response = await axios.post('https://contribute.openpassport.app', {
|
||||
nullifier: forge.md.sha256.create().update(passportData.encryptedDigest.toString()).digest().toHex(),
|
||||
data: data
|
||||
});
|
||||
console.log("Server Response:", response.data);
|
||||
} catch (error) {
|
||||
console.error("Encryption Error:", error);
|
||||
}
|
||||
const response = await axios.post('https://contribute.openpassport.app', {
|
||||
nullifier: forge.md.sha256
|
||||
.create()
|
||||
.update(passportData.encryptedDigest.toString())
|
||||
.digest()
|
||||
.toHex(),
|
||||
data: data,
|
||||
});
|
||||
console.log('Server Response:', response.data);
|
||||
} catch (error) {
|
||||
console.error('Encryption Error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
import { castCSCAProof } from "../../../common/src/utils/types";
|
||||
import useUserStore from "../stores/userStore";
|
||||
import { ModalProofSteps } from "./utils";
|
||||
import { castCSCAProof } from '../../../common/src/utils/types';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { ModalProofSteps } from './utils';
|
||||
|
||||
export const sendCSCARequest = async (inputs_csca: any, modalServerUrl: string, setModalProofStep: (modalProofStep: number) => void) => {
|
||||
try {
|
||||
console.log("inputs_csca before requesting modal server - cscaRequest.ts");
|
||||
fetch(modalServerUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(inputs_csca)
|
||||
}).then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
useUserStore.getState().cscaProof = castCSCAProof(data);
|
||||
setModalProofStep(ModalProofSteps.MODAL_SERVER_SUCCESS);
|
||||
console.log('Response from server:', data);
|
||||
}).catch(error => {
|
||||
console.error('Error during request:', error);
|
||||
setModalProofStep(ModalProofSteps.MODAL_SERVER_ERROR);
|
||||
});
|
||||
} catch (error) {
|
||||
export const sendCSCARequest = async (
|
||||
inputs_csca: any,
|
||||
modalServerUrl: string,
|
||||
setModalProofStep: (modalProofStep: number) => void,
|
||||
) => {
|
||||
try {
|
||||
console.log('inputs_csca before requesting modal server - cscaRequest.ts');
|
||||
fetch(modalServerUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(inputs_csca),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
useUserStore.getState().cscaProof = castCSCAProof(data);
|
||||
setModalProofStep(ModalProofSteps.MODAL_SERVER_SUCCESS);
|
||||
console.log('Response from server:', data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during request:', error);
|
||||
setModalProofStep(ModalProofSteps.MODAL_SERVER_ERROR);
|
||||
}
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error during request:', error);
|
||||
setModalProofStep(ModalProofSteps.MODAL_SERVER_ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,77 +1,118 @@
|
||||
import { ArgumentsDisclose, ArgumentsProveOffChain, DisclosureOptions, OpenPassportApp } from '../../../common/src/utils/appType';
|
||||
import { PassportData } from '../../../common/src/utils/types';
|
||||
import { generateCircuitInputsDisclose, generateCircuitInputsProve } from '../../../common/src/utils/generateInputs';
|
||||
import { circuitToSelectorMode, DEFAULT_MAJORITY, getCountryCode, PASSPORT_ATTESTATION_ID } from '../../../common/src/constants/constants';
|
||||
import { revealBitmapFromAttributes } from '../../../common/src/utils/revealBitmap';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import namejson from '../../../common/ofacdata/outputs/nameSMT.json';
|
||||
import { SMT } from '@openpassport/zk-kit-smt';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
import { LeanIMT } from '@openpassport/zk-kit-lean-imt';
|
||||
|
||||
import namejson from '../../../common/ofacdata/outputs/nameSMT.json';
|
||||
import {
|
||||
circuitToSelectorMode,
|
||||
DEFAULT_MAJORITY,
|
||||
getCountryCode,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import {
|
||||
ArgumentsDisclose,
|
||||
ArgumentsProveOffChain,
|
||||
DisclosureOptions,
|
||||
OpenPassportApp,
|
||||
} from '../../../common/src/utils/appType';
|
||||
import {
|
||||
generateCircuitInputsDisclose,
|
||||
generateCircuitInputsProve,
|
||||
} from '../../../common/src/utils/generateInputs';
|
||||
import { fetchTreeFromUrl } from '../../../common/src/utils/pubkeyTree';
|
||||
import { revealBitmapFromAttributes } from '../../../common/src/utils/revealBitmap';
|
||||
import { PassportData } from '../../../common/src/utils/types';
|
||||
|
||||
import useUserStore from '../stores/userStore';
|
||||
|
||||
export const generateCircuitInputsInApp = async (
|
||||
passportData: PassportData,
|
||||
app: OpenPassportApp
|
||||
passportData: PassportData,
|
||||
app: OpenPassportApp,
|
||||
): Promise<any> => {
|
||||
const { secret, dscSecret } = useUserStore.getState();
|
||||
const selector_mode = circuitToSelectorMode[app.mode as keyof typeof circuitToSelectorMode];
|
||||
let smt = new SMT(poseidon2, true);
|
||||
smt.import(namejson);
|
||||
switch (app.mode) {
|
||||
case "prove_offchain":
|
||||
case "prove_onchain":
|
||||
const disclosureOptions: DisclosureOptions = (app.args as ArgumentsProveOffChain).disclosureOptions;
|
||||
const selector_dg1 = revealBitmapFromAttributes(disclosureOptions);
|
||||
const selector_older_than = disclosureOptions.minimumAge.enabled ? 1 : 0;
|
||||
const selector_ofac = disclosureOptions.ofac ? 1 : 0;
|
||||
const forbidden_countries_list = disclosureOptions.excludedCountries.value.map(country => getCountryCode(country));
|
||||
const inputs = generateCircuitInputsProve(
|
||||
selector_mode,
|
||||
secret,
|
||||
dscSecret as string,
|
||||
passportData,
|
||||
app.scope,
|
||||
selector_dg1,
|
||||
selector_older_than,
|
||||
disclosureOptions.minimumAge.value ?? DEFAULT_MAJORITY,
|
||||
smt,
|
||||
selector_ofac,
|
||||
forbidden_countries_list,
|
||||
app.userId,
|
||||
app.userIdType
|
||||
);
|
||||
return inputs;
|
||||
case "register":
|
||||
const selector_dg1_zero = new Array(88).fill(0);
|
||||
const selector_older_than_zero = 0;
|
||||
const selector_ofac_zero = 0;
|
||||
return generateCircuitInputsProve(
|
||||
selector_mode,
|
||||
secret,
|
||||
dscSecret as string,
|
||||
passportData,
|
||||
app.scope,
|
||||
selector_dg1_zero,
|
||||
selector_older_than_zero,
|
||||
DEFAULT_MAJORITY,
|
||||
smt,
|
||||
selector_ofac_zero,
|
||||
[],
|
||||
app.userId,
|
||||
app.userIdType
|
||||
);
|
||||
break;
|
||||
case "vc_and_disclose":
|
||||
const commitmentMerkleTreeUrl = (app as any).args.commitmentMerkleTreeUrl;
|
||||
const tree = await fetchTreeFromUrl(commitmentMerkleTreeUrl);
|
||||
const { secret, dscSecret } = useUserStore.getState();
|
||||
const selector_mode =
|
||||
circuitToSelectorMode[app.mode as keyof typeof circuitToSelectorMode];
|
||||
let smt = new SMT(poseidon2, true);
|
||||
smt.import(namejson);
|
||||
switch (app.mode) {
|
||||
case 'prove_offchain':
|
||||
case 'prove_onchain':
|
||||
const disclosureOptions: DisclosureOptions = (
|
||||
app.args as ArgumentsProveOffChain
|
||||
).disclosureOptions;
|
||||
const selector_dg1 = revealBitmapFromAttributes(disclosureOptions);
|
||||
const selector_older_than = disclosureOptions.minimumAge.enabled ? 1 : 0;
|
||||
const selector_ofac = disclosureOptions.ofac ? 1 : 0;
|
||||
const forbidden_countries_list =
|
||||
disclosureOptions.excludedCountries.value.map(country =>
|
||||
getCountryCode(country),
|
||||
);
|
||||
const inputs = generateCircuitInputsProve(
|
||||
selector_mode,
|
||||
secret,
|
||||
dscSecret as string,
|
||||
passportData,
|
||||
app.scope,
|
||||
selector_dg1,
|
||||
selector_older_than,
|
||||
disclosureOptions.minimumAge.value ?? DEFAULT_MAJORITY,
|
||||
smt,
|
||||
selector_ofac,
|
||||
forbidden_countries_list,
|
||||
app.userId,
|
||||
app.userIdType,
|
||||
);
|
||||
return inputs;
|
||||
case 'register':
|
||||
const selector_dg1_zero = new Array(88).fill(0);
|
||||
const selector_older_than_zero = 0;
|
||||
const selector_ofac_zero = 0;
|
||||
return generateCircuitInputsProve(
|
||||
selector_mode,
|
||||
secret,
|
||||
dscSecret as string,
|
||||
passportData,
|
||||
app.scope,
|
||||
selector_dg1_zero,
|
||||
selector_older_than_zero,
|
||||
DEFAULT_MAJORITY,
|
||||
smt,
|
||||
selector_ofac_zero,
|
||||
[],
|
||||
app.userId,
|
||||
app.userIdType,
|
||||
);
|
||||
case 'vc_and_disclose':
|
||||
const commitmentMerkleTreeUrl = (app as any).args.commitmentMerkleTreeUrl;
|
||||
const tree = await fetchTreeFromUrl(commitmentMerkleTreeUrl);
|
||||
|
||||
const disclosureOptionsDisclose: DisclosureOptions = (app.args as ArgumentsDisclose).disclosureOptions;
|
||||
const selector_dg1_disclose = revealBitmapFromAttributes(disclosureOptionsDisclose);
|
||||
const selector_older_than_disclose = disclosureOptionsDisclose.minimumAge.enabled ? 1 : 0;
|
||||
const selector_ofac_disclose = disclosureOptionsDisclose.ofac ? 1 : 0;
|
||||
const forbidden_countries_list_disclose = disclosureOptionsDisclose.excludedCountries.value.map(country => getCountryCode(country))
|
||||
return generateCircuitInputsDisclose(secret, PASSPORT_ATTESTATION_ID, passportData, app.scope, selector_dg1_disclose, selector_older_than_disclose, tree, disclosureOptionsDisclose.minimumAge.value ?? DEFAULT_MAJORITY, smt, selector_ofac_disclose, forbidden_countries_list_disclose, app.userId)
|
||||
}
|
||||
|
||||
}
|
||||
const disclosureOptionsDisclose: DisclosureOptions = (
|
||||
app.args as ArgumentsDisclose
|
||||
).disclosureOptions;
|
||||
const selector_dg1_disclose = revealBitmapFromAttributes(
|
||||
disclosureOptionsDisclose,
|
||||
);
|
||||
const selector_older_than_disclose = disclosureOptionsDisclose.minimumAge
|
||||
.enabled
|
||||
? 1
|
||||
: 0;
|
||||
const selector_ofac_disclose = disclosureOptionsDisclose.ofac ? 1 : 0;
|
||||
const forbidden_countries_list_disclose =
|
||||
disclosureOptionsDisclose.excludedCountries.value.map(country =>
|
||||
getCountryCode(country),
|
||||
);
|
||||
return generateCircuitInputsDisclose(
|
||||
secret,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
passportData,
|
||||
app.scope,
|
||||
selector_dg1_disclose,
|
||||
selector_older_than_disclose,
|
||||
tree,
|
||||
disclosureOptionsDisclose.minimumAge.value ?? DEFAULT_MAJORITY,
|
||||
smt,
|
||||
selector_ofac_disclose,
|
||||
forbidden_countries_list_disclose,
|
||||
app.userId,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
import { ethers } from 'ethers';
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
|
||||
import { PassportData } from '../../../common/src/utils/types';
|
||||
|
||||
export async function loadSecretOrCreateIt() {
|
||||
const secret = await loadSecret()
|
||||
const secret = await loadSecret();
|
||||
if (secret) {
|
||||
return secret
|
||||
return secret;
|
||||
}
|
||||
console.log("No secret found, creating one")
|
||||
|
||||
console.log('No secret found, creating one');
|
||||
const randomWallet = ethers.Wallet.createRandom();
|
||||
const newSecret = randomWallet.privateKey;
|
||||
await Keychain.setGenericPassword("secret", newSecret, { service: "secret" });
|
||||
return newSecret
|
||||
await Keychain.setGenericPassword('secret', newSecret, { service: 'secret' });
|
||||
return newSecret;
|
||||
}
|
||||
|
||||
export async function loadSecret() {
|
||||
const secretCreds = await Keychain.getGenericPassword({ service: "secret" })
|
||||
return secretCreds === false ? false : secretCreds.password
|
||||
const secretCreds = await Keychain.getGenericPassword({ service: 'secret' });
|
||||
return secretCreds === false ? false : secretCreds.password;
|
||||
}
|
||||
|
||||
export async function loadPassportData() {
|
||||
const passportDataCreds = await Keychain.getGenericPassword({ service: "passportData" });
|
||||
return passportDataCreds === false ? false : passportDataCreds.password
|
||||
const passportDataCreds = await Keychain.getGenericPassword({
|
||||
service: 'passportData',
|
||||
});
|
||||
return passportDataCreds === false ? false : passportDataCreds.password;
|
||||
}
|
||||
|
||||
export async function storePassportData(passportData: PassportData) {
|
||||
await Keychain.setGenericPassword("passportData", JSON.stringify(passportData), { service: "passportData" });
|
||||
}
|
||||
await Keychain.setGenericPassword(
|
||||
'passportData',
|
||||
JSON.stringify(passportData),
|
||||
{ service: 'passportData' },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,32 @@
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
// @ts-ignore
|
||||
import PassportReader from 'react-native-passport-reader';
|
||||
import { checkInputs } from '../utils/utils';
|
||||
import { PassportData } from '../../../common/src/utils/types';
|
||||
import forge from 'node-forge';
|
||||
import { Buffer } from 'buffer';
|
||||
import * as amplitude from '@amplitude/analytics-react-native';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { Buffer } from 'buffer';
|
||||
import PassportReader from 'react-native-passport-reader';
|
||||
|
||||
import { PassportData } from '../../../common/src/utils/types';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { checkInputs } from '../utils/utils';
|
||||
import { parsePassportData } from './parsePassportData';
|
||||
|
||||
export const scan = async (setModalProofStep: (modalProofStep: number) => void) => {
|
||||
const {
|
||||
passportNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
} = useUserStore.getState()
|
||||
export const scan = async (
|
||||
setModalProofStep: (modalProofStep: number) => void,
|
||||
) => {
|
||||
const { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore.getState();
|
||||
|
||||
const { toast } = useNavigationStore.getState();
|
||||
|
||||
const check = checkInputs(
|
||||
passportNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry
|
||||
);
|
||||
const check = checkInputs(passportNumber, dateOfBirth, dateOfExpiry);
|
||||
|
||||
if (!check.success) {
|
||||
amplitude.track('inputs_invalid', { error: check.message });
|
||||
toast.show("Unvailable", {
|
||||
toast.show('Unvailable', {
|
||||
message: check.message,
|
||||
customData: {
|
||||
type: "info",
|
||||
type: 'info',
|
||||
},
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -45,12 +39,10 @@ export const scan = async (setModalProofStep: (modalProofStep: number) => void)
|
||||
}
|
||||
};
|
||||
|
||||
const scanAndroid = async (setModalProofStep: (modalProofStep: number) => void) => {
|
||||
const {
|
||||
passportNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
} = useUserStore.getState()
|
||||
const scanAndroid = async (
|
||||
setModalProofStep: (modalProofStep: number) => void,
|
||||
) => {
|
||||
const { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore.getState();
|
||||
const { toast, setNfcSheetIsOpen } = useNavigationStore.getState();
|
||||
setNfcSheetIsOpen(true);
|
||||
|
||||
@@ -58,7 +50,7 @@ const scanAndroid = async (setModalProofStep: (modalProofStep: number) => void)
|
||||
const response = await PassportReader.scan({
|
||||
documentNumber: passportNumber,
|
||||
dateOfBirth: dateOfBirth,
|
||||
dateOfExpiry: dateOfExpiry
|
||||
dateOfExpiry: dateOfExpiry,
|
||||
});
|
||||
console.log('scanned');
|
||||
setNfcSheetIsOpen(false);
|
||||
@@ -68,32 +60,29 @@ const scanAndroid = async (setModalProofStep: (modalProofStep: number) => void)
|
||||
console.log('error during scan:', e);
|
||||
setNfcSheetIsOpen(false);
|
||||
amplitude.track('nfc_scan_unsuccessful', { error: e.message });
|
||||
if (e.message.includes("InvalidMRZKey")) {
|
||||
if (e.message.includes('InvalidMRZKey')) {
|
||||
toast.show('Error', {
|
||||
message: "Go to previous screen and rescan your passport with the camera",
|
||||
message:
|
||||
'Go to previous screen and rescan your passport with the camera',
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
timeout: 5000,
|
||||
})
|
||||
useNavigationStore.getState().setSelectedTab("scan");
|
||||
});
|
||||
useNavigationStore.getState().setSelectedTab('scan');
|
||||
} else {
|
||||
toast.show('Error', {
|
||||
message: e.message,
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
|
||||
const {
|
||||
passportNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry
|
||||
} = useUserStore.getState()
|
||||
const { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore.getState();
|
||||
const { toast } = useNavigationStore.getState();
|
||||
|
||||
console.log('passportNumber', passportNumber);
|
||||
@@ -104,7 +93,7 @@ const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
|
||||
const response = await NativeModules.PassportReader.scanPassport(
|
||||
passportNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry
|
||||
dateOfExpiry,
|
||||
);
|
||||
console.log('scanned');
|
||||
handleResponseIOS(response, setModalProofStep);
|
||||
@@ -120,63 +109,81 @@ const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
if (e.message.includes("InvalidMRZKey")) {
|
||||
if (e.message.includes('InvalidMRZKey')) {
|
||||
toast.show('Error', {
|
||||
message: "Go to previous screen and rescan your passport with the camera",
|
||||
message:
|
||||
'Go to previous screen and rescan your passport with the camera',
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
timeout: 5000,
|
||||
})
|
||||
useNavigationStore.getState().setSelectedTab("scan");
|
||||
});
|
||||
useNavigationStore.getState().setSelectedTab('scan');
|
||||
} else {
|
||||
toast.show('Error', {
|
||||
message: e.message,
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleResponseIOS = async (
|
||||
response: any,
|
||||
setModalProofStep: (modalProofStep: number) => void
|
||||
setModalProofStep: (modalProofStep: number) => void,
|
||||
) => {
|
||||
const { toast } = useNavigationStore.getState();
|
||||
|
||||
const parsed = JSON.parse(response);
|
||||
|
||||
const dgHashesObj = JSON.parse(parsed?.dataGroupHashes)
|
||||
const dg1HashString = dgHashesObj?.DG1?.sodHash
|
||||
const dg1Hash = Array.from(Buffer.from(dg1HashString, 'hex'))
|
||||
const dg2HashString = dgHashesObj?.DG2?.sodHash
|
||||
const dg2Hash = Array.from(Buffer.from(dg2HashString, 'hex'))
|
||||
const dgHashesObj = JSON.parse(parsed?.dataGroupHashes);
|
||||
const dg1HashString = dgHashesObj?.DG1?.sodHash;
|
||||
const dg1Hash = Array.from(Buffer.from(dg1HashString, 'hex'));
|
||||
const dg2HashString = dgHashesObj?.DG2?.sodHash;
|
||||
const dg2Hash = Array.from(Buffer.from(dg2HashString, 'hex'));
|
||||
|
||||
const eContentBase64 = parsed?.eContentBase64; // this is what we call concatenatedDataHashes in android world
|
||||
const signedAttributes = parsed?.signedAttributes; // this is what we call eContent in android world
|
||||
const mrz = parsed?.passportMRZ;
|
||||
const signatureBase64 = parsed?.signatureBase64;
|
||||
console.log('dataGroupsPresent', parsed?.dataGroupsPresent)
|
||||
console.log('placeOfBirth', parsed?.placeOfBirth)
|
||||
console.log('activeAuthenticationPassed', parsed?.activeAuthenticationPassed)
|
||||
console.log('isPACESupported', parsed?.isPACESupported)
|
||||
console.log('isChipAuthenticationSupported', parsed?.isChipAuthenticationSupported)
|
||||
console.log('residenceAddress', parsed?.residenceAddress)
|
||||
console.log('passportPhoto', parsed?.passportPhoto.substring(0, 100) + '...')
|
||||
console.log('encapsulatedContentDigestAlgorithm', parsed?.encapsulatedContentDigestAlgorithm)
|
||||
console.log('documentSigningCertificate', parsed?.documentSigningCertificate)
|
||||
const pem = JSON.parse(parsed?.documentSigningCertificate).PEM.replace(/\n/g, '');
|
||||
console.log('pem', pem)
|
||||
console.log('dataGroupsPresent', parsed?.dataGroupsPresent);
|
||||
console.log('placeOfBirth', parsed?.placeOfBirth);
|
||||
console.log('activeAuthenticationPassed', parsed?.activeAuthenticationPassed);
|
||||
console.log('isPACESupported', parsed?.isPACESupported);
|
||||
console.log(
|
||||
'isChipAuthenticationSupported',
|
||||
parsed?.isChipAuthenticationSupported,
|
||||
);
|
||||
console.log('residenceAddress', parsed?.residenceAddress);
|
||||
console.log('passportPhoto', parsed?.passportPhoto.substring(0, 100) + '...');
|
||||
console.log(
|
||||
'encapsulatedContentDigestAlgorithm',
|
||||
parsed?.encapsulatedContentDigestAlgorithm,
|
||||
);
|
||||
console.log('documentSigningCertificate', parsed?.documentSigningCertificate);
|
||||
const pem = JSON.parse(parsed?.documentSigningCertificate).PEM.replace(
|
||||
/\n/g,
|
||||
'',
|
||||
);
|
||||
console.log('pem', pem);
|
||||
|
||||
const eContentArray = Array.from(Buffer.from(signedAttributes, 'base64'));
|
||||
const signedEContentArray = eContentArray.map(byte => byte > 127 ? byte - 256 : byte);
|
||||
const signedEContentArray = eContentArray.map(byte =>
|
||||
byte > 127 ? byte - 256 : byte,
|
||||
);
|
||||
|
||||
const concatenatedDataHashesArray = Array.from(Buffer.from(eContentBase64, 'base64'));
|
||||
const concatenatedDataHashesArraySigned = concatenatedDataHashesArray.map(byte => byte > 127 ? byte - 256 : byte);
|
||||
const concatenatedDataHashesArray = Array.from(
|
||||
Buffer.from(eContentBase64, 'base64'),
|
||||
);
|
||||
const concatenatedDataHashesArraySigned = concatenatedDataHashesArray.map(
|
||||
byte => (byte > 127 ? byte - 256 : byte),
|
||||
);
|
||||
|
||||
const encryptedDigestArray = Array.from(Buffer.from(signatureBase64, 'base64')).map(byte => byte > 127 ? byte - 256 : byte);
|
||||
const encryptedDigestArray = Array.from(
|
||||
Buffer.from(signatureBase64, 'base64'),
|
||||
).map(byte => (byte > 127 ? byte - 256 : byte));
|
||||
|
||||
// amplitude.track('nfc_response_parsed', {
|
||||
// dataGroupsPresent: parsed?.dataGroupsPresent,
|
||||
@@ -199,30 +206,30 @@ const handleResponseIOS = async (
|
||||
eContent: concatenatedDataHashesArraySigned,
|
||||
signedAttr: signedEContentArray,
|
||||
encryptedDigest: encryptedDigestArray,
|
||||
photoBase64: "data:image/jpeg;base64," + parsed.passportPhoto,
|
||||
mockUser: false
|
||||
photoBase64: 'data:image/jpeg;base64,' + parsed.passportPhoto,
|
||||
mockUser: false,
|
||||
};
|
||||
const parsedPassportData = parsePassportData(passportData);
|
||||
amplitude.track('nfc_response_parsed', parsedPassportData);
|
||||
|
||||
try {
|
||||
useUserStore.getState().registerPassportData(passportData)
|
||||
useNavigationStore.getState().setSelectedTab("next");
|
||||
useUserStore.getState().registerPassportData(passportData);
|
||||
useNavigationStore.getState().setSelectedTab('next');
|
||||
} catch (e: any) {
|
||||
console.log('error during parsing:', e);
|
||||
amplitude.track('error_parsing_nfc_response', { error: e.message });
|
||||
toast.show('Error', {
|
||||
message: e.message,
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleResponseAndroid = async (
|
||||
response: any,
|
||||
setModalProofStep: (modalProofStep: number) => void
|
||||
setModalProofStep: (modalProofStep: number) => void,
|
||||
) => {
|
||||
const { toast } = useNavigationStore.getState();
|
||||
|
||||
@@ -238,14 +245,17 @@ const handleResponseAndroid = async (
|
||||
unicodeVersion,
|
||||
encapContent,
|
||||
documentSigningCertificate,
|
||||
dataGroupHashes
|
||||
dataGroupHashes,
|
||||
} = response;
|
||||
|
||||
const dgHashesObj = JSON.parse(dataGroupHashes);
|
||||
const dg1HashString = dgHashesObj["1"];
|
||||
const dg1HashString = dgHashesObj['1'];
|
||||
const dg1Hash = Array.from(Buffer.from(dg1HashString, 'hex'));
|
||||
const dg2Hash = dgHashesObj["2"];
|
||||
const pem = "-----BEGIN CERTIFICATE-----" + documentSigningCertificate + "-----END CERTIFICATE-----"
|
||||
const dg2Hash = dgHashesObj['2'];
|
||||
const pem =
|
||||
'-----BEGIN CERTIFICATE-----' +
|
||||
documentSigningCertificate +
|
||||
'-----END CERTIFICATE-----';
|
||||
|
||||
const dgPresents = Object.keys(dgHashesObj)
|
||||
.map(key => parseInt(key))
|
||||
@@ -262,26 +272,36 @@ const handleResponseAndroid = async (
|
||||
signedAttr: JSON.parse(eContent),
|
||||
encryptedDigest: JSON.parse(encryptedDigest),
|
||||
photoBase64: photo.base64,
|
||||
mockUser: false
|
||||
mockUser: false,
|
||||
};
|
||||
|
||||
console.log('passportData', JSON.stringify({
|
||||
...passportData,
|
||||
photoBase64: passportData.photoBase64.substring(0, 100) + '...'
|
||||
}, null, 2));
|
||||
console.log(
|
||||
'passportData',
|
||||
JSON.stringify(
|
||||
{
|
||||
...passportData,
|
||||
photoBase64: passportData.photoBase64.substring(0, 100) + '...',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
console.log('mrz', passportData?.mrz);
|
||||
console.log('dataGroupHashes', passportData?.eContent);
|
||||
console.log('eContent', passportData?.eContent);
|
||||
console.log('encryptedDigest', passportData?.encryptedDigest);
|
||||
console.log("photoBase64", passportData?.photoBase64.substring(0, 100) + '...')
|
||||
console.log("digestAlgorithm", digestAlgorithm)
|
||||
console.log("signerInfoDigestAlgorithm", signerInfoDigestAlgorithm)
|
||||
console.log("digestEncryptionAlgorithm", digestEncryptionAlgorithm)
|
||||
console.log("LDSVersion", LDSVersion)
|
||||
console.log("unicodeVersion", unicodeVersion)
|
||||
console.log("encapContent", encapContent)
|
||||
console.log("documentSigningCertificate", documentSigningCertificate)
|
||||
console.log(
|
||||
'photoBase64',
|
||||
passportData?.photoBase64.substring(0, 100) + '...',
|
||||
);
|
||||
console.log('digestAlgorithm', digestAlgorithm);
|
||||
console.log('signerInfoDigestAlgorithm', signerInfoDigestAlgorithm);
|
||||
console.log('digestEncryptionAlgorithm', digestEncryptionAlgorithm);
|
||||
console.log('LDSVersion', LDSVersion);
|
||||
console.log('unicodeVersion', unicodeVersion);
|
||||
console.log('encapContent', encapContent);
|
||||
console.log('documentSigningCertificate', documentSigningCertificate);
|
||||
|
||||
const parsedPassportData = parsePassportData(passportData);
|
||||
amplitude.track('nfc_response_parsed', parsedPassportData);
|
||||
@@ -297,16 +317,16 @@ const handleResponseAndroid = async (
|
||||
// });
|
||||
|
||||
try {
|
||||
await useUserStore.getState().registerPassportData(passportData)
|
||||
useNavigationStore.getState().setSelectedTab("next");
|
||||
await useUserStore.getState().registerPassportData(passportData);
|
||||
useNavigationStore.getState().setSelectedTab('next');
|
||||
} catch (e: any) {
|
||||
console.log('error during parsing:', e);
|
||||
amplitude.track('error_parsing_nfc_response', { error: e.message });
|
||||
toast.show('Error', {
|
||||
message: e.message,
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
import { parseProofAndroid } from './utils';
|
||||
import RNFS from 'react-native-fs';
|
||||
import * as amplitude from '@amplitude/analytics-react-native';
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
import RNFS from 'react-native-fs';
|
||||
|
||||
export const generateProof = async (
|
||||
circuit: string,
|
||||
inputs: any,
|
||||
) => {
|
||||
import { parseProofAndroid } from './utils';
|
||||
|
||||
export const generateProof = async (circuit: string, inputs: any) => {
|
||||
console.log('launching generateProof function');
|
||||
console.log('inputs in prover.ts', inputs);
|
||||
console.log('circuit', circuit);
|
||||
|
||||
const zkey_path = `${RNFS.DocumentDirectoryPath}/${circuit}.zkey`
|
||||
// Example: "/data/user/0/com.proofofpassportapp/files/register_sha256WithRSAEncryption_65537.zkey" on android
|
||||
const dat_path = `${RNFS.DocumentDirectoryPath}/${circuit}.dat`
|
||||
const zkey_path = `${RNFS.DocumentDirectoryPath}/${circuit}.zkey`;
|
||||
const dat_path = `${RNFS.DocumentDirectoryPath}/${circuit}.dat`;
|
||||
|
||||
const witness_calculator = circuit;
|
||||
|
||||
@@ -29,7 +27,7 @@ export const generateProof = async (
|
||||
zkey_path,
|
||||
witness_calculator,
|
||||
dat_path,
|
||||
inputs
|
||||
inputs,
|
||||
);
|
||||
|
||||
// console.log('local proof:', response);
|
||||
@@ -37,7 +35,7 @@ export const generateProof = async (
|
||||
if (Platform.OS === 'android') {
|
||||
const parsedResponse = parseProofAndroid(response);
|
||||
console.log('parsedResponse', parsedResponse);
|
||||
return formatProof(parsedResponse)
|
||||
return formatProof(parsedResponse);
|
||||
} else {
|
||||
const parsedResponse = JSON.parse(response);
|
||||
console.log('parsedResponse', parsedResponse);
|
||||
@@ -45,7 +43,7 @@ export const generateProof = async (
|
||||
return formatProof({
|
||||
proof: parsedResponse.proof,
|
||||
pub_signals: parsedResponse.inputs,
|
||||
})
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log('err', err);
|
||||
@@ -63,24 +61,16 @@ export const generateProof = async (
|
||||
export const formatProof = (rawProof: any): any => {
|
||||
return {
|
||||
proof: {
|
||||
pi_a: [
|
||||
rawProof.proof.a[0],
|
||||
rawProof.proof.a[1],
|
||||
"1"
|
||||
],
|
||||
pi_a: [rawProof.proof.a[0], rawProof.proof.a[1], '1'],
|
||||
pi_b: [
|
||||
[rawProof.proof.b[0][0], rawProof.proof.b[0][1]],
|
||||
[rawProof.proof.b[1][0], rawProof.proof.b[1][1]],
|
||||
["1", "0"]
|
||||
['1', '0'],
|
||||
],
|
||||
pi_c: [
|
||||
rawProof.proof.c[0],
|
||||
rawProof.proof.c[1],
|
||||
"1"
|
||||
],
|
||||
protocol: "groth16",
|
||||
curve: "bn128"
|
||||
pi_c: [rawProof.proof.c[0], rawProof.proof.c[1], '1'],
|
||||
protocol: 'groth16',
|
||||
curve: 'bn128',
|
||||
},
|
||||
publicSignals: (rawProof as any).pub_signals
|
||||
}
|
||||
}
|
||||
publicSignals: (rawProof as any).pub_signals,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,162 +1,192 @@
|
||||
import { NativeModules, Platform, Linking } from "react-native";
|
||||
// import { AppType, reconstructAppType } from "../../../common/src/utils/appType";
|
||||
import msgpack from 'msgpack-lite';
|
||||
import pako from 'pako';
|
||||
import { Linking, NativeModules, Platform } from 'react-native';
|
||||
|
||||
import { Mode, OpenPassportApp } from '../../../common/src/utils/appType';
|
||||
import {
|
||||
getCircuitNameOld,
|
||||
parseCertificateSimple,
|
||||
} from '../../../common/src/utils/certificate_parsing/parseCertificateSimple';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import useUserStore from "../stores/userStore";
|
||||
import { downloadZkey } from "./zkeyDownload";
|
||||
import msgpack from "msgpack-lite";
|
||||
import pako from "pako";
|
||||
import { Mode, OpenPassportApp } from "../../../common/src/utils/appType";
|
||||
import { getCircuitNameOld, parseCertificateSimple } from "../../../common/src/utils/certificate_parsing/parseCertificateSimple";
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { downloadZkey } from './zkeyDownload';
|
||||
|
||||
const parseUrlParams = (url: string): Map<string, string> => {
|
||||
const [, queryString] = url.split('?');
|
||||
const params = new Map<string, string>();
|
||||
if (queryString) {
|
||||
queryString.split('&').forEach(pair => {
|
||||
const [key, value] = pair.split('=');
|
||||
params.set(key, decodeURIComponent(value));
|
||||
});
|
||||
}
|
||||
return params;
|
||||
const [, queryString] = url.split('?');
|
||||
const params = new Map<string, string>();
|
||||
if (queryString) {
|
||||
queryString.split('&').forEach(pair => {
|
||||
const [key, value] = pair.split('=');
|
||||
params.set(key, decodeURIComponent(value));
|
||||
});
|
||||
}
|
||||
return params;
|
||||
};
|
||||
|
||||
export const scanQRCode = () => {
|
||||
const { toast, setSelectedApp, setSelectedTab } = useNavigationStore.getState();
|
||||
const { toast, setSelectedApp, setSelectedTab } =
|
||||
useNavigationStore.getState();
|
||||
|
||||
Linking.getInitialURL().then((url) => {
|
||||
if (url) {
|
||||
handleUniversalLink(url);
|
||||
} else {
|
||||
if (Platform.OS === 'ios') {
|
||||
console.log("Scanning QR code on iOS without Universal Link");
|
||||
Linking.getInitialURL()
|
||||
.then(url => {
|
||||
if (url) {
|
||||
handleUniversalLink(url);
|
||||
} else {
|
||||
if (Platform.OS === 'ios') {
|
||||
console.log('Scanning QR code on iOS without Universal Link');
|
||||
|
||||
const qrScanner = NativeModules.QRScannerBridge;
|
||||
if (qrScanner && qrScanner.scanQRCode) {
|
||||
qrScanner.scanQRCode()
|
||||
.then((result: string) => {
|
||||
const params = parseUrlParams(result);
|
||||
const encodedData = params.get('data');
|
||||
handleQRCodeScan(encodedData as string, toast, setSelectedApp, setSelectedTab);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('QR Scanner Error:', error);
|
||||
toast.show('Error', {
|
||||
message: 'Failed to scan QR code',
|
||||
type: 'error',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('QR Scanner module not found for iOS');
|
||||
toast.show('Error', {
|
||||
message: 'QR Scanner not available',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
} else if (Platform.OS === 'android') {
|
||||
const qrScanner = NativeModules.QRCodeScanner;
|
||||
if (qrScanner && qrScanner.scanQRCode) {
|
||||
qrScanner.scanQRCode()
|
||||
.then((result: string) => {
|
||||
const params = parseUrlParams(result);
|
||||
const encodedData = params.get('data');
|
||||
handleQRCodeScan(encodedData as string, toast, setSelectedApp, setSelectedTab);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('QR Scanner Error:', error);
|
||||
toast.show('Error', {
|
||||
message: 'Failed to scan QR code',
|
||||
type: 'error',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('QR Scanner module not found for Android');
|
||||
toast.show('Error', {
|
||||
message: 'QR Scanner not available',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
}
|
||||
const qrScanner = NativeModules.QRScannerBridge;
|
||||
if (qrScanner && qrScanner.scanQRCode) {
|
||||
qrScanner
|
||||
.scanQRCode()
|
||||
.then((result: string) => {
|
||||
const params = parseUrlParams(result);
|
||||
const encodedData = params.get('data');
|
||||
handleQRCodeScan(
|
||||
encodedData as string,
|
||||
toast,
|
||||
setSelectedApp,
|
||||
setSelectedTab,
|
||||
);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('QR Scanner Error:', error);
|
||||
toast.show('Error', {
|
||||
message: 'Failed to scan QR code',
|
||||
type: 'error',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('QR Scanner module not found for iOS');
|
||||
toast.show('Error', {
|
||||
message: 'QR Scanner not available',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
} else if (Platform.OS === 'android') {
|
||||
const qrScanner = NativeModules.QRCodeScanner;
|
||||
if (qrScanner && qrScanner.scanQRCode) {
|
||||
qrScanner
|
||||
.scanQRCode()
|
||||
.then((result: string) => {
|
||||
const params = parseUrlParams(result);
|
||||
const encodedData = params.get('data');
|
||||
handleQRCodeScan(
|
||||
encodedData as string,
|
||||
toast,
|
||||
setSelectedApp,
|
||||
setSelectedTab,
|
||||
);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('QR Scanner Error:', error);
|
||||
toast.show('Error', {
|
||||
message: 'Failed to scan QR code',
|
||||
type: 'error',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('QR Scanner module not found for Android');
|
||||
toast.show('Error', {
|
||||
message: 'QR Scanner not available',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('An error occurred while getting initial URL', err);
|
||||
toast.show('Error', {
|
||||
message: 'Failed to process initial link',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('An error occurred while getting initial URL', err);
|
||||
toast.show('Error', {
|
||||
message: 'Failed to process initial link',
|
||||
type: 'error',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleQRCodeScan = (result: string, toast: any, setSelectedApp: any, setSelectedTab: any) => {
|
||||
try {
|
||||
const dsc = useUserStore.getState().passportData?.dsc;
|
||||
if (dsc) {
|
||||
const handleQRCodeScan = (
|
||||
result: string,
|
||||
toast: any,
|
||||
setSelectedApp: any,
|
||||
setSelectedTab: any,
|
||||
) => {
|
||||
try {
|
||||
const dsc = useUserStore.getState().passportData?.dsc;
|
||||
if (dsc) {
|
||||
const decodedResult = atob(result);
|
||||
const uint8Array = new Uint8Array(
|
||||
decodedResult.split('').map(char => char.charCodeAt(0)),
|
||||
);
|
||||
const decompressedData = pako.inflate(uint8Array);
|
||||
const unpackedData = msgpack.decode(decompressedData);
|
||||
const openPassportApp: OpenPassportApp = unpackedData;
|
||||
setSelectedApp(openPassportApp);
|
||||
|
||||
const decodedResult = atob(result);
|
||||
const uint8Array = new Uint8Array(decodedResult.split('').map(char => char.charCodeAt(0)));
|
||||
const decompressedData = pako.inflate(uint8Array);
|
||||
const unpackedData = msgpack.decode(decompressedData);
|
||||
const openPassportApp: OpenPassportApp = unpackedData;
|
||||
setSelectedApp(openPassportApp);
|
||||
const parsedDsc = parseCertificateSimple(dsc);
|
||||
|
||||
const parsedDsc = parseCertificateSimple(dsc);
|
||||
const circuitName =
|
||||
openPassportApp.mode === 'vc_and_disclose'
|
||||
? 'vc_and_disclose'
|
||||
: getCircuitNameOld(
|
||||
'prove' as Mode,
|
||||
parsedDsc.signatureAlgorithm,
|
||||
parsedDsc.hashAlgorithm,
|
||||
);
|
||||
downloadZkey(circuitName as any);
|
||||
|
||||
const circuitName = openPassportApp.mode === 'vc_and_disclose'
|
||||
? 'vc_and_disclose'
|
||||
: getCircuitNameOld("prove" as Mode, parsedDsc.signatureAlgorithm, parsedDsc.hashAlgorithm);
|
||||
downloadZkey(circuitName as any);
|
||||
|
||||
setSelectedTab("prove");
|
||||
toast.show('✅', {
|
||||
message: "QR code scanned",
|
||||
customData: {
|
||||
type: "success",
|
||||
},
|
||||
});
|
||||
}
|
||||
else {
|
||||
toast.show('Welcome', {
|
||||
message: 'Please register your passport first',
|
||||
type: 'info',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing QR code result:', error);
|
||||
toast.show('Try again', {
|
||||
message: "Error reading QR code: " + (error as Error).message,
|
||||
customData: {
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
setSelectedTab('prove');
|
||||
toast.show('✅', {
|
||||
message: 'QR code scanned',
|
||||
customData: {
|
||||
type: 'success',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
toast.show('Welcome', {
|
||||
message: 'Please register your passport first',
|
||||
type: 'info',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing QR code result:', error);
|
||||
toast.show('Try again', {
|
||||
message: 'Error reading QR code: ' + (error as Error).message,
|
||||
customData: {
|
||||
type: 'error',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUniversalLink = (url: string) => {
|
||||
const { toast, setSelectedApp, setSelectedTab } = useNavigationStore.getState();
|
||||
const params = parseUrlParams(url);
|
||||
const encodedData = params.get('data');
|
||||
console.log("Encoded data:", encodedData);
|
||||
if (encodedData) {
|
||||
handleQRCodeScan(encodedData, toast, setSelectedApp, setSelectedTab);
|
||||
} else {
|
||||
console.error('No data found in the Universal Link');
|
||||
toast.show('Error', {
|
||||
message: 'Invalid link',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
const { toast, setSelectedApp, setSelectedTab } =
|
||||
useNavigationStore.getState();
|
||||
const params = parseUrlParams(url);
|
||||
const encodedData = params.get('data');
|
||||
console.log('Encoded data:', encodedData);
|
||||
if (encodedData) {
|
||||
handleQRCodeScan(encodedData, toast, setSelectedApp, setSelectedTab);
|
||||
} else {
|
||||
console.error('No data found in the Universal Link');
|
||||
toast.show('Error', {
|
||||
message: 'Invalid link',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const setupUniversalLinkListener = () => {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
if (url) {
|
||||
handleUniversalLink(url);
|
||||
}
|
||||
});
|
||||
Linking.getInitialURL().then(url => {
|
||||
if (url) {
|
||||
handleUniversalLink(url);
|
||||
}
|
||||
});
|
||||
|
||||
const linkingEventListener = Linking.addEventListener('url', ({ url }) => {
|
||||
handleUniversalLink(url);
|
||||
});
|
||||
const linkingEventListener = Linking.addEventListener('url', ({ url }) => {
|
||||
handleUniversalLink(url);
|
||||
});
|
||||
|
||||
return () => {
|
||||
linkingEventListener.remove();
|
||||
};
|
||||
return () => {
|
||||
linkingEventListener.remove();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import axios from "axios";
|
||||
import { COMMITMENT_TREE_TRACKER_URL, PASSPORT_ATTESTATION_ID } from "../../../common/src/constants/constants";
|
||||
import { LeanIMT, LeanIMTHashFunction } from "@openpassport/zk-kit-lean-imt";
|
||||
import { poseidon2, poseidon6 } from "poseidon-lite";
|
||||
import { PassportData } from "../../../common/src/utils/types";
|
||||
import { generateCommitment, getLeaf } from "../../../common/src/utils/pubkeyTree";
|
||||
import { formatMrz, packBytes } from "../../../common/src/utils/utils";
|
||||
import { findIndexInTree } from "../../../common/src/utils/generateInputs";
|
||||
|
||||
export async function isCommitmentRegistered(secret: string, passportData: PassportData) {
|
||||
import { LeanIMT, LeanIMTHashFunction } from '@openpassport/zk-kit-lean-imt';
|
||||
import axios from 'axios';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
|
||||
import {
|
||||
COMMITMENT_TREE_TRACKER_URL,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import { findIndexInTree } from '../../../common/src/utils/generateInputs';
|
||||
import {
|
||||
generateCommitment,
|
||||
getLeaf,
|
||||
} from '../../../common/src/utils/pubkeyTree';
|
||||
import { PassportData } from '../../../common/src/utils/types';
|
||||
import { formatMrz, packBytes } from '../../../common/src/utils/utils';
|
||||
|
||||
export async function isCommitmentRegistered(
|
||||
secret: string,
|
||||
passportData: PassportData,
|
||||
) {
|
||||
let response;
|
||||
console.log(COMMITMENT_TREE_TRACKER_URL)
|
||||
|
||||
console.log(COMMITMENT_TREE_TRACKER_URL);
|
||||
try {
|
||||
response = await axios.get(COMMITMENT_TREE_TRACKER_URL);
|
||||
} catch (error) {
|
||||
@@ -20,21 +29,28 @@ export async function isCommitmentRegistered(secret: string, passportData: Passp
|
||||
}
|
||||
console.log('response.data:', response.data);
|
||||
|
||||
const hashFunction: LeanIMTHashFunction = (a: bigint, b: bigint) => poseidon2([a, b]);
|
||||
const hashFunction: LeanIMTHashFunction = (a: bigint, b: bigint) =>
|
||||
poseidon2([a, b]);
|
||||
const imt = LeanIMT.import(hashFunction, response.data);
|
||||
|
||||
const pubkey_leaf = getLeaf(passportData.dsc);
|
||||
|
||||
const formattedMrz = formatMrz(passportData.mrz);
|
||||
const mrz_bytes = packBytes(formattedMrz);
|
||||
const commitment = generateCommitment(secret, PASSPORT_ATTESTATION_ID, pubkey_leaf, mrz_bytes, passportData.dg2Hash?.map((x) => x.toString()) || []);
|
||||
const commitment = generateCommitment(
|
||||
secret,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
pubkey_leaf,
|
||||
mrz_bytes,
|
||||
passportData.dg2Hash?.map(x => x.toString()) || [],
|
||||
);
|
||||
|
||||
console.log('commitment', commitment.toString());
|
||||
|
||||
try {
|
||||
findIndexInTree(imt as any, commitment); // this will throw if not found
|
||||
return true
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
// utils from snarkjs copied here before snarkjs imports node crypto
|
||||
|
||||
function unstringifyBigInts(o: any): any {
|
||||
if (typeof o == "string" && /^[0-9]+$/.test(o)) {
|
||||
return BigInt(o);
|
||||
} else if (typeof o == "string" && /^0x[0-9a-fA-F]+$/.test(o)) {
|
||||
return BigInt(o);
|
||||
if (typeof o === 'string' && /^[0-9]+$/.test(o)) {
|
||||
return BigInt(o);
|
||||
} else if (typeof o === 'string' && /^0x[0-9a-fA-F]+$/.test(o)) {
|
||||
return BigInt(o);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(unstringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
if (o === null) return null;
|
||||
const res: any = {};
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach((k) => {
|
||||
res[k] = unstringifyBigInts(o[k]);
|
||||
});
|
||||
return res;
|
||||
return o.map(unstringifyBigInts);
|
||||
} else if (typeof o === 'object') {
|
||||
if (o === null) {
|
||||
return null;
|
||||
}
|
||||
const res: any = {};
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach(k => {
|
||||
res[k] = unstringifyBigInts(o[k]);
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
function p256(n: any) {
|
||||
let nstr = n.toString(16);
|
||||
while (nstr.length < 64) nstr = "0"+nstr;
|
||||
while (nstr.length < 64) {
|
||||
nstr = '0' + nstr;
|
||||
}
|
||||
nstr = `"0x${nstr}"`;
|
||||
return nstr;
|
||||
}
|
||||
@@ -31,16 +35,21 @@ export default function groth16ExportSolidityCallData(_proof: any, _pub: any) {
|
||||
const proof = unstringifyBigInts(_proof);
|
||||
const pub = unstringifyBigInts(_pub);
|
||||
|
||||
let inputs = "";
|
||||
for (let i=0; i<pub.length; i++) {
|
||||
if (inputs != "") inputs = inputs + ",";
|
||||
inputs = inputs + p256(pub[i]);
|
||||
let inputs = '';
|
||||
for (let i = 0; i < pub.length; i++) {
|
||||
if (inputs !== '') {
|
||||
inputs = inputs + ',';
|
||||
}
|
||||
inputs = inputs + p256(pub[i]);
|
||||
}
|
||||
|
||||
const S =`[${p256(proof.a[0])}, ${p256(proof.a[1])}],` +
|
||||
`[[${p256(proof.b[0][1])}, ${p256(proof.b[0][0])}],[${p256(proof.b[1][1])}, ${p256(proof.b[1][0])}]],` +
|
||||
const S =
|
||||
`[${p256(proof.a[0])}, ${p256(proof.a[1])}],` +
|
||||
`[[${p256(proof.b[0][1])}, ${p256(proof.b[0][0])}],[${p256(
|
||||
proof.b[1][1],
|
||||
)}, ${p256(proof.b[1][0])}]],` +
|
||||
`[${p256(proof.c[0])}, ${p256(proof.c[1])}],` +
|
||||
`[${inputs}]`;
|
||||
|
||||
return S;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
import { ethers } from "ethers";
|
||||
import axios from 'axios';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
import {
|
||||
CHAIN_NAME,
|
||||
RELAYER_URL,
|
||||
RPC_URL,
|
||||
SignatureAlgorithmIndex,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import {
|
||||
formatCallData_disclose,
|
||||
formatCallData_dsc,
|
||||
formatCallData_register,
|
||||
} from '../../../common/src/utils/formatCallData';
|
||||
import { Proof } from '../../../common/src/utils/types';
|
||||
import registerArtefacts from '../../deployments/artifacts/Deploy_Registry#OpenPassportRegister.json';
|
||||
import sbtArtefacts from '../../deployments/artifacts/Deploy_Registry#SBT.json';
|
||||
import contractAddresses from '../../deployments/deployed_addresses.json';
|
||||
import groth16ExportSolidityCallData from './snarkjs';
|
||||
import contractAddresses from "../../deployments/deployed_addresses.json";
|
||||
import registerArtefacts from "../../deployments/artifacts/Deploy_Registry#OpenPassportRegister.json";
|
||||
import sbtArtefacts from "../../deployments/artifacts/Deploy_Registry#SBT.json";
|
||||
import { CHAIN_NAME, RELAYER_URL, RPC_URL, SignatureAlgorithmIndex } from '../../../common/src/constants/constants';
|
||||
import { Proof } from "../../../common/src/utils/types";
|
||||
import { formatCallData_disclose, formatCallData_dsc, formatCallData_register } from "../../../common/src/utils/formatCallData";
|
||||
|
||||
export const sendRegisterTransaction = async (
|
||||
proof: Proof,
|
||||
cscaProof: Proof,
|
||||
sigAlgIndex: SignatureAlgorithmIndex
|
||||
sigAlgIndex: SignatureAlgorithmIndex,
|
||||
) => {
|
||||
const provider = new ethers.JsonRpcProvider(RPC_URL);
|
||||
|
||||
if (!contractAddresses["Deploy_Registry#OpenPassportRegister"] || !registerArtefacts.abi) {
|
||||
if (
|
||||
!contractAddresses['Deploy_Registry#OpenPassportRegister'] ||
|
||||
!registerArtefacts.abi
|
||||
) {
|
||||
console.log('contracts addresses or abi not found');
|
||||
return;
|
||||
}
|
||||
@@ -25,11 +38,14 @@ export const sendRegisterTransaction = async (
|
||||
const cd = groth16ExportSolidityCallData(proof.proof, proof.pub_signals);
|
||||
const callData = JSON.parse(`[${cd}]`);
|
||||
//console.log('callData', callData);
|
||||
const formattedCallData_register = formatCallData_register(callData)
|
||||
const formattedCallData_register = formatCallData_register(callData);
|
||||
console.log('formattedCallData_register', formattedCallData_register);
|
||||
|
||||
//console.log("exporting csca proof", cscaProof, cscaProof.proof, cscaProof.pub_signals)
|
||||
const cd_csca = groth16ExportSolidityCallData(cscaProof.proof, cscaProof.pub_signals);
|
||||
const cd_csca = groth16ExportSolidityCallData(
|
||||
cscaProof.proof,
|
||||
cscaProof.pub_signals,
|
||||
);
|
||||
const callData_csca = JSON.parse(`[${cd_csca}]`);
|
||||
//console.log('callData_csca', callData_csca);
|
||||
const formattedCallData_csca = formatCallData_dsc(callData_csca);
|
||||
@@ -37,18 +53,23 @@ export const sendRegisterTransaction = async (
|
||||
|
||||
try {
|
||||
const registerContract = new ethers.Contract(
|
||||
contractAddresses["Deploy_Registry#OpenPassportRegister"],
|
||||
contractAddresses['Deploy_Registry#OpenPassportRegister'],
|
||||
registerArtefacts.abi,
|
||||
provider
|
||||
provider,
|
||||
);
|
||||
|
||||
const transactionRequest = await registerContract
|
||||
.validateProof.populateTransaction(formattedCallData_register, formattedCallData_csca, sigAlgIndex, sigAlgIndex);
|
||||
const transactionRequest =
|
||||
await registerContract.validateProof.populateTransaction(
|
||||
formattedCallData_register,
|
||||
formattedCallData_csca,
|
||||
sigAlgIndex,
|
||||
sigAlgIndex,
|
||||
);
|
||||
console.log('transactionRequest', transactionRequest);
|
||||
|
||||
const response = await axios.post(RELAYER_URL, {
|
||||
chain: CHAIN_NAME,
|
||||
tx_data: transactionRequest
|
||||
tx_data: transactionRequest,
|
||||
});
|
||||
console.log('response status', response.status);
|
||||
console.log('response data', response.data);
|
||||
@@ -71,12 +92,10 @@ export const sendRegisterTransaction = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const mintSBT = async (
|
||||
proof: Proof,
|
||||
) => {
|
||||
export const mintSBT = async (proof: Proof) => {
|
||||
const provider = new ethers.JsonRpcProvider(RPC_URL);
|
||||
|
||||
if (!contractAddresses["Deploy_Registry#SBT"] || !sbtArtefacts.abi) {
|
||||
if (!contractAddresses['Deploy_Registry#SBT'] || !sbtArtefacts.abi) {
|
||||
console.log('contracts addresses or abi not found');
|
||||
return;
|
||||
}
|
||||
@@ -86,22 +105,26 @@ export const mintSBT = async (
|
||||
const parsedCallData_disclose = JSON.parse(`[${cd}]`);
|
||||
console.log('parsedCallData_disclose', parsedCallData_disclose);
|
||||
|
||||
const formattedCallData_disclose = formatCallData_disclose(parsedCallData_disclose);
|
||||
const formattedCallData_disclose = formatCallData_disclose(
|
||||
parsedCallData_disclose,
|
||||
);
|
||||
|
||||
try {
|
||||
const proofOfPassportContract = new ethers.Contract(
|
||||
contractAddresses["Deploy_Registry#SBT"],
|
||||
contractAddresses['Deploy_Registry#SBT'],
|
||||
sbtArtefacts.abi,
|
||||
provider
|
||||
provider,
|
||||
);
|
||||
|
||||
const transactionRequest = await proofOfPassportContract
|
||||
.mint.populateTransaction(formattedCallData_disclose);
|
||||
const transactionRequest =
|
||||
await proofOfPassportContract.mint.populateTransaction(
|
||||
formattedCallData_disclose,
|
||||
);
|
||||
console.log('transactionRequest', transactionRequest);
|
||||
|
||||
const response = await axios.post(RELAYER_URL, {
|
||||
chain: CHAIN_NAME,
|
||||
tx_data: transactionRequest
|
||||
tx_data: transactionRequest,
|
||||
});
|
||||
console.log('response status', response.status);
|
||||
console.log('response data', response.data);
|
||||
@@ -122,4 +145,4 @@ export const mintSBT = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Function to extract information from a two-line MRZ.
|
||||
|
||||
import { countryCodes } from "../../../common/src/constants/constants";
|
||||
import { Proof } from "../../../common/src/utils/types";
|
||||
import { getCountryCodeFromMrz } from "./parsePassportData";
|
||||
import { countryCodes } from '../../../common/src/constants/constants';
|
||||
import { Proof } from '../../../common/src/utils/types';
|
||||
|
||||
// The actual parsing would depend on the standard being used (TD1, TD2, TD3, MRVA, MRVB).
|
||||
export function extractMRZInfo(mrzString: string) {
|
||||
@@ -34,11 +33,10 @@ export function formatDateToYYMMDD(inputDate: string) {
|
||||
return year + month + day;
|
||||
}
|
||||
|
||||
|
||||
export const ModalProofSteps = {
|
||||
MODAL_REQUEST_SENT: 1,
|
||||
MODAL_SERVER_ERROR: 2,
|
||||
MODAL_SERVER_SUCCESS: 3
|
||||
MODAL_SERVER_SUCCESS: 3,
|
||||
};
|
||||
|
||||
export function formatAttribute(key: string, attribute: string) {
|
||||
@@ -48,9 +46,8 @@ export function formatAttribute(key: string, attribute: string) {
|
||||
const day = attribute.substring(4, 6);
|
||||
return `${year}-${month}-${day}`; // ISO 8601 format (YYYY-MM-DD)
|
||||
} else if (key === 'nationality' && attribute in countryCodes) {
|
||||
return countryCodes[attribute as keyof typeof countryCodes]
|
||||
}
|
||||
else if (key === 'date_of_birth') {
|
||||
return countryCodes[attribute as keyof typeof countryCodes];
|
||||
} else if (key === 'date_of_birth') {
|
||||
let year = '19' + attribute.substring(0, 2);
|
||||
const currentYear = 2024;
|
||||
const birthYear = parseInt(year);
|
||||
@@ -65,9 +62,13 @@ export function formatAttribute(key: string, attribute: string) {
|
||||
}
|
||||
|
||||
export const parseProofAndroid = (response: string) => {
|
||||
const match = response.match(/ZkProof\(proof=Proof\(pi_a=\[(.*?)\], pi_b=\[\[(.*?)\], \[(.*?)\], \[1, 0\]\], pi_c=\[(.*?)\], protocol=groth16, curve=bn128\), pub_signals=\[(.*?)\]\)/);
|
||||
const match = response.match(
|
||||
/ZkProof\(proof=Proof\(pi_a=\[(.*?)\], pi_b=\[\[(.*?)\], \[(.*?)\], \[1, 0\]\], pi_c=\[(.*?)\], protocol=groth16, curve=bn128\), pub_signals=\[(.*?)\]\)/,
|
||||
);
|
||||
|
||||
if (!match) throw new Error('Invalid input format');
|
||||
if (!match) {
|
||||
throw new Error('Invalid input format');
|
||||
}
|
||||
|
||||
const [, pi_a, pi_b_1, pi_b_2, pi_c, pub_signals] = match;
|
||||
|
||||
@@ -80,41 +81,41 @@ export const parseProofAndroid = (response: string) => {
|
||||
],
|
||||
c: pi_c.split(',').map((n: string) => n.trim()),
|
||||
},
|
||||
pub_signals: pub_signals.split(',').map((n: string) => n.trim())
|
||||
pub_signals: pub_signals.split(',').map((n: string) => n.trim()),
|
||||
} as Proof;
|
||||
};
|
||||
|
||||
export function getFirstName(mrz: string): string {
|
||||
const names = mrz.split("<<");
|
||||
const firstName = names[1].split("<")[0].trim();
|
||||
const names = mrz.split('<<');
|
||||
const firstName = names[1].split('<')[0].trim();
|
||||
const capitalized = firstName.charAt(0) + firstName.slice(1).toLowerCase();
|
||||
return capitalized || "Unknown";
|
||||
return capitalized || 'Unknown';
|
||||
}
|
||||
|
||||
export function checkInputs(
|
||||
passportNumber: string,
|
||||
dateOfBirth: string,
|
||||
dateOfExpiry: string,
|
||||
): { success: boolean, message: string } {
|
||||
): { success: boolean; message: string } {
|
||||
// if (passportNumber.length !== 9) {
|
||||
// throw new Error('Passport number must be 9 characters long');
|
||||
// }
|
||||
if (dateOfBirth.length !== 6) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Complete Step 1 first'
|
||||
message: 'Complete Step 1 first',
|
||||
};
|
||||
}
|
||||
if (dateOfExpiry.length !== 6) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Date of expiry must be 6 characters long'
|
||||
message: 'Date of expiry must be 6 characters long',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: ''
|
||||
message: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,4 +125,4 @@ export const maskString = (input: string): string => {
|
||||
} else {
|
||||
return input.charAt(0) + input.charAt(1) + '*'.repeat(input.length - 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,32 +6,40 @@ import { unzip } from 'react-native-zip-archive';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
|
||||
const zkeyZipUrls = {
|
||||
prove_rsa_65537_sha256: "https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha256.zkey.zip",
|
||||
prove_rsa_65537_sha1: "https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha1.zkey.zip",
|
||||
prove_rsapss_65537_sha256: "https://d8o9bercqupgk.cloudfront.net/staging/prove_rsapss_65537_sha256.zkey.zip",
|
||||
prove_rsa_65537_sha256:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha256.zkey.zip',
|
||||
prove_rsa_65537_sha1:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha1.zkey.zip',
|
||||
prove_rsapss_65537_sha256:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/prove_rsapss_65537_sha256.zkey.zip',
|
||||
// register_sha256WithRSAEncryption_65537: "https://d8o9bercqupgk.cloudfront.net/staging/register_sha256WithRSAEncryption_65537_csca2.zkey.zip",
|
||||
// disclose: "https://d8o9bercqupgk.cloudfront.net/staging/disclose3.zkey.zip",
|
||||
vc_and_disclose: "https://d8o9bercqupgk.cloudfront.net/staging/vc_and_disclose.zkey.zip",
|
||||
vc_and_disclose:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/vc_and_disclose.zkey.zip',
|
||||
};
|
||||
|
||||
const datZipUrls = {
|
||||
prove_rsa_65537_sha256: "https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha256.dat.zip",
|
||||
prove_rsa_65537_sha1: "https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha1.dat.zip",
|
||||
prove_rsapss_65537_sha256: "https://d8o9bercqupgk.cloudfront.net/staging/prove_rsapss_65537_sha256.dat.zip",
|
||||
vc_and_disclose: "https://d8o9bercqupgk.cloudfront.net/staging/vc_and_disclose.dat.zip",
|
||||
prove_rsa_65537_sha256:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha256.dat.zip',
|
||||
prove_rsa_65537_sha1:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/prove_rsa_65537_sha1.dat.zip',
|
||||
prove_rsapss_65537_sha256:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/prove_rsapss_65537_sha256.dat.zip',
|
||||
vc_and_disclose:
|
||||
'https://d8o9bercqupgk.cloudfront.net/staging/vc_and_disclose.dat.zip',
|
||||
};
|
||||
|
||||
export type CircuitName = keyof typeof zkeyZipUrls;
|
||||
|
||||
export type ShowWarningModalProps = {
|
||||
show: boolean,
|
||||
circuit: CircuitName | "",
|
||||
size: number,
|
||||
}
|
||||
show: boolean;
|
||||
circuit: CircuitName | '';
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type IsZkeyDownloading = {
|
||||
[circuit in CircuitName]: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
// each time we download a zkey, we store the size of the zip file in a file named <circuit_name>_zip_size.txt
|
||||
// we assume a new zkey zip will always have a different size
|
||||
@@ -43,50 +51,51 @@ export type IsZkeyDownloading = {
|
||||
// 4. the commitment is already registered and the function is called for a register zkey. If it's the case, there is no need to download the latest zkey.
|
||||
// => this should be fine is the function is never called after the commitment is registered.
|
||||
|
||||
export async function downloadZkey(
|
||||
circuit: CircuitName,
|
||||
) {
|
||||
const {
|
||||
isZkeyDownloading,
|
||||
update
|
||||
} = useNavigationStore.getState();
|
||||
export async function downloadZkey(circuit: CircuitName) {
|
||||
const { isZkeyDownloading, update } = useNavigationStore.getState();
|
||||
|
||||
const downloadRequired = await isDownloadRequired(circuit, isZkeyDownloading);
|
||||
if (!downloadRequired) {
|
||||
console.log(`zkey and dat for ${circuit} already downloaded`)
|
||||
console.log(`zkey and dat for ${circuit} already downloaded`);
|
||||
amplitude.track('zkey_already_downloaded', { circuit: circuit });
|
||||
return;
|
||||
}
|
||||
|
||||
const networkInfo = await NetInfo.fetch();
|
||||
console.log('Network type:', networkInfo.type)
|
||||
console.log('Network type:', networkInfo.type);
|
||||
if (networkInfo.type === 'wifi') {
|
||||
fetchZkeyAndDat(circuit);
|
||||
} else {
|
||||
const zkeyResponse = await axios.head(zkeyZipUrls[circuit]);
|
||||
const datResponse = await axios.head(datZipUrls[circuit]);
|
||||
const expectedSize = parseInt(zkeyResponse.headers['content-length'], 10) + parseInt(datResponse.headers['content-length'], 10);
|
||||
const expectedSize =
|
||||
parseInt(zkeyResponse.headers['content-length'], 10) +
|
||||
parseInt(datResponse.headers['content-length'], 10);
|
||||
|
||||
update({
|
||||
showWarningModal: {
|
||||
show: true,
|
||||
circuit: circuit,
|
||||
size: expectedSize,
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function isDownloadRequired(
|
||||
circuit: CircuitName,
|
||||
isZkeyDownloading: IsZkeyDownloading
|
||||
isZkeyDownloading: IsZkeyDownloading,
|
||||
) {
|
||||
if (isZkeyDownloading[circuit]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const zkeyExists = await RNFS.exists(`${RNFS.DocumentDirectoryPath}/${circuit}.zkey`);
|
||||
const datExists = await RNFS.exists(`${RNFS.DocumentDirectoryPath}/${circuit}.dat`);
|
||||
const zkeyExists = await RNFS.exists(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}.zkey`,
|
||||
);
|
||||
const datExists = await RNFS.exists(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}.dat`,
|
||||
);
|
||||
if (!zkeyExists || !datExists) {
|
||||
return true;
|
||||
}
|
||||
@@ -94,10 +103,22 @@ export async function isDownloadRequired(
|
||||
let storedZkeyZipSize = 0;
|
||||
let storedDatZipSize = 0;
|
||||
try {
|
||||
storedZkeyZipSize = Number(await RNFS.readFile(`${RNFS.DocumentDirectoryPath}/${circuit}_zkey_zip_size.txt`, 'utf8'));
|
||||
storedDatZipSize = Number(await RNFS.readFile(`${RNFS.DocumentDirectoryPath}/${circuit}_dat_zip_size.txt`, 'utf8'));
|
||||
storedZkeyZipSize = Number(
|
||||
await RNFS.readFile(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}_zkey_zip_size.txt`,
|
||||
'utf8',
|
||||
),
|
||||
);
|
||||
storedDatZipSize = Number(
|
||||
await RNFS.readFile(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}_dat_zip_size.txt`,
|
||||
'utf8',
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(`Size files not found for ${circuit}, assuming files are outdated.`);
|
||||
console.log(
|
||||
`Size files not found for ${circuit}, assuming files are outdated.`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,27 +127,24 @@ export async function isDownloadRequired(
|
||||
const expectedZkeySize = parseInt(zkeyResponse.headers['content-length'], 10);
|
||||
const expectedDatSize = parseInt(datResponse.headers['content-length'], 10);
|
||||
|
||||
return storedZkeyZipSize !== expectedZkeySize || storedDatZipSize !== expectedDatSize;
|
||||
return (
|
||||
storedZkeyZipSize !== expectedZkeySize ||
|
||||
storedDatZipSize !== expectedDatSize
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchZkeyAndDat(
|
||||
circuit: CircuitName,
|
||||
) {
|
||||
console.log(`fetching zkey and dat for ${circuit} ...`)
|
||||
export async function fetchZkeyAndDat(circuit: CircuitName) {
|
||||
console.log(`fetching zkey and dat for ${circuit} ...`);
|
||||
amplitude.track('fetching_zkey_and_dat', { circuit: circuit });
|
||||
|
||||
const {
|
||||
isZkeyDownloading,
|
||||
toast,
|
||||
update,
|
||||
setZkeyDownloadedPercentage
|
||||
} = useNavigationStore.getState();
|
||||
const { isZkeyDownloading, toast, update, setZkeyDownloadedPercentage } =
|
||||
useNavigationStore.getState();
|
||||
|
||||
update({
|
||||
isZkeyDownloading: {
|
||||
...isZkeyDownloading,
|
||||
[circuit]: true,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const startTime = Date.now();
|
||||
@@ -142,22 +160,26 @@ export async function fetchZkeyAndDat(
|
||||
},
|
||||
progress: (res: any) => {
|
||||
if (fileName.endsWith('.zkey.zip')) {
|
||||
const percentComplete = Math.floor((res.bytesWritten / res.contentLength) * 100);
|
||||
if (percentComplete % 5 === 0 && percentComplete !== previousPercentComplete) {
|
||||
const percentComplete = Math.floor(
|
||||
(res.bytesWritten / res.contentLength) * 100,
|
||||
);
|
||||
if (
|
||||
percentComplete % 5 === 0 &&
|
||||
percentComplete !== previousPercentComplete
|
||||
) {
|
||||
console.log(`${percentComplete}%`);
|
||||
previousPercentComplete = percentComplete;
|
||||
setZkeyDownloadedPercentage(percentComplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
await RNFS.downloadFile(options).promise;
|
||||
console.log(`Download of ${fileName} complete`);
|
||||
RNFS.readDir(RNFS.DocumentDirectoryPath)
|
||||
.then((result) => {
|
||||
console.log('Directory contents before unzipping:', result);
|
||||
})
|
||||
RNFS.readDir(RNFS.DocumentDirectoryPath).then(result => {
|
||||
console.log('Directory contents before unzipping:', result);
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -171,10 +193,14 @@ export async function fetchZkeyAndDat(
|
||||
isZkeyDownloading: {
|
||||
...isZkeyDownloading,
|
||||
[circuit]: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
console.log('zkey and dat download succeeded, took ' + ((Date.now() - startTime) / 1000) + ' seconds');
|
||||
console.log(
|
||||
'zkey and dat download succeeded, took ' +
|
||||
(Date.now() - startTime) / 1000 +
|
||||
' seconds',
|
||||
);
|
||||
|
||||
await saveFileSize(circuit, 'zkey');
|
||||
await saveFileSize(circuit, 'dat');
|
||||
@@ -185,19 +211,18 @@ export async function fetchZkeyAndDat(
|
||||
|
||||
const result = await RNFS.readDir(RNFS.DocumentDirectoryPath);
|
||||
console.log('Directory contents at the end:', result);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
update({
|
||||
isZkeyDownloading: {
|
||||
...isZkeyDownloading,
|
||||
[circuit]: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
toast.show('Error', {
|
||||
message: `Error: ${error.message}`,
|
||||
customData: {
|
||||
type: "error",
|
||||
type: 'error',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -205,7 +230,10 @@ export async function fetchZkeyAndDat(
|
||||
|
||||
async function unzipFile(circuit: CircuitName, fileType: 'zkey' | 'dat') {
|
||||
const unzipPath = `${RNFS.DocumentDirectoryPath}/${circuit}_temp`;
|
||||
await unzip(`${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}.zip`, unzipPath);
|
||||
await unzip(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}.zip`,
|
||||
unzipPath,
|
||||
);
|
||||
const files = await RNFS.readDir(unzipPath);
|
||||
const targetFile = files.find(file => file.name.endsWith(`.${fileType}`));
|
||||
if (targetFile) {
|
||||
@@ -217,13 +245,21 @@ async function unzipFile(circuit: CircuitName, fileType: 'zkey' | 'dat') {
|
||||
await RNFS.moveFile(targetFile.path, destinationPath);
|
||||
console.log(`File moved to ${circuit}.${fileType}`);
|
||||
} else {
|
||||
throw new Error(`${fileType.toUpperCase()} file not found in the unzipped directory`);
|
||||
throw new Error(
|
||||
`${fileType.toUpperCase()} file not found in the unzipped directory`,
|
||||
);
|
||||
}
|
||||
await RNFS.unlink(unzipPath);
|
||||
}
|
||||
|
||||
async function saveFileSize(circuit: CircuitName, fileType: 'zkey' | 'dat') {
|
||||
const zipSize = await RNFS.stat(`${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}.zip`);
|
||||
await RNFS.writeFile(`${RNFS.DocumentDirectoryPath}/${circuit}_${fileType}_zip_size.txt`, zipSize.size.toString(), 'utf8');
|
||||
const zipSize = await RNFS.stat(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}.zip`,
|
||||
);
|
||||
await RNFS.writeFile(
|
||||
`${RNFS.DocumentDirectoryPath}/${circuit}_${fileType}_zip_size.txt`,
|
||||
zipSize.size.toString(),
|
||||
'utf8',
|
||||
);
|
||||
console.log(`${fileType} zip size written to file`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { config } from '@tamagui/config/v3'
|
||||
import { createTamagui } from 'tamagui' // or '@tamagui/core'
|
||||
import { config } from '@tamagui/config/v3';
|
||||
import { createTamagui } from 'tamagui'; // or '@tamagui/core'
|
||||
|
||||
const appConfig = createTamagui(config)
|
||||
const appConfig = createTamagui(config);
|
||||
|
||||
export type AppConfig = typeof appConfig
|
||||
export type AppConfig = typeof appConfig;
|
||||
|
||||
declare module 'tamagui' {
|
||||
// or '@tamagui/core'
|
||||
// overrides TamaguiCustomConfig so your custom types
|
||||
// work everywhere you import `tamagui`
|
||||
interface TamaguiCustomConfig extends AppConfig { }
|
||||
interface TamaguiCustomConfig extends AppConfig {}
|
||||
}
|
||||
|
||||
export default appConfig
|
||||
export default appConfig;
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
{
|
||||
"extends": "@react-native/typescript-config/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext"
|
||||
]
|
||||
"lib": ["dom", "esnext"]
|
||||
},
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./src/types"
|
||||
]
|
||||
}
|
||||
"typeRoots": ["./node_modules/@types", "./src/types"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user