Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: backup and restore #194

Merged
merged 3 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions app/screens/ExportWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { addWalletRecord, findWalletRecordsByQuery, useAdeyaAgent, utils } from '@adeya/ssi'
import { useNavigation } from '@react-navigation/core'
import { generateMnemonic } from 'bip39'
import React, { useEffect, useState } from 'react'
Expand All @@ -13,6 +14,7 @@ const ExportWallet: React.FC = () => {
const navigation = useNavigation()
const { t } = useTranslation()
const [phraseData, setPhraseData] = useState<string[]>([])
const { agent } = useAdeyaAgent()

const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window')

Expand Down Expand Up @@ -86,15 +88,42 @@ const ExportWallet: React.FC = () => {
})

useEffect(() => {
const mnemonic = generateMnemonic(128)
const mnemonicArray = mnemonic.split(' ')
const createMnemonic = async () => {
const mnemonicRecord = await findWalletRecordsByQuery(agent, { type: 'mnemonic' })
if (mnemonicRecord?.length > 0) {
const mnemonic = mnemonicRecord[0].content.mnemonic as string
const mnemonicArray = mnemonic.split(' ')

const mnemonicIndividualWordsArray: string[] = []
mnemonicArray.forEach(word => {
mnemonicIndividualWordsArray.push(word)
})
const mnemonicIndividualWordsArray: string[] = []
mnemonicArray.forEach(word => {
mnemonicIndividualWordsArray.push(word)
})

setPhraseData(mnemonicIndividualWordsArray.splice(1, 8))
setPhraseData(mnemonicIndividualWordsArray.splice(1, 8))
} else {
const mnemonic = generateMnemonic(128)
const mnemonicArray = mnemonic.split(' ')

const mnemonicIndividualWordsArray: string[] = []
mnemonicArray.forEach(word => {
mnemonicIndividualWordsArray.push(word)
})

await addWalletRecord(agent, {
id: utils.uuid(),
content: {
mnemonic,
},
tags: {
type: 'mnemonic',
},
})

setPhraseData(mnemonicIndividualWordsArray.splice(1, 8))
}
}

createMnemonic()
}, [])

return (
Expand Down
116 changes: 30 additions & 86 deletions app/screens/ExportWalletConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { exportWallet as exportAdeyaWallet } from '@adeya/ssi'
import { useNavigation, useRoute } from '@react-navigation/core'
import shuffle from 'lodash.shuffle'
import moment from 'moment'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
View,
Text,
TouchableOpacity,
ScrollView,
PermissionsAndroid,
Platform,
Share,
Dimensions,
PixelRatio,
StyleSheet,
Platform,
Share,
} from 'react-native'
import { DownloadDirectoryPath, exists, mkdir, unlink } from 'react-native-fs'
import * as RNFS from 'react-native-fs'
import Toast from 'react-native-toast-message'
import RNFetchBlob from 'rn-fetch-blob'
import { zip } from 'react-native-zip-archive'

