add common sdk (#537)

* add common sdk

* remove sdk backend api

* remove registry

* regenerate sha256 rsa dsc each time

* download ski-pem dynamically on staging, refactor initpassportDataParsing

* add state machine for button on prove screen, improve ux on splash screen

* fetch ski-pem in production

* fix linter issues

* fix prove screen button bugs

* update podfile.lock and yarn.lock

* run linter in circuits repo

* bump build

* bump version for sentry debugging

* bump ios to version 118

---------

Co-authored-by: Justin Hernandez <transphorm@gmail.com>
This commit is contained in:
turnoffthiscomputer
2025-05-07 05:45:21 -04:00
committed by GitHub
parent 5163761a52
commit cc169061bd
213 changed files with 5413 additions and 144545 deletions

View File

@@ -0,0 +1,266 @@
import { useMachine } from '@xstate/react';
import React, { useEffect } from 'react';
import { ActivityIndicator, View } from 'react-native';
import { assign, createMachine } from 'xstate';
import { black } from '../../utils/colors';
import Description from '../typography/Description';
import { HeldPrimaryButton } from './PrimaryButtonLongHold';
interface HeldPrimaryButtonProveScreenProps {
onVerify: () => void;
selectedAppSessionId: string | undefined | null;
hasScrolledToBottom: boolean;
isReadyToProve: boolean;
}
interface ButtonContext {
selectedAppSessionId: string | undefined | null;
hasScrolledToBottom: boolean;
isReadyToProve: boolean;
onVerify: () => void;
}
type ButtonEvent =
| {
type: 'PROPS_UPDATED';
selectedAppSessionId: string | undefined | null;
hasScrolledToBottom: boolean;
isReadyToProve: boolean;
}
| { type: 'VERIFY' };
const buttonMachine = createMachine(
{
id: 'proveButton',
types: {} as {
context: ButtonContext;
events: ButtonEvent;
actions: { type: 'callOnVerify' } | { type: 'updateContext' };
},
initial: 'waitingForSession',
context: ({ input }: { input: { onVerify: () => void } }) => ({
selectedAppSessionId: null as string | undefined | null,
hasScrolledToBottom: false,
isReadyToProve: false,
onVerify: input.onVerify,
}),
on: {
PROPS_UPDATED: {
actions: 'updateContext',
},
},
states: {
waitingForSession: {
always: {
target: 'needsScroll',
guard: ({ context }) => !!context.selectedAppSessionId,
},
},
needsScroll: {
always: [
{
target: 'waitingForSession',
guard: ({ context }) => !context.selectedAppSessionId,
},
{
target: 'preparing',
guard: ({ context }) => context.hasScrolledToBottom,
},
],
},
preparing: {
always: [
{
target: 'waitingForSession',
guard: ({ context }) => !context.selectedAppSessionId,
},
{
target: 'needsScroll',
guard: ({ context }) => !context.hasScrolledToBottom,
},
{
target: 'ready',
guard: ({ context }) => context.isReadyToProve,
},
],
after: {
500: { target: 'preparing2' },
},
},
preparing2: {
always: [
{
target: 'waitingForSession',
guard: ({ context }) => !context.selectedAppSessionId,
},
{
target: 'needsScroll',
guard: ({ context }) => !context.hasScrolledToBottom,
},
{
target: 'ready',
guard: ({ context }) => context.isReadyToProve,
},
],
after: {
500: { target: 'preparing3' },
},
},
preparing3: {
always: [
{
target: 'waitingForSession',
guard: ({ context }) => !context.selectedAppSessionId,
},
{
target: 'needsScroll',
guard: ({ context }) => !context.hasScrolledToBottom,
},
{
target: 'ready',
guard: ({ context }) => context.isReadyToProve,
},
],
},
ready: {
on: {
VERIFY: 'verifying',
},
always: [
{
target: 'waitingForSession',
guard: ({ context }) => !context.selectedAppSessionId,
},
{
target: 'needsScroll',
guard: ({ context }) => !context.hasScrolledToBottom,
},
{
target: 'preparing',
guard: ({ context }) => !context.isReadyToProve,
},
],
},
verifying: {
entry: 'callOnVerify',
// Remove always transitions checking hasScrolledToBottom and isReadyToProve
// Keep the button visually verifying until the component unmounts or session changes
always: {
target: 'waitingForSession',
guard: ({ context }) => !context.selectedAppSessionId,
},
},
},
},
{
actions: {
updateContext: assign(({ context, event }) => {
if (event.type === 'PROPS_UPDATED') {
if (
context.selectedAppSessionId !== event.selectedAppSessionId ||
context.hasScrolledToBottom !== event.hasScrolledToBottom ||
context.isReadyToProve !== event.isReadyToProve
) {
return {
selectedAppSessionId: event.selectedAppSessionId,
hasScrolledToBottom: event.hasScrolledToBottom,
isReadyToProve: event.isReadyToProve,
};
}
}
return context;
}),
callOnVerify: ({ context }) => {
context.onVerify();
},
},
},
);
export const HeldPrimaryButtonProveScreen: React.FC<
HeldPrimaryButtonProveScreenProps
> = ({
onVerify,
selectedAppSessionId,
hasScrolledToBottom,
isReadyToProve,
}) => {
const [state, send] = useMachine(buttonMachine, {
input: { onVerify },
});
useEffect(() => {
send({
type: 'PROPS_UPDATED',
selectedAppSessionId,
hasScrolledToBottom,
isReadyToProve,
});
}, [selectedAppSessionId, hasScrolledToBottom, isReadyToProve, send]);
const isDisabled = !state.matches('ready');
const renderButtonContent = () => {
if (state.matches('waitingForSession')) {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<ActivityIndicator color={black} style={{ marginRight: 8 }} />
<Description color={black}>Waiting for app...</Description>
</View>
);
}
if (state.matches('needsScroll')) {
return 'Please read all disclosures';
}
if (state.matches('preparing')) {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<ActivityIndicator color={black} style={{ marginRight: 8 }} />
<Description color={black}>Accessing to Keychain data</Description>
</View>
);
}
if (state.matches('preparing2')) {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<ActivityIndicator color={black} style={{ marginRight: 8 }} />
<Description color={black}>Parsing passport data</Description>
</View>
);
}
if (state.matches('preparing3')) {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<ActivityIndicator color={black} style={{ marginRight: 8 }} />
<Description color={black}>Preparing for verification</Description>
</View>
);
}
if (state.matches('ready')) {
return 'Hold to verify';
}
if (state.matches('verifying')) {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<ActivityIndicator color={black} style={{ marginRight: 8 }} />
<Description color={black}>Generating proof</Description>
</View>
);
}
return null;
};
return (
<HeldPrimaryButton
onLongPress={() => {
if (state.matches('ready')) {
send({ type: 'VERIFY' });
}
}}
disabled={isDisabled}
>
{renderButtonContent()}
</HeldPrimaryButton>
);
};