import ButtonLoading from '../components/animated/ButtonLoading'
import Button, { ButtonType } from '../components/buttons/Button'
Expand Down Expand Up @@ -136,67 +136,29 @@ function ExportWalletConfirmation() {
const encodeHash = seed

try {
const documentDirectory: string = DownloadDirectoryPath
const backupDirectory = `${documentDirectory}/Wallet_Backup`
const destFileExists = await exists(backupDirectory)
if (destFileExists) {
await unlink(backupDirectory)
}
const date = new Date()
const dformat = `${date.getHours()}-${date.getMinutes()}-${date.getSeconds()}`
const WALLET_FILE_NAME = `SSI_Wallet_${dformat}`

await mkdir(backupDirectory)
const encryptedFileName = `${WALLET_FILE_NAME}.wallet`
const encryptedFileLocation = `${backupDirectory}/${encryptedFileName}`

const exportConfig = {
key: encodeHash,
path: encryptedFileLocation,
let downloadDirectory = ''
if (Platform.OS === 'ios') {
downloadDirectory = RNFS.DocumentDirectoryPath
} else {
downloadDirectory = RNFS.DownloadDirectoryPath
}

await exportAdeyaWallet(agent, exportConfig)
const backupTimeStamp = moment().format('YYYY-MM-DD-HH-mm-ss')
// const backupDirectory = `${documentDirectory}/Wallet_Backup`
const zipUpDirectory = `${downloadDirectory}/ADEYA-Wallet-${backupTimeStamp}`

Toast.show({
type: ToastType.Success,
text1: 'Backup successfully',
})
setMatchPhrase(true)
navigation.navigate(Screens.Success, { encryptedFileLocation })
} catch (e) {
Toast.show({
type: ToastType.Error,
text1: 'Backup failed',
})
}
}
const exportWalletIOS = async (seed: string) => {
setMatchPhrase(true)

const encodeHash = seed
const { fs } = RNFetchBlob
try {
const documentDirectory = fs.dirs.DocumentDir

const zipDirectory = `${documentDirectory}/Wallet_Backup`

const destFileExists = await fs.exists(zipDirectory)
const destFileExists = await RNFS.exists(zipUpDirectory)
if (destFileExists) {
await fs.unlink(zipDirectory)
await RNFS.unlink(zipUpDirectory)
}

const date = new Date()
const dformat = `${date.getHours()}-${date.getMinutes()}-${date.getSeconds()}`
const WALLET_FILE_NAME = `SSI_Wallet_${dformat}`
const WALLET_FILE_NAME = 'ADEYA_WALLET'

await fs.mkdir(zipDirectory).catch(err =>
Toast.show({
type: ToastType.Error,
text1: err,
}),
)
const zipFileName = `${WALLET_FILE_NAME}-${backupTimeStamp}.zip`
await RNFS.mkdir(zipUpDirectory)
const encryptedFileName = `${WALLET_FILE_NAME}.wallet`
const encryptedFileLocation = `${zipDirectory}/${encryptedFileName}`
const encryptedFileLocation = `${zipUpDirectory}/${encryptedFileName}`
const destinationZipPath = `${downloadDirectory}/${zipFileName}`

const exportConfig = {
key: encodeHash,
Expand All @@ -205,19 +167,23 @@ function ExportWalletConfirmation() {

await exportAdeyaWallet(agent, exportConfig)

await zip(zipUpDirectory, destinationZipPath)

await RNFS.unlink(zipUpDirectory)

if (Platform.OS === 'ios') {
await Share.share({
title: 'Share file',
url: encryptedFileLocation,
title: 'Share backup zip file',
url: destinationZipPath,
})
}

Toast.show({
type: ToastType.Success,
text1: 'Backup successfully',
text1: 'Backup successfully completed',
})
setMatchPhrase(true)
navigation.navigate(Screens.Success, { encryptedFileLocation })
navigation.navigate(Screens.Success, { encryptedFileLocation: destinationZipPath })
} catch (e) {
Toast.show({
type: ToastType.Error,
Expand Down Expand Up @@ -248,36 +214,14 @@ function ExportWalletConfirmation() {
setNextPhraseIndex(index)
}

const askPermission = async (sysPassPhrase: string) => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
title: 'Permission',
message: 'ADEYA Wallet needs to write to storage',
buttonPositive: '',
})
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
await exportWallet(sysPassPhrase)
}
} catch (error) {
Toast.show({
type: ToastType.Error,
text1: `${error}`,
})
}
} else {
await exportWalletIOS(sysPassPhrase)
}
}

const verifyPhrase = () => {
const verifyPhrase = async () => {
const addedPassPhraseData = arraySetPhraseData.join('')
const displayedPassphrase = parms?.params?.phraseData.map(item => item).join('')
if (displayedPassphrase.trim() !== '') {
const sysPassPhrase = addedPassPhraseData.trim()
const userPassphrase = displayedPassphrase.trim()
if (sysPassPhrase === userPassphrase) {
askPermission(sysPassPhrase)
await exportWallet(sysPassPhrase)
} else {
Toast.show({
type: ToastType.Error,
Expand Down
41 changes: 14 additions & 27 deletions app/screens/ImportWalletConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
LogLevel,
InitConfig,
getAgentModules,
isWalletImportable,
DidsModule,
IndyVdrIndyDidResolver,
SingleContextStorageLruCache,
Expand All @@ -25,10 +24,12 @@ import {
ScrollView,
} from 'react-native'
import { Config } from 'react-native-config'
import { DocumentPickerResponse, isCancel, pickSingle, types } from 'react-native-document-picker'
import { isCancel, pickSingle, types } from 'react-native-document-picker'
import * as RNFS from 'react-native-fs'
import { heightPercentageToDP } from 'react-native-responsive-screen'
import { Toast } from 'react-native-toast-message/lib/src/Toast'
import { unzip } from 'react-native-zip-archive'
import RNFetchBlob from 'rn-fetch-blob'

import indyLedgers from '../../configs/ledgers/indy'
import ButtonLoading from '../components/animated/ButtonLoading'
Expand Down Expand Up @@ -128,9 +129,15 @@ const ImportWalletVerify: React.FC<ImportWalletVerifyProps> = ({ navigation }) =
key: credentials.key,
}

const { fs } = RNFetchBlob
const restoreDirectoryPath = `${fs.dirs.DocumentDir}`
const walletFilePath = `${restoreDirectoryPath}/ADEYA_WALLET_RESTORE/ADEYA_WALLET.wallet`

await unzip(selectedFilePath, restoreDirectoryPath + '/ADEYA_WALLET_RESTORE')

const importConfig = {
key: encodeHash,
path: selectedFilePath,
path: walletFilePath,
}

const agentConfig: InitConfig = {
Expand All @@ -140,18 +147,6 @@ const ImportWalletVerify: React.FC<ImportWalletVerifyProps> = ({ navigation }) =
autoUpdateStorageOnStartup: true,
}

const walletImportCheck = await isWalletImportable({ ...walletConfig }, importConfig)

if (!walletImportCheck) {
Toast.show({
type: ToastType.Error,
text1: `You've entered an invalid passphrase.`,
position: 'bottom',
})
setVerify(false)
return
}

const agent = await importWalletWithAgent({
agentConfig,
importConfig,
Expand All @@ -169,6 +164,8 @@ const ImportWalletVerify: React.FC<ImportWalletVerifyProps> = ({ navigation }) =
},
})

await RNFS.unlink(restoreDirectoryPath + '/ADEYA_WALLET_RESTORE')

setAgent(agent!)
setVerify(true)
Toast.show({
Expand Down Expand Up @@ -204,21 +201,11 @@ const ImportWalletVerify: React.FC<ImportWalletVerifyProps> = ({ navigation }) =

const handleSelect = async () => {
try {
const res: DocumentPickerResponse = await pickSingle({
type: [types.allFiles],
const res = await pickSingle({
type: [types.zip],
copyTo: 'documentDirectory',
})

if (!res.name?.endsWith('.wallet')) {
Toast.show({
type: ToastType.Error,
text1: 'Please select a valid wallet file',
visibilityTime: 2000,
})
navigation.goBack()
return
}

if (!res.fileCopyUri) {
Toast.show({
type: ToastType.Error,
Expand Down
14 changes: 14 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,15 @@ PODS:
- React-Core
- RNVectorIcons (10.0.0):
- React-Core
- RNZipArchive (6.1.2):
- React-Core
- RNZipArchive/Core (= 6.1.2)
- SSZipArchive (~> 2.2)
- RNZipArchive/Core (6.1.2):
- React-Core
- SSZipArchive (~> 2.2)
- SocketRocket (0.6.1)
- SSZipArchive (2.4.3)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -740,6 +748,7 @@ DEPENDENCIES:
- RNShare (from `../node_modules/react-native-share`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- RNZipArchive (from `../node_modules/react-native-zip-archive`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

SPEC REPOS:
Expand All @@ -761,6 +770,7 @@ SPEC REPOS:
- nanopb
- PromisesObjC
- SocketRocket
- SSZipArchive

EXTERNAL SOURCES:
anoncreds:
Expand Down Expand Up @@ -902,6 +912,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-svg"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
RNZipArchive:
:path: "../node_modules/react-native-zip-archive"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"

Expand Down Expand Up @@ -991,7 +1003,9 @@ SPEC CHECKSUMS:
RNShare: bed7c4fbe615f3d977f22feb0902af9a790c1660
RNSVG: 80584470ff1ffc7994923ea135a3e5ad825546b9
RNVectorIcons: 8b5bb0fa61d54cd2020af4f24a51841ce365c7e9
RNZipArchive: 6d736ee4e286dbbd9d81206b7a4da355596ca04a
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce

PODFILE CHECKSUM: c4f2b64f733d5092ed97bf9c8913baae9338ea31
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"react-native-toast-message": "^2.1.6",
"react-native-vector-icons": "^10.0.0",
"react-native-webview": "^13.3.1",
"react-native-zip-archive": "^6.1.2",
"readable-stream": "1.0.33",
"rn-fetch-blob": "^0.12.0",
"stream-browserify": "^1.0.0",
Expand Down
Loading
Loading