View File

@@ -16,9 +16,9 @@ const ACTION_TIMER = 600; // time in ms
const COLORS: RGBA[] = ['rgba(30, 41, 59, 0.3)', 'rgba(30, 41, 59, 1)'];
export function HeldPrimaryButton({
children,
onPress,
onLongPress,
...props
}: ButtonProps) {
}: ButtonProps & { onLongPress: () => void }) {
const animation = useAnimatedValue(0);
const [hasTriggered, setHasTriggered] = useState(false);
const [size, setSize] = useState({ width: 0, height: 0 });
@@ -68,14 +68,13 @@ export function HeldPrimaryButton({
animation.addListener(({ value }) => {
if (value >= 0.95 && !hasTriggered) {
setHasTriggered(true);
// @ts-expect-error
onPress();
onLongPress();
}
});
return () => {
animation.removeAllListeners();
};
}, [animation, hasTriggered]);
}, [animation, hasTriggered, onLongPress]);
return (
<PrimaryButton

View File

@@ -19,7 +19,8 @@ import {
} from 'tamagui';
import { countryCodes } from '../../../common/src/constants/constants';
import { genMockPassportData } from '../../../common/src/utils/passports/genMockPassportData';
import { getSKIPEM } from '../../../common/src/utils/csca';
import { genAndInitMockPassportData } from '../../../common/src/utils/passports/genMockPassportData';
import { initPassportDataParsing } from '../../../common/src/utils/passports/passport';
import { PrimaryButton } from '../components/buttons/PrimaryButton';
import { SecondaryButton } from '../components/buttons/SecondaryButton';
@@ -170,7 +171,7 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({}) => {
];
if (isInOfacList) {
mockPassportData = genMockPassportData(
mockPassportData = genAndInitMockPassportData(
hashFunction1,
hashFunction2,
signatureAlgorithm,
@@ -183,7 +184,7 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({}) => {
'ARCANGEL DE JESUS',
);
} else {
mockPassportData = genMockPassportData(
mockPassportData = genAndInitMockPassportData(
hashFunction1,
hashFunction2,
signatureAlgorithm,
@@ -193,7 +194,8 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({}) => {
randomPassportNumber,
);
}
mockPassportData = initPassportDataParsing(mockPassportData);
const skiPem = await getSKIPEM('staging');
mockPassportData = initPassportDataParsing(mockPassportData, skiPem);
await storePassportData(mockPassportData);
resolve(null);
}, 0),

View File

@@ -1,6 +1,7 @@
import { StaticScreenProps, usePreventRemove } from '@react-navigation/native';
import LottieView from 'lottie-react-native';
import React, { useEffect } from 'react';
import { ActivityIndicator, View } from 'react-native';
import successAnimation from '../../assets/animations/loading/success.json';
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
@@ -30,6 +31,8 @@ const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = ({
},
});
const provingStore = useProvingStore();
const currentState = useProvingStore(state => state.currentState);
const isReadyToProve = currentState === 'ready_to_prove';
useEffect(() => {
notificationSuccess();
@@ -78,7 +81,16 @@ const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = ({
By continuing, you certify that this passport belongs to you and is
not stolen or forged.
</Description>
<PrimaryButton onPress={onOkPress}>Confirm</PrimaryButton>
<PrimaryButton onPress={onOkPress} disabled={!isReadyToProve}>
{isReadyToProve ? (
'Confirm'
) : (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<ActivityIndicator color={black} style={{ marginRight: 8 }} />
<Description color={black}>Preparing verification</Description>
</View>
)}
</PrimaryButton>
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
</>

View File

@@ -11,6 +11,7 @@ import {
import NfcManager from 'react-native-nfc-manager';
import { Image } from 'tamagui';
import { getSKIPEM } from '../../../../common/src/utils/csca';
import { initPassportDataParsing } from '../../../../common/src/utils/passports/passport';
import { PassportData } from '../../../../common/src/utils/types';
import passportVerifyAnimation from '../../assets/animations/passport_verify.json';
@@ -77,7 +78,6 @@ const PassportNFCScanScreen: React.FC<PassportNFCScanScreenProps> = ({}) => {
buttonTap();
if (isNfcEnabled) {
setIsNfcSheetOpen(true);
// Add timestamp when scan starts
const scanStartTime = Date.now();
@@ -112,7 +112,8 @@ const PassportNFCScanScreen: React.FC<PassportNFCScanScreenProps> = ({}) => {
return;
}
try {
parsedPassportData = initPassportDataParsing(passportData);
const skiPem = await getSKIPEM('production');
parsedPassportData = initPassportDataParsing(passportData, skiPem);
const passportMetadata = parsedPassportData.passportMetadata!;
trackEvent('Passport Parsed', {
success: true,

View File

@@ -48,37 +48,37 @@ const ProofHistoryDetailScreen: React.FC<ProofHistoryDetailScreenProps> = ({
const result: string[] = [];
Object.entries(parsedDisclosures).forEach(([key, value]) => {
if (key == DisclosureType.MINIMUM_AGE && value) {
if (key === DisclosureType.MINIMUM_AGE && value) {
result.push(`Age is over ${value}`);
}
if (key == DisclosureType.NAME && value) {
if (key === DisclosureType.NAME && value) {
result.push(`Disclosed Name to ${data.appName}`);
}
if (key == DisclosureType.OFAC && value) {
if (key === DisclosureType.OFAC && value) {
result.push(`Not on the OFAC list`);
}
if (key == DisclosureType.AGE && value) {
if (key === DisclosureType.AGE && value) {
result.push(`Disclosed Age to ${data.appName}`);
}
if (key == DisclosureType.ISSUING_STATE && value) {
if (key === DisclosureType.ISSUING_STATE && value) {
result.push(`Disclosed Issuing State to ${data.appName}`);
}
if (key == DisclosureType.PASSPORT_NUMBER && value) {
if (key === DisclosureType.PASSPORT_NUMBER && value) {
result.push(`Disclosed Passport Number to ${data.appName}`);
}
if (key == DisclosureType.NATIONALITY && value) {
if (key === DisclosureType.NATIONALITY && value) {
result.push(`Disclosed Nationality to ${data.appName}`);
}
if (key == DisclosureType.DATE_OF_BIRTH && value) {
if (key === DisclosureType.DATE_OF_BIRTH && value) {
result.push(`Disclosed Date of Birth to ${data.appName}`);
}
if (key == DisclosureType.GENDER && value) {
if (key === DisclosureType.GENDER && value) {
result.push(`Disclosed Gender to ${data.appName}`);
}
if (key == DisclosureType.EXPIRY_DATE && value) {
if (key === DisclosureType.EXPIRY_DATE && value) {
result.push(`Disclosed Expiry Date to ${data.appName}`);
}
if (key == DisclosureType.EXCLUDED_COUNTRIES) {
if (key === DisclosureType.EXCLUDED_COUNTRIES) {
if (value && Array.isArray(value) && value.length > 0) {
result.push(`Disclosed - Not from excluded countries`);
}
@@ -98,9 +98,9 @@ const ProofHistoryDetailScreen: React.FC<ProofHistoryDetailScreenProps> = ({
}, [data.timestamp]);
const proofStatus = useMemo(() => {
if (data.status == 'success') {
if (data.status === 'success') {
return 'PROOF GRANTED';
} else if (data.status == ProofStatus.PENDING) {
} else if (data.status === ProofStatus.PENDING) {
return 'PROOF PENDING';
} else {
return 'PROOF FAILED';
@@ -128,8 +128,8 @@ const ProofHistoryDetailScreen: React.FC<ProofHistoryDetailScreenProps> = ({
const isEthereumAddress = useMemo(() => {
return (
/^0x[a-fA-F0-9]+$/.test(data.userId) &&
(data.endpointType == 'staging_celo' || data.endpointType == 'celo') &&
data.userIdType == 'hex'
(data.endpointType === 'staging_celo' || data.endpointType === 'celo') &&
data.userIdType === 'hex'
);
}, [data.userId, data.endpointType, data.userIdType]);
@@ -263,7 +263,9 @@ const ProofHistoryDetailScreen: React.FC<ProofHistoryDetailScreenProps> = ({
>
<YStack
backgroundColor={
data.status == ProofStatus.SUCCESS ? emerald500 : red500
data.status === ProofStatus.SUCCESS
? emerald500
: red500
}
width={8}
height={8}

View File

@@ -219,8 +219,8 @@ const ProofHistoryScreen: React.FC = () => {
{formatDate(item.timestamp)}
</BodyText>
</YStack>
{(item.endpointType == 'staging_celo' ||
item.endpointType == 'celo') && (
{(item.endpointType === 'staging_celo' ||
item.endpointType === 'celo') && (
<XStack
backgroundColor={blue100}
paddingVertical={2}

View File

@@ -45,8 +45,10 @@ const SuccessScreen: React.FC = () => {
function onOkPress() {
buttonTap();
cleanSelfApp();
goHome();
setTimeout(() => {
cleanSelfApp();
}, 2000); // Wait 2 seconds to user coming back to the home screen. If we don't wait the appname will change and user will see it.
}
useEffect(() => {

View File

@@ -19,7 +19,7 @@ import { Image, Text, View, YStack } from 'tamagui';
import { SelfAppDisclosureConfig } from '../../../../common/src/utils/appType';
import { formatEndpoint } from '../../../../common/src/utils/scope';
import miscAnimation from '../../assets/animations/loading/misc.json';
import { HeldPrimaryButton } from '../../components/buttons/PrimaryButtonLongHold';
import { HeldPrimaryButtonProveScreen } from '../../components/buttons/HeldPrimaryButtonProveScreen';
import Disclosures from '../../components/Disclosures';
import { BodyText } from '../../components/typography/BodyText';
import { Caption } from '../../components/typography/Caption';
@@ -48,7 +48,11 @@ const ProveScreen: React.FC = () => {
() => scrollViewContentHeight <= scrollViewHeight,
[scrollViewContentHeight, scrollViewHeight],
);
const provingStore = useProvingStore();
const currentState = useProvingStore(state => state.currentState);
const isReadyToProve = currentState === 'ready_to_prove';
const { addProofHistory } = useProofHistoryStore();
useEffect(() => {
@@ -129,7 +133,7 @@ const ProveScreen: React.FC = () => {
buttonTap();
setTimeout(() => {
navigate('ProofRequestStatusScreen');
}, 200);
}, 100);
}
const handleScroll = useCallback(
@@ -227,14 +231,12 @@ const ProveScreen: React.FC = () => {
</Caption>
</View>
</ScrollView>
<HeldPrimaryButton
onPress={onVerify}
disabled={!selectedApp?.sessionId || !hasScrolledToBottom}
>
{hasScrolledToBottom
? 'Hold To Verify'
: 'Please read all disclosures'}
</HeldPrimaryButton>
<HeldPrimaryButtonProveScreen
onVerify={onVerify}
selectedAppSessionId={selectedApp?.sessionId}
hasScrolledToBottom={hasScrolledToBottom}
isReadyToProve={isReadyToProve}
/>
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
);

View File

@@ -19,7 +19,7 @@ import {
YStack,
} from 'tamagui';
import { genMockPassportData } from '../../../../common/src/utils/passports/genMockPassportData';
import { genAndInitMockPassportData } from '../../../../common/src/utils/passports/genMockPassportData';
import { RootStackParamList } from '../../Navigation';
import {
unsafe_clearSecrets,
@@ -160,7 +160,7 @@ const DevSettingsScreen: React.FC<DevSettingsScreenProps> = ({}) => {
}
function handleGenerateMockPassportData() {
const passportData = genMockPassportData(
const passportData = genAndInitMockPassportData(
'sha256',
'sha256',
'rsa_sha256_65537_2048',

View File

@@ -1,6 +1,6 @@
import { useNavigation } from '@react-navigation/native';
import LottieView from 'lottie-react-native';
import React, { useCallback, useEffect } from 'react';
import React, { useCallback, useEffect, useRef } from 'react';
import { StyleSheet } from 'react-native';
import { PassportData } from '../../../common/src/utils/types';
@@ -17,60 +17,82 @@ const SplashScreen: React.FC = ({}) => {
const navigation = useNavigation();
const { checkBiometricsAvailable } = useAuth();
const { setBiometricsAvailable } = useSettingStore();
const [isAnimationFinished, setIsAnimationFinished] = React.useState(false);
const [nextScreen, setNextScreen] = React.useState<string | null>(null);
const dataLoadInitiatedRef = useRef(false);
useEffect(() => {
checkBiometricsAvailable()
.then(setBiometricsAvailable)
.catch(err => {
console.warn('Error checking biometrics availability', err);
navigation.navigate('Launch');
throw new Error(`Error checking biometrics availability: ${err}`);
});
}, [navigation]);
if (!dataLoadInitiatedRef.current) {
dataLoadInitiatedRef.current = true;
console.log('Starting data loading and validation...');
checkBiometricsAvailable()
.then(setBiometricsAvailable)
.catch(err => {
console.warn('Error checking biometrics availability', err);
});
const loadDataAndDetermineNextScreen = async () => {
try {
const passportDataAndSecret = await loadPassportDataAndSecret();
if (!passportDataAndSecret) {
setNextScreen('Launch');
return;
}
const { passportData, secret } = JSON.parse(passportDataAndSecret);
if (!isPassportDataValid(passportData)) {
setNextScreen('Launch');
return;
}
const environment =
(passportData as PassportData).documentType &&
(passportData as PassportData).documentType !== 'passport'
? 'stg'
: 'prod';
await useProtocolStore.getState().passport.fetch_all(environment);
const isRegistered = await isUserRegistered(passportData, secret);
console.log('User is registered:', isRegistered);
if (isRegistered) {
console.log(
'Passport is registered already. Setting next screen to Home',
);
setNextScreen('Home');
return;
}
// Currently, we dont check isPassportNullified(passportData);
// This could lead to AccountRecoveryChoice just like in LoadingScreen
// But it looks better right now to keep the LaunchScreen flow
// In case user wants to try with another passport.
// Long term, we could also show a modal instead that prompts the user to recover or scan a new passport.
// Rest of the time, keep the LaunchScreen flow
setNextScreen('Launch');
} catch (error) {
console.error(`Error in SplashScreen data loading: ${error}`);
setNextScreen('Launch');
}
};
loadDataAndDetermineNextScreen();
}
}, []);
const handleAnimationFinish = useCallback(() => {
try {
setTimeout(async () => {
impactLight();
const passportDataAndSecret = await loadPassportDataAndSecret();
impactLight();
setIsAnimationFinished(true);
}, []);
if (!passportDataAndSecret) {
navigation.navigate('Launch');
return;
}
const { passportData, secret } = JSON.parse(passportDataAndSecret);
if (!isPassportDataValid(passportData)) {
navigation.navigate('Launch');
return;
}
const environment =
(passportData as PassportData).documentType &&
(passportData as PassportData).documentType !== 'passport'
? 'stg'
: 'prod';
await useProtocolStore.getState().passport.fetch_all(environment);
const isRegistered = await isUserRegistered(passportData, secret);
console.log('User is registered:', isRegistered);
if (isRegistered) {
console.log('Passport is registered already. Skipping to HomeScreen');
navigation.navigate('Home');
return;
}
// Currently, we dont check isPassportNullified(passportData);
// This could lead to AccountRecoveryChoice just like in LoadingScreen
// But it looks better right now to keep the LaunchScreen flow
// In case user wants to try with another passport.
// Long term, we could also show a modal instead that prompts the user to recover or scan a new passport.
// Rest of the time, keep the LaunchScreen flow
navigation.navigate('Launch');
}, 1000);
} catch (error) {
navigation.navigate('Launch');
throw new Error(`Error in SplashScreen: ${error}`);
useEffect(() => {
if (isAnimationFinished && nextScreen) {
console.log(`Navigating to ${nextScreen}`);
requestAnimationFrame(() => {
navigation.navigate(nextScreen as any);
});
}
}, [navigation]);
}, [isAnimationFinished, nextScreen, navigation]);
return (
<LottieView

View File

@@ -15,7 +15,7 @@ interface ProtocolState {
passport: {
commitment_tree: any;
dsc_tree: any;
csca_tree: any;
csca_tree: string[][] | null;
deployed_circuits: any;
circuits_dns_mapping: any;
fetch_deployed_circuits: (environment: 'prod' | 'stg') => Promise<void>;
@@ -44,45 +44,106 @@ export const useProtocolStore = create<ProtocolState>((set, get) => ({
]);
},
fetch_deployed_circuits: async (environment: 'prod' | 'stg') => {
const response = await fetch(
`${
environment === 'prod' ? API_URL : API_URL_STAGING
}/deployed-circuits`,
);
const data = await response.json();
set({ passport: { ...get().passport, deployed_circuits: data.data } });
const url = `${environment === 'prod' ? API_URL : API_URL_STAGING}/deployed-circuits`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`HTTP error fetching ${url}! status: ${response.status}`,
);
}
const responseText = await response.text();
const data = JSON.parse(responseText);
set({ passport: { ...get().passport, deployed_circuits: data.data } });
} catch (error) {
console.error(`Failed fetching deployed circuits from ${url}:`, error);
// Optionally handle error state
}
},
fetch_circuits_dns_mapping: async (environment: 'prod' | 'stg') => {
const response = await fetch(
`${
environment === 'prod' ? API_URL : API_URL_STAGING
}/circuit-dns-mapping`,
);
const data = await response.json();
set({ passport: { ...get().passport, circuits_dns_mapping: data.data } });
const url = `${environment === 'prod' ? API_URL : API_URL_STAGING}/circuit-dns-mapping`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`HTTP error fetching ${url}! status: ${response.status}`,
);
}
const responseText = await response.text();
const data = JSON.parse(responseText);
set({
passport: { ...get().passport, circuits_dns_mapping: data.data },
});
} catch (error) {
console.error(
`Failed fetching circuit DNS mapping from ${url}:`,
error,
);
// Optionally handle error state
}
},
fetch_csca_tree: async (environment: 'prod' | 'stg') => {
const response = await fetch(
`${environment === 'prod' ? CSCA_TREE_URL : CSCA_TREE_URL_STAGING}`,
);
const data = await response.json();
set({ passport: { ...get().passport, csca_tree: data.data } });
const url =
environment === 'prod' ? CSCA_TREE_URL : CSCA_TREE_URL_STAGING;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`HTTP error fetching ${url}! status: ${response.status}`,
);
}
const responseText = await response.text();
const rawData = JSON.parse(responseText);
let treeData: any;
if (rawData && rawData.data) {
treeData =
typeof rawData.data === 'string'
? JSON.parse(rawData.data)
: rawData.data;
} else {
treeData = rawData; // Assume rawData is the tree if no .data field
}
set({ passport: { ...get().passport, csca_tree: treeData } });
} catch (error) {
console.error(`Failed fetching CSCA tree from ${url}:`, error);
set({ passport: { ...get().passport, csca_tree: null } }); // Reset on error
}
},
fetch_dsc_tree: async (environment: 'prod' | 'stg') => {
const response = await fetch(
`${environment === 'prod' ? DSC_TREE_URL : DSC_TREE_URL_STAGING}`,
);
const data = await response.json();
set({ passport: { ...get().passport, dsc_tree: data.data } });
const url = environment === 'prod' ? DSC_TREE_URL : DSC_TREE_URL_STAGING;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`HTTP error fetching ${url}! status: ${response.status}`,
);
}
const responseText = await response.text();
const data = JSON.parse(responseText);
set({ passport: { ...get().passport, dsc_tree: data.data } });
} catch (error) {
console.error(`Failed fetching DSC tree from ${url}:`, error);
// Optionally handle error state
}
},
fetch_identity_tree: async (environment: 'prod' | 'stg') => {
const response = await fetch(
`${
environment === 'prod' ? IDENTITY_TREE_URL : IDENTITY_TREE_URL_STAGING
}`,
);
const data = await response.json();
set({ passport: { ...get().passport, commitment_tree: data.data } });
const url =
environment === 'prod' ? IDENTITY_TREE_URL : IDENTITY_TREE_URL_STAGING;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`HTTP error fetching ${url}! status: ${response.status}`,
);
}
const responseText = await response.text();
const data = JSON.parse(responseText);
set({ passport: { ...get().passport, commitment_tree: data.data } });
} catch (error) {
console.error(`Failed fetching identity tree from ${url}:`, error);
// Optionally handle error state
}
},
},
}));

View File

@@ -40,7 +40,7 @@ export function generateTEEInputsDSC(
passportData: PassportData,
cscaTree: string[][],
) {
const inputs = generateCircuitInputsDSC(passportData.dsc, cscaTree);
const inputs = generateCircuitInputsDSC(passportData, cscaTree);
const circuitName = getCircuitNameFromPassportData(passportData, 'dsc');
const endpointType =
passportData.documentType && passportData.documentType !== 'passport'
@@ -78,7 +78,7 @@ export function generateTEEInputsDisclose(
const { passportNoAndNationalitySMT, nameAndDobSMT, nameAndYobSMT } =
getOfacSMTs();
const serialized_tree = useProtocolStore.getState().passport.commitment_tree; //await getCommitmentTree(passportData.documentType);
const serialized_tree = useProtocolStore.getState().passport.commitment_tree;
const tree = LeanIMT.import((a, b) => poseidon2([a, b]), serialized_tree);
const inputs = generateCircuitInputsVCandDisclose(

View File

@@ -124,7 +124,10 @@ interface ProvingState {
error_code: string | null;
reason: string | null;
endpointType: EndpointType | null;
init: (circuitType: 'dsc' | 'disclose' | 'register') => Promise<void>;
init: (
circuitType: 'dsc' | 'disclose' | 'register',
userConfirmed?: boolean,
) => Promise<void>;
startFetchingData: () => Promise<void>;
validatingDocument: () => Promise<void>;
initTeeConnection: () => Promise<boolean>;
@@ -432,7 +435,10 @@ export const useProvingStore = create<ProvingState>((set, get) => {
}
},
init: async (circuitType: 'dsc' | 'disclose' | 'register') => {
init: async (
circuitType: 'dsc' | 'disclose' | 'register',
userConfirmed: boolean = false,
) => {
get()._closeConnections();
if (actor) {
@@ -450,7 +456,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
wsConnection: null,
socketConnection: null,
uuid: null,
userConfirmed: false,
userConfirmed: userConfirmed,
passportData: null,
secret: null,
circuitType,
@@ -641,7 +647,9 @@ export const useProvingStore = create<ProvingState>((set, get) => {
_checkActorInitialized(actor);
const { circuitType } = get();
if (circuitType === 'dsc') {
get().init('register');
setTimeout(() => {
get().init('register', true);
}, 1500);
} else if (circuitType === 'register') {
actor!.send({ type: 'COMPLETED' });
} else if (circuitType === 'disclose') {
@@ -700,7 +708,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
({ inputs, circuitName, endpointType, endpoint } =
generateTEEInputsDSC(
passportData,
protocolStore.passport.csca_tree,
protocolStore.passport.csca_tree as string[][],
));
break;
case 'disclose':