From 9f94bc51665b382169f662b3b899d8b0323bed56 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 12 Aug 2024 18:09:26 +0200 Subject: [PATCH 01/25] Checkpoint for VerusID revocation/recovery, implement beginnings of UI from home screen, start linking revocation code to verusid-ts-client library --- .../sendModal/dispatchers/sendModal.js | 22 ++- .../LinkIdentityForm/LinkIdentityForm.js | 2 +- .../LinkIdentityResult/LinkIdentityResult.js | 29 ++++ .../LinkIdentityResult.render.js | 76 +++++++++ .../RevokeIdentityConfirm.js | 89 ++++++++++ .../RevokeIdentityConfirm.render.js | 44 +++++ .../RevokeIdentityForm/RevokeIdentityForm.js | 141 ++++++++++++++++ .../RevokeIdentityForm.render.js | 41 +++++ src/components/SendModal/SendModal.js | 4 +- src/components/SendModal/SendModal.render.js | 12 +- .../Forms/ImportWallet/Forms/ImportIntro.js | 7 +- .../Forms/ImportWallet/ImportWallet.js | 2 + .../DeepLink/InvoiceInfo/InvoiceInfo.js | 2 +- .../LoginRequestInfo/LoginRequestInfo.js | 2 +- src/containers/Login/Login.js | 15 ++ .../RevokeRecoverIdentityForm.js | 151 +++++++++++++++++ .../RevokeRecover/RevokeRecoverSlider.js | 159 ++++++++++++++++++ .../RevokeRecoverStackScreens.js | 65 +++++++ .../SignedOutStackScreens.js | 9 + .../VerusIdServiceOverview.js | 2 +- .../verusid/requests/getFriendlyNameMap.js | 4 +- .../verusid/requests/updateIdentity.js | 83 +++++++++ .../channels/vrpc/requests/getAddressUtxos.js | 28 +++ .../api/channels/vrpc/requests/preflight.js | 25 +-- src/utils/constants/sendModal.js | 3 + src/utils/crypto/verifyMerkle.js | 15 -- 26 files changed, 980 insertions(+), 52 deletions(-) create mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js create mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js create mode 100644 src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js create mode 100644 src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js create mode 100644 src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js create mode 100644 src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js create mode 100644 src/containers/RevokeRecover/RevokeRecoverIdentityForm.js create mode 100644 src/containers/RevokeRecover/RevokeRecoverSlider.js create mode 100644 src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js create mode 100644 src/utils/api/channels/verusid/requests/updateIdentity.js diff --git a/src/actions/actions/sendModal/dispatchers/sendModal.js b/src/actions/actions/sendModal/dispatchers/sendModal.js index 3eee5ad5..ca54a356 100644 --- a/src/actions/actions/sendModal/dispatchers/sendModal.js +++ b/src/actions/actions/sendModal/dispatchers/sendModal.js @@ -1,4 +1,5 @@ import store from '../../../../store'; +import { coinsList } from '../../../../utils/CoinData/CoinsList'; import { CONVERSION_SEND_MODAL, WITHDRAW_SEND_MODAL, @@ -36,7 +37,10 @@ import { SEND_MODAL_SHOW_MAPPING_FIELD, SEND_MODAL_PBAAS_CURRENCY_PASSTHROUGH, SEND_MODAL_SHOW_IS_PRECONVERT, - SEND_MODAL_DISABLED_INPUTS + SEND_MODAL_DISABLED_INPUTS, + SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, + REVOKE_IDENTITY_SEND_MODAL, + SEND_MODAL_SYSTEM_ID } from '../../../../utils/constants/sendModal'; import { CLOSE_SEND_COIN_MODAL, @@ -129,6 +133,22 @@ export const openLinkIdentityModal = (coinObj, data) => { ); }; +export const openRevokeIdentitySendModal = (data) => { + openSendModal( + `Revoke VerusID`, + null, + null, + data == null + ? { + [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', + [SEND_MODAL_SYSTEM_ID]: coinsList.VRSC.system_id + } + : data, + REVOKE_IDENTITY_SEND_MODAL, + 'To revoke a VerusID, enter the handle or i-Address of a VerusID with a revocation VerusID that you control.', + ); +}; + export const openAddPbaasCurrencyModal = (coinObj, data) => { openSendModal( `Add Currency`, diff --git a/src/components/SendModal/LinkIdentity/LinkIdentityForm/LinkIdentityForm.js b/src/components/SendModal/LinkIdentity/LinkIdentityForm/LinkIdentityForm.js index 56f64ff0..7a1fdb1c 100644 --- a/src/components/SendModal/LinkIdentity/LinkIdentityForm/LinkIdentityForm.js +++ b/src/components/SendModal/LinkIdentity/LinkIdentityForm/LinkIdentityForm.js @@ -103,7 +103,7 @@ const LinkIdentityForm = (props) => { ); } - const friendlyNames = await getFriendlyNameMap(coinObj, res.result); + const friendlyNames = await getFriendlyNameMap(coinObj.system_id, res.result); props.setModalHeight(height >= 720 ? 696 : height - 24); props.navigation.navigate(SEND_MODAL_FORM_STEP_CONFIRM, { diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js new file mode 100644 index 00000000..b6bd791b --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js @@ -0,0 +1,29 @@ +import {useEffect, useState} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; +import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; +import {LinkIdentityResultRender} from './LinkIdentityResult.render'; + +const LinkIdentityResult = (props) => { + const [verusId, setVerusId] = useState(props.route.params == null ? {} : props.route.params.verusId); + const [friendlyNames, setFriendlyNames] = useState(props.route.params == null ? {} : props.route.params.friendlyNames); + const sendModal = useSelector(state => state.sendModal); + const {data} = sendModal; + + const finishSend = async () => { + if (data.noLogin) { + await props.updateSendFormData( + "success", + true, + ); + } + closeSendModal() + }; + + return LinkIdentityResultRender({ + verusId, + friendlyNames, + finishSend + }); +}; + +export default LinkIdentityResult; diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js new file mode 100644 index 00000000..78c55379 --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js @@ -0,0 +1,76 @@ +import React from 'react'; +import {ScrollView, View, TouchableOpacity} from 'react-native'; +import {Button, Text} from 'react-native-paper'; +import Colors from '../../../../globals/colors'; +import Styles from '../../../../styles'; +import {copyToClipboard} from '../../../../utils/clipboard/clipboard'; +import AnimatedSuccessCheckmark from '../../../AnimatedSuccessCheckmark'; +import { useSelector } from 'react-redux'; +import { convertFqnToDisplayFormat } from '../../../../utils/fullyqualifiedname'; + +export const LinkIdentityResultRender = ({verusId, finishSend}) => { + const coinObj = useSelector(state => state.sendModal.coinObj); + const formattedFriendlyName = convertFqnToDisplayFormat(verusId.fullyqualifiedname); + + return ( + + + copyToClipboard(verusId.identity.identityaddress, { + title: 'Address copied', + message: `${verusId.identity.identityaddress} copied to clipboard.`, + }) + } + style={{ + width: '75%', + marginTop: 16, + }}> + + {`${formattedFriendlyName} linked`} + + + + + + + + {`Your VerusID will now appear as a card in your ${coinObj.display_ticker} wallet.`} + + + + + + + ); +}; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js new file mode 100644 index 00000000..08fbc5ba --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js @@ -0,0 +1,89 @@ +import React, {useState, useCallback} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; +import {Alert} from 'react-native'; +import {setUserCoins} from '../../../../actions/actionCreators'; +import {updateVerusIdWallet} from '../../../../actions/actions/channels/verusid/dispatchers/VerusidWalletReduxManager'; +import { + clearChainLifecycle, + refreshActiveChainLifecycles, +} from '../../../../actions/actions/intervals/dispatchers/lifecycleManager'; +import {linkVerusId} from '../../../../actions/actions/services/dispatchers/verusid/verusid'; +import { + SEND_MODAL_FORM_STEP_FORM, + SEND_MODAL_FORM_STEP_RESULT, +} from '../../../../utils/constants/sendModal'; +import {convertFqnToDisplayFormat} from '../../../../utils/fullyqualifiedname'; +import {RevokeIdentityConfirmRender} from './RevokeIdentityConfirm.render'; + +const RevokeIdentityConfirm = props => { + const [verusId, setVerusId] = useState(props.route.params.targetId); + const [friendlyNames, setFriendlyNames] = useState( + props.route.params.friendlyNames, + ); + + const dispatch = useDispatch(); + const sendModal = useSelector(state => state.sendModal); + const activeAccount = useSelector( + state => state.authentication.activeAccount, + ); + const activeCoinList = useSelector(state => state.coins.activeCoinList); + + const goBack = useCallback(() => { + props.setModalHeight(); + props.navigation.navigate(SEND_MODAL_FORM_STEP_FORM); + }, [props]); + + const submitData = useCallback(async () => { + await props.setLoading(true); + await props.setPreventExit(true); + + try { + const {identityaddress} = verusId.identity; + const {coinObj} = sendModal; + + await linkVerusId( + identityaddress, + convertFqnToDisplayFormat(verusId.fullyqualifiedname), + coinObj.id, + ); + + await updateVerusIdWallet(); + clearChainLifecycle(coinObj.id); + const setUserCoinsAction = setUserCoins(activeCoinList, activeAccount.id); + dispatch(setUserCoinsAction); + + refreshActiveChainLifecycles( + setUserCoinsAction.payload.activeCoinsForUser, + ); + + props.navigation.navigate(SEND_MODAL_FORM_STEP_RESULT, { + verusId, + friendlyNames, + }); + } catch (e) { + Alert.alert('Error', e.message); + } + + props.setPreventExit(false); + props.setLoading(false); + }, [ + verusId, + friendlyNames, + sendModal, + activeAccount, + activeCoinList, + dispatch, + props, + ]); + + return RevokeIdentityConfirmRender({ + verusId, + friendlyNames, + goBack, + submitData, + revocableByUser: !!props.route.params.revocableByUser, + ownedAddress: props.route.params.ownedAddress || '', + }); +}; + +export default RevokeIdentityConfirm; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js new file mode 100644 index 00000000..ba6be9f0 --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { ScrollView, View, SafeAreaView } from 'react-native'; +import { Button } from 'react-native-paper'; +import Colors from '../../../../globals/colors'; +import Styles from '../../../../styles'; +import VerusIdObjectData from '../../../VerusIdObjectData'; + +export const RevokeIdentityConfirmRender = ({ verusId, friendlyNames, goBack, submitData, ownedByUser, ownedAddress }) => { + return ( + + + + + + } + /> + + ); +}; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js new file mode 100644 index 00000000..4e7a3784 --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js @@ -0,0 +1,141 @@ +import {useCallback} from 'react'; +import {useSelector, useDispatch} from 'react-redux'; +import {fromBase58Check} from '@bitgo/utxo-lib/dist/src/address'; +import {Alert, Dimensions} from 'react-native'; +import {createAlert} from '../../../../actions/actions/alert/dispatchers/alert'; +import { + getFriendlyNameMap, + getIdentity, +} from '../../../../utils/api/channels/verusid/callCreators'; +import {ELECTRUM} from '../../../../utils/constants/intervalConstants'; +import { + SEND_MODAL_FORM_STEP_CONFIRM, + SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, + SEND_MODAL_SYSTEM_ID, +} from '../../../../utils/constants/sendModal'; +import {deriveKeyPair} from '../../../../utils/keys'; +import {RevokeIdentityFormRender} from './RevokeIdentityForm.render'; +import { createRevokeIdentityTx } from '../../../../utils/api/channels/verusid/requests/updateIdentity'; +import { coinsList } from '../../../../utils/CoinData/CoinsList'; + +const RevokeIdentityForm = (props) => { + const { height } = Dimensions.get("window"); + const dispatch = useDispatch(); + const sendModal = useSelector(state => state.sendModal); + + const formHasError = useCallback(() => { + const {data} = sendModal; + + const identity = + data[SEND_MODAL_IDENTITY_TO_REVOKE_FIELD] != null + ? data[SEND_MODAL_IDENTITY_TO_REVOKE_FIELD].trim() + : ''; + + if (!identity || identity.length < 1) { + createAlert('Required Field', 'Identity is a required field.'); + return true; + } + + try { + fromBase58Check(identity); + } catch (e) { + if (!identity.endsWith('@')) { + createAlert( + 'Invalid Identity', + 'Identity not a valid identity handle or iAddress.', + ) + + return true; + } + } + + return false; + }, [sendModal, dispatch]); + + const getPotentialPrimaryAddresses = useCallback(async (coinObj, channel) => { + const seeds = await props.requestSeeds(); + + const seed = seeds[channel]; + + if (!seed) throw new Error("No seed found"); + + const keyObj = await deriveKeyPair(seed, coinObj, channel); + const {addresses} = keyObj; + + return addresses; + }, []); + + const submitData = useCallback(async () => { + if (formHasError()) { + return; + } + + props.setLoading(true) + + const {data} = sendModal; + + const identity = data[SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]; + + let ownedAddress = ''; + let revocableByUser = false; + + try { + const tarRes = await getIdentity(data[SEND_MODAL_SYSTEM_ID], identity); + if (tarRes.error) { + throw new Error(tarRes.error.message); + } + + const revocation = tarRes.result.identity.revocationauthority; + + const revRes = await getIdentity(data[SEND_MODAL_SYSTEM_ID], revocation); + if (revRes.error) { + throw new Error(revRes.error.message); + } + + let isInWallet = false; + const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC, ELECTRUM); + + for (const address of revRes.result.identity.primaryaddresses) { + if (addrs.includes(address)) { + isInWallet = true; + ownedAddress = address; + revocableByUser = true; + break; + } + } + + if (!isInWallet) { + throw new Error( + 'Ensure that your imported seed/key corresponds to the primary address of the VerusID set as your revocation authority.', + ); + } + + const friendlyNames = await getFriendlyNameMap(data[SEND_MODAL_SYSTEM_ID], targetId.result); + props.setModalHeight(height >= 720 ? 696 : height - 24); + + const targetIdAddr = tarRes.result.identity.identityaddress; + const revocationResult = await createRevokeIdentityTx(data[SEND_MODAL_SYSTEM_ID], targetIdAddr, addrs[0]) + + props.navigation.navigate(SEND_MODAL_FORM_STEP_CONFIRM, { + targetId: tarRes.result, + revocationId: revRes.result, + friendlyNames, + ownedAddress, + revocableByUser, + revocationResult + }) + } catch (e) { + Alert.alert('Error', e.message); + } + + props.setLoading(false) + }, [formHasError, getPotentialPrimaryAddresses, sendModal, dispatch, props]); + + return RevokeIdentityFormRender({ + submitData, + updateSendFormData: props.updateSendFormData, + formDataValue: sendModal.data[SEND_MODAL_IDENTITY_TO_REVOKE_FIELD] + }); +}; + +export default RevokeIdentityForm; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js new file mode 100644 index 00000000..514a9113 --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js @@ -0,0 +1,41 @@ +import React from "react"; +import { ScrollView, View, TouchableWithoutFeedback, Keyboard } from "react-native"; +import { TextInput, Button } from "react-native-paper"; +import Styles from "../../../../styles"; +import { SEND_MODAL_IDENTITY_TO_REVOKE_FIELD } from "../../../../utils/constants/sendModal"; + +export const RevokeIdentityFormRender = ({submitData, updateSendFormData, formDataValue}) => { + return ( + + + + + updateSendFormData(SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, text) + } + autoCapitalize={"none"} + autoCorrect={false} + /> + + + + + + + ); +}; \ No newline at end of file diff --git a/src/components/SendModal/SendModal.js b/src/components/SendModal/SendModal.js index 25c5d27c..e1966b35 100644 --- a/src/components/SendModal/SendModal.js +++ b/src/components/SendModal/SendModal.js @@ -18,6 +18,7 @@ import { ADD_PBAAS_CURRENCY_MODAL, CONVERT_OR_CROSS_CHAIN_SEND_MODAL, ADD_ERC20_TOKEN_MODAL, + REVOKE_IDENTITY_SEND_MODAL, } from "../../utils/constants/sendModal"; import { SendModalRender } from "./SendModal.render" import { DEVICE_WINDOW_HEIGHT } from "../../utils/constants/constants"; @@ -36,7 +37,8 @@ class SendModal extends Component { [AUTHENTICATE_USER_SEND_MODAL]: 442, [ADD_PBAAS_CURRENCY_MODAL]: 442, [ADD_ERC20_TOKEN_MODAL]: 442, - [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: 696 + [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: 696, + [REVOKE_IDENTITY_SEND_MODAL]: 442 }; for (const key in this.DEFAULT_MODAL_HEIGHTS) { diff --git a/src/components/SendModal/SendModal.render.js b/src/components/SendModal/SendModal.render.js index e63bef45..3166e6ea 100644 --- a/src/components/SendModal/SendModal.render.js +++ b/src/components/SendModal/SendModal.render.js @@ -11,6 +11,7 @@ import { DEPOSIT_SEND_MODAL, LINK_IDENTITY_SEND_MODAL, PROVISION_IDENTITY_SEND_MODAL, + REVOKE_IDENTITY_SEND_MODAL, SEND_MODAL_FORM_STEP_CONFIRM, SEND_MODAL_FORM_STEP_FORM, SEND_MODAL_FORM_STEP_RESULT, @@ -52,6 +53,8 @@ import ConvertOrCrossChainSendResult from "./ConvertOrCrossChainSend/ConvertOrCr import AddErc20TokenForm from "./AddErc20Token/AddErc20TokenForm/AddErc20TokenForm"; import AddErc20TokenConfirm from "./AddErc20Token/AddErc20TokenConfirm/AddErc20TokenConfirm"; import AddErc20TokenResult from "./AddErc20Token/AddErc20TokenResult/AddErc20TokenResult"; +import RevokeIdentityForm from "./RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm"; +import RevokeIdentityConfirm from "./RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm"; const TopTabs = createMaterialTopTabNavigator(); const Root = createStackNavigator(); @@ -66,7 +69,8 @@ const SEND_FORMS = { [AUTHENTICATE_USER_SEND_MODAL]: AuthenticateUserForm, [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyForm, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendForm, - [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenForm + [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenForm, + [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityForm }; const SEND_CONFIRMATION = { @@ -79,7 +83,8 @@ const SEND_CONFIRMATION = { [AUTHENTICATE_USER_SEND_MODAL]: AuthenticateUserPassword, [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyConfirm, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendConfirm, - [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenConfirm + [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenConfirm, + [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityConfirm }; const SEND_RESULTS = { @@ -92,7 +97,8 @@ const SEND_RESULTS = { [AUTHENTICATE_USER_SEND_MODAL]: AuthenticateUserResult, [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyResult, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendResult, - [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenResult + [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenResult, + [REVOKE_IDENTITY_SEND_MODAL]: LinkIdentityResult }; export const SendModalRender = function () { diff --git a/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportIntro.js b/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportIntro.js index 43fd6b9a..dbc9d035 100644 --- a/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportIntro.js +++ b/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportIntro.js @@ -4,7 +4,7 @@ import {Text, Button} from 'react-native-paper'; import {TwentyFourWordIcon, ScanQrIcon, EnterKeyIcon} from '../../../../../images/customIcons'; import Colors from '../../../../../globals/colors'; -export default function ImportIntro({navigation}) { +export default function ImportIntro({navigation, label}) { const {height} = Dimensions.get('window'); return ( @@ -29,9 +29,10 @@ export default function ImportIntro({navigation}) { color: Colors.primaryColor, fontSize: 28, fontWeight: 'bold', - marginBottom: 48 + marginBottom: 48, + maxWidth: "90%" }}> - {'Import Wallet'} + {label ? label : 'Import Wallet'} + state.keyboard.active); + const [identityTerm, setIdentityTerm] = useState(""); + const [networkSelectOpen, setNetworkSelectOpen] = useState(false); + const [selectedNetwork, setSelectedNetwork] = useState(coinsList.VRSC); + + const validate = () => { + const res = { valid: false, message: "" } + + // if (!profileName || profileName.length < 1) { + // res.message = "Please enter a profile name." + // return res + // } else if (profileName.length > 50) { + // res.message = "Please enter a profile name shorter than 50 characters." + // return res + // } else if (isDuplicateAccount(profileName)) { + // res.message = "A profile with this name already exists." + // return res + // } + + res.valid = true + return res + } + + const next = () => { + const { valid, message } = validate() + + if (!valid) createAlert("Error", message) + else { + openRevokeIdentitySendModal( + + ) + } + } + + return ( + Keyboard.dismiss()}> + + + {networkSelectOpen && ( + setSelectedNetwork(item ? item.value : coinsList.VRSC)} + data={DEFAULT_SYSTEMS.map(x => { + return { + key: x.id, + title: x.display_ticker, + description: x.display_name, + value: x + } + })} + cancel={() => setNetworkSelectOpen(false)} + /> + )} + + + + {"Enter VerusID"} + + + {`Enter the handle or i-address of the VerusID you want to ${isRecovery ? "recover" : "revoke"}, and select the blockchain that the VerusID exists on.`} + + setIdentityTerm(text)} + /> + setNetworkSelectOpen(true)}> + + + + {!isKeyboardActive && + {"Next"} + } + + + ); +} diff --git a/src/containers/RevokeRecover/RevokeRecoverSlider.js b/src/containers/RevokeRecover/RevokeRecoverSlider.js new file mode 100644 index 00000000..1df66fe6 --- /dev/null +++ b/src/containers/RevokeRecover/RevokeRecoverSlider.js @@ -0,0 +1,159 @@ +import React from 'react'; +import {View, Dimensions} from 'react-native'; +import AppIntroSlider from 'react-native-app-intro-slider'; +import {Text, Paragraph, Button} from 'react-native-paper'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import TallButton from '../../components/LargerButton'; +import Colors from '../../globals/colors'; +import { + VerusIdLogo +} from '../../images/customIcons'; +import { useState } from 'react'; +import { useEffect } from 'react'; +import { SMALL_DEVICE_HEGHT } from '../../utils/constants/constants'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; +import { NavigationActions } from '@react-navigation/compat'; + +export default function RevokeRecoverSlider({ navigation, setIsRecovery }) { + const {height} = Dimensions.get('window'); + + const [showIcons, setShowIcons] = useState(height > SMALL_DEVICE_HEGHT ? true : false); + + useEffect(() => { + if (height > SMALL_DEVICE_HEGHT) { + setShowIcons(true); + } else { + setShowIcons(false); + } + }) + + const goToImportWallet = (recovery = false) => { + setIsRecovery(recovery); + navigation.navigate('ImportWallet'); + } + + const renderSlide = (key, icon, text, title) => { + return ( + + {height >= SMALL_DEVICE_HEGHT && } + + + {title} + + + {text} + + {key === 1 && + + + + + } + + + ); + }; + + return ( + + + renderSlide(index, item.icon, item.text, item.title) + } + bottomButton={true} + onSkip={() => navigation.dispatch(NavigationActions.back())} + data={[ + { + key: 0, + title: 'Revocation/Recovery', + text: 'Here you can revoke access to a lost or compromised VerusID, or recover a revoked VerusID with a new set of keys.', + }, + { + key: 2, + title: '', + text: 'To revoke, you will need access to the seed of your revocation VerusID. To recover, you will need access to the seed of your recovery VerusID.', + }, + ]} + renderNextButton={() => { + return ( + + {'Next'} + + ); + }} + renderSkipButton={() => { + return ( + + {'Cancel'} + + ); + }} + /> + + ); +} diff --git a/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js b/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js new file mode 100644 index 00000000..86be8064 --- /dev/null +++ b/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js @@ -0,0 +1,65 @@ +import React, { useState } from 'react'; +import { createStackNavigator } from "@react-navigation/stack"; +import { defaultHeaderOptions } from '../../../utils/navigation/header'; +import RevokeRecoverSlider from '../../RevokeRecover/RevokeRecoverSlider'; +import ImportWalletStackScreens from '../../CreateWallet/Forms/ImportWallet/ImportWallet'; +import RevokeRecoverIdentityForm from '../../RevokeRecover/RevokeRecoverIdentityForm'; + +const RevokeRecoverStack = createStackNavigator(); + +const RevokeRecoverStackScreens = props => { + const [importedSeed, setImportedSeed] = useState(null); + const [isRecovery, setIsRecovery] = useState(false); + + return ( + + + {() => ( + + )} + + + + {() => ( + props.navigation.navigate("IdentityForm")} + label={`Import ${isRecovery ? "Recovery" : "Revocation"} Seed or Key`} + /> + )} + + + + {() => ( + + )} + + + ); +}; + +export default RevokeRecoverStackScreens \ No newline at end of file diff --git a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js index fa6333ed..dc8c1465 100644 --- a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js +++ b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js @@ -9,6 +9,7 @@ import SignUp from '../../SignUp/SignUp'; import {useDispatch, useSelector} from 'react-redux'; import {setDeeplinkUrl} from '../../../actions/actionCreators'; import CreateProfile from '../../Onboard/CreateProfile/CreateProfile'; +import RevokeRecoverStackScreens from '../RevokeRecoverStackScreens/RevokeRecoverStackScreens'; const SignedOutStack = createStackNavigator(); @@ -84,6 +85,14 @@ const SignedOutStackScreens = props => { headerShown: false, }} /> + + ); }; diff --git a/src/containers/Services/ServiceComponents/VerusIdService/VerusIdServiceOverview/VerusIdServiceOverview.js b/src/containers/Services/ServiceComponents/VerusIdService/VerusIdServiceOverview/VerusIdServiceOverview.js index eba10663..ddcf2756 100644 --- a/src/containers/Services/ServiceComponents/VerusIdService/VerusIdServiceOverview/VerusIdServiceOverview.js +++ b/src/containers/Services/ServiceComponents/VerusIdService/VerusIdServiceOverview/VerusIdServiceOverview.js @@ -24,7 +24,7 @@ class VerusIdServiceOverview extends Component { try { const identityObj = await this.getVerusId(chain, iAddress); - return getFriendlyNameMap(CoinDirectory.getBasicCoinObj(chain), identityObj); + return getFriendlyNameMap(CoinDirectory.getBasicCoinObj(chain).system_id, identityObj); } catch (e) { return { ['i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV']: 'VRSC', diff --git a/src/utils/api/channels/verusid/requests/getFriendlyNameMap.js b/src/utils/api/channels/verusid/requests/getFriendlyNameMap.js index 223c7cba..0515420d 100644 --- a/src/utils/api/channels/verusid/requests/getFriendlyNameMap.js +++ b/src/utils/api/channels/verusid/requests/getFriendlyNameMap.js @@ -1,7 +1,7 @@ import { convertFqnToDisplayFormat } from "../../../../fullyqualifiedname"; import { getIdentity } from "./getIdentity"; -export const getFriendlyNameMap = async (coinObj, identityObj) => { +export const getFriendlyNameMap = async (systemId, identityObj) => { let names = { ['i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV']: 'VRSC', ['iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq']: 'VRSCTEST', @@ -20,7 +20,7 @@ export const getFriendlyNameMap = async (coinObj, identityObj) => { for (const addr of iAddresses) { try { - const id = await getIdentity(coinObj.system_id, addr); + const id = await getIdentity(systemId, addr); names[addr] = convertFqnToDisplayFormat(id.result.fullyqualifiedname); } catch (e) { diff --git a/src/utils/api/channels/verusid/requests/updateIdentity.js b/src/utils/api/channels/verusid/requests/updateIdentity.js new file mode 100644 index 00000000..05ea30de --- /dev/null +++ b/src/utils/api/channels/verusid/requests/updateIdentity.js @@ -0,0 +1,83 @@ +import { toIAddress, GetIdentityResponse } from "verus-typescript-primitives"; +import { CoinDirectory } from "../../../../CoinData/CoinDirectory"; +import VrpcProvider from "../../../../vrpc/vrpcInterface" +import { IS_PBAAS } from "../../../../constants/intervalConstants"; +import { Identity, decompile, OptCCParams } from 'verus-typescript-primitives'; +import { getIdentity } from "./getIdentity"; +import { getSpendableUtxos, getTransaction, sendRawTransaction } from "../../vrpc/callCreators"; +import { networks, Transaction } from "@bitgo/utxo-lib"; + +/** + * Safely extracts an editable/serializable identity class instance from the transaction an identity came from, + * as opposed to trusting the server to return the object correctly + * @param {string} systemId + * @param {GetIdentityResponse["result"]} getIdentityResult + * @returns {Promise<{tx: string, identity: Identity}>} + */ +export const getUpdatableIdentity = async (systemId, getIdentityResult) => { + const { txid, vout } = getIdentityResult; + + const rawIdTxRes = await getTransaction(systemId, txid, 0); + + if (rawIdTxRes.error) throw new Error(rawIdTxRes.error.message); + else { + const rawIdTx = rawIdTxRes.result; + const identityTransaction = Transaction.fromHex(rawIdTx, networks.verus); + const idOutput = identityTransaction.outs[vout]; + const decomp = decompile(idOutput.script); + + if (decomp.length !== 4) throw new Error("Identity output script is not the correct length"); + if (decomp[1] !== OPS.OP_CHECKCRYPTOCONDITION) throw new Error("Identity output script does not contain a cryptocondition"); + if (decomp[3] !== OPS.OP_DROP) throw new Error("Identity output script does not contain a drop"); + + const outMaster = OptCCParams.fromChunk(decomp[0]); + const outParams = OptCCParams.fromChunk(decomp[2]); + + if (!outMaster.eval_code.eq(new BN(EVALS.EVAL_NONE))) throw new Error("Identity output script does not contain a master"); + if (!outParams.eval_code.eq(new BN(EVALS.EVAL_IDENTITY_PRIMARY))) throw new Error("Identity output script does not contain a primary identity"); + + const __identity = new Identity(); + __identity.fromBuffer(outParams.getParamObject()) + + return { tx: rawIdTxRes, identity: __identity }; + } +} + +/** + * Updates the provided identity from its current state (using the provided identities i-addr) to the + * state provided in identity + * @param {string} systemId The system id to update the id on + * @param {Identity} identity The identity to update to + * @param {string} changeAaddr The address to send the change to + * @param {string} rawIdTx The raw transaction that created the identity + * @param {number} idHeight The height of the block that the identity was created in + * @returns + */ +export const createUpdateIdentityTx = async (systemId, identity, changeAaddr, rawIdTx, idHeight) => { + const verusid = VrpcProvider.getVerusIdInterface(systemId); + const utxos = await getSpendableUtxos(systemId, systemId, [changeAaddr]); + + return verusid.createUpdateIdentityTransaction(identity, changeAaddr, rawIdTx, idHeight, utxos); +} + +export const createRevokeIdentityTx = async (systemId, iAddr, changeAaddr) => { + const verusid = VrpcProvider.getVerusIdInterface(systemId); + + const idRes = await getIdentity(systemId, iAddr); + + if (idRes.error) throw new Error(idRes.error.message); + else { + const { blockheight } = idRes.result; + const { tx, identity } = await getUpdatableIdentity(systemId, idRes.result); + const utxos = await getSpendableUtxos(systemId, systemId, [changeAaddr]); + + return verusid.createRevokeIdentityTransaction(identity, changeAaddr, tx, blockheight, utxos); + } +} + +export const pushUpdateIdentityTx = (systemId, txHex, inputs, keys) => { + const verusid = VrpcProvider.getVerusIdInterface(systemId); + const signedTx = verusid.signUpdateIdentityTransaction(txHex, inputs, keys); + + return sendRawTransaction(systemId, signedTx, inputs, keys); +} \ No newline at end of file diff --git a/src/utils/api/channels/vrpc/requests/getAddressUtxos.js b/src/utils/api/channels/vrpc/requests/getAddressUtxos.js index 4af89318..e6d7263b 100644 --- a/src/utils/api/channels/vrpc/requests/getAddressUtxos.js +++ b/src/utils/api/channels/vrpc/requests/getAddressUtxos.js @@ -1,3 +1,4 @@ +import { unpackOutput } from "@bitgo/utxo-lib/dist/src/smart_transactions"; import VrpcProvider from "../../../../vrpc/vrpcInterface" export const getAddressUtxos = (systemId, addresses, includeFriendlyNames) => { @@ -5,4 +6,31 @@ export const getAddressUtxos = (systemId, addresses, includeFriendlyNames) => { addresses, friendlynames: includeFriendlyNames, }); +} + +export const getSpendableUtxos = async (systemId, currency, addresses) => { + const utxosRes = await getAddressUtxos(systemId, addresses); + + if (utxosRes.error) throw new Error(utxosRes.error.message); + + const utxoList = [] + + utxosRes.result.forEach((inputUtxo) => { + if (inputUtxo.isspendable && + (inputUtxo.satoshis != 0 || + (inputUtxo.currencyvalues != null && Object.keys(inputUtxo.currencyvalues).includes(currency)) + )) { + const _script = Buffer.from(inputUtxo.script, 'hex') + const _value = inputUtxo.satoshis + + try { + unpackOutput({ value: _value, script: _script }, systemId, true) + utxoList.push(inputUtxo) + } catch (e) { + console.warn(e.message) + } + } + }) + + return utxoList; } \ No newline at end of file diff --git a/src/utils/api/channels/vrpc/requests/preflight.js b/src/utils/api/channels/vrpc/requests/preflight.js index 576e0903..3479491a 100644 --- a/src/utils/api/channels/vrpc/requests/preflight.js +++ b/src/utils/api/channels/vrpc/requests/preflight.js @@ -6,7 +6,7 @@ import { Transaction, networks, smarttxs } from "@bitgo/utxo-lib"; import { calculateCurrencyTransferFee } from "./calculateCurrencyTransferFee"; import { getInfo } from "./getInfo"; import { fundRawTransaction } from "./fundRawTransaction"; -import { getAddressUtxos } from "./getAddressUtxos"; +import { getAddressUtxos, getSpendableUtxos } from "./getAddressUtxos"; import { getCurrency, getIdentity } from "../../verusid/callCreators"; import { getSystemNameFromSystemId } from "../../../../CoinData/CoinData"; import { estimateConversion } from "./estimateConversion"; @@ -441,28 +441,7 @@ export const preflightCurrencyTransfer = async (coinObj, channelId, activeUser, ); } - const utxosRes = await getAddressUtxos(systemId, [source]); - - if (utxosRes.error) throw new Error(utxosRes.error.message); - - const utxoList = [] - - utxosRes.result.forEach((inputUtxo) => { - if (inputUtxo.isspendable && - (inputUtxo.satoshis != 0 || - (inputUtxo.currencyvalues != null && Object.keys(inputUtxo.currencyvalues).includes(currency)) - )) { - const _script = Buffer.from(inputUtxo.script, 'hex') - const _value = inputUtxo.satoshis - - try { - unpackOutput({ value: _value, script: _script }, systemId, true) - utxoList.push(inputUtxo) - } catch (e) { - console.warn(e.message) - } - } - }) + const utxoList = await getSpendableUtxos(systemId, currency, [source]); const fundRes = await fundRawTransaction( systemId, diff --git a/src/utils/constants/sendModal.js b/src/utils/constants/sendModal.js index 9ffd50e1..9b868c14 100644 --- a/src/utils/constants/sendModal.js +++ b/src/utils/constants/sendModal.js @@ -32,6 +32,8 @@ export const SEND_MODAL_DISABLED_INPUTS = 'SEND_MODAL_DISABLED_INPUTS' export const SEND_MODAL_CONTINUE_IMMEDIATELY = 'SEND_MODAL_CONTINUE_IMMEDIATELY' export const SEND_MODAL_STRICT_AMOUNT = 'SEND_MODAL_STRICT_AMOUNT' export const SEND_MODAL_SEND_COMPLETED = 'SEND_MODAL_SEND_COMPLETED' +export const SEND_MODAL_IDENTITY_TO_REVOKE_FIELD = 'SEND_MODAL_IDENTITY_TO_REVOKE_FIELD' +export const SEND_MODAL_SYSTEM_ID = 'SEND_MODAL_SYSTEM_ID' // Send modal types export const TRADITIONAL_CRYPTO_SEND_MODAL = 'TRADITIONAL_CRYPTO_SEND_MODAL' @@ -44,6 +46,7 @@ export const AUTHENTICATE_USER_SEND_MODAL = 'AUTHENTICATE_USER_SEND_MODAL' export const ADD_PBAAS_CURRENCY_MODAL = 'ADD_PBAAS_CURRENCY_MODAL' export const CONVERT_OR_CROSS_CHAIN_SEND_MODAL = 'CONVERT_OR_CROSS_CHAIN_SEND_MODAL' export const ADD_ERC20_TOKEN_MODAL = 'ADD_ERC20_TOKEN_MODAL' +export const REVOKE_IDENTITY_SEND_MODAL = 'REVOKE_IDENTITY_SEND_MODAL' // Form steps export const SEND_MODAL_FORM_STEP_FORM = "SEND_MODAL_FORM_STEP_FORM" diff --git a/src/utils/crypto/verifyMerkle.js b/src/utils/crypto/verifyMerkle.js index 5fd9f1b6..52911ef9 100644 --- a/src/utils/crypto/verifyMerkle.js +++ b/src/utils/crypto/verifyMerkle.js @@ -7,15 +7,6 @@ export const calculateMerkleRoot = (txid, proof, pos, txBlockHeight) => { let hash = txid; let serialized; - /* For debugging - if (__DEV__) { - console.log("Fetching root for transaction with id " + txid) - console.log("Hashes required to prove merkle root:") - console.log(proof) - console.log("Transaction index in block: " + pos) - } - */ - for (i = 0; i < proof.length; i++) { const _hashBuff = new Buffer(hash, 'hex'); const _proofBuff = new Buffer(proof[i], 'hex'); @@ -29,12 +20,6 @@ export const calculateMerkleRoot = (txid, proof, pos, txBlockHeight) => { hash = reverse(sha256(sha256(serialized))).toString('hex'); pos /= 2; } - - /* For debugging - if (__DEV__) { - console.log("Merkle root calculated is " + hash) - } - */ return { root: hash, From afdac572fec27063ef6aa7d66383e4b7d549ce70 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 20 Sep 2024 15:20:56 +0200 Subject: [PATCH 02/25] Checkpoint for VerusID revocation/recovery, add revocation modal up until confirm screen --- src/VerusMobile.js | 6 +++- src/actions/actionCreators.js | 13 +++++++- src/actions/actions/UserData.js | 6 +++- .../sendModal/dispatchers/sendModal.js | 6 ++-- .../RevokeIdentityForm/RevokeIdentityForm.js | 20 ++++++------ .../RevokeRecoverIdentityForm.js | 31 ++++++++++++++----- .../RevokeRecoverStackScreens.js | 1 + src/reducers/authentication.js | 11 +++++-- .../verusid/requests/updateIdentity.js | 15 +++------ src/utils/auth/authBox.js | 4 +++ src/utils/constants/sendModal.js | 1 + src/utils/constants/storeType.js | 1 + 12 files changed, 81 insertions(+), 34 deletions(-) diff --git a/src/VerusMobile.js b/src/VerusMobile.js index 9e47e6f3..4921cb00 100644 --- a/src/VerusMobile.js +++ b/src/VerusMobile.js @@ -17,7 +17,8 @@ import { initSettings, fetchActiveCoins, requestSeedData, - initNotifications + initNotifications, + initInstanceKey } from './actions/actionCreators'; import { initCache, @@ -40,6 +41,7 @@ import LoadingModal from "./components/LoadingModal/LoadingModal"; import { CoinDirectory } from "./utils/CoinData/CoinDirectory"; import { removeInactiveCurrencyDefinitions } from "./utils/asyncStore/currencyDefinitionStorage"; import { removeInactiveContractDefinitions } from "./utils/asyncStore/contractDefinitionStorage"; +import { initInstance } from "./utils/auth/authBox"; class VerusMobile extends React.Component { constructor(props) { @@ -110,6 +112,8 @@ class VerusMobile extends React.Component { const settingsAction = await initSettings(); this.props.dispatch(settingsAction); + this.props.dispatch(initInstanceKey(await initInstance())); + CoinDirectory.setVrpcOverrides(settingsAction.settings.generalWalletSettings ? settingsAction.settings.generalWalletSettings.vrpcOverrides : {}); await clearCachedVrpcResponses() diff --git a/src/actions/actionCreators.js b/src/actions/actionCreators.js index c70a245c..22df6a7d 100644 --- a/src/actions/actionCreators.js +++ b/src/actions/actionCreators.js @@ -77,7 +77,8 @@ import { SET_SECURE_LOADING_SUCCESS_DATA, SET_SECURE_LOADING_ERROR_DATA, CLEAR_SECURE_LOADING_DATA, - UPDATE_SESSION_KEY + UPDATE_SESSION_KEY, + INIT_INSTANCE_KEY } from "../utils/constants/storeType"; //Reducer Name: authentication @@ -117,6 +118,16 @@ export const updateSessionKey = (sessionKey) => { } } +//Reducer Name: authentication +export const initInstanceKey = (instanceKey) => { + return { + type: INIT_INSTANCE_KEY, + payload: { + instanceKey + } + } +} + //Reducer Name: authentication export const signOut = () => { return { diff --git a/src/actions/actions/UserData.js b/src/actions/actions/UserData.js index 10b0f600..0078ad62 100644 --- a/src/actions/actions/UserData.js +++ b/src/actions/actions/UserData.js @@ -275,8 +275,12 @@ export const authenticateAccount = async (account, password) => { if (seeds[seedChannel] == null) throw new Error('No seed for channel ' + seedChannel); + const decryptedSeed = decryptkey(password, seeds[seedChannel]); + + if (!decryptedSeed) throw new Error('Failed to decrypt seed for channel ' + seedChannel); + const keyObj = await deriveKeyPair( - decryptkey(password, seeds[seedChannel]), + decryptedSeed, activeCoins[i], channel, account.keyDerivationVersion == null diff --git a/src/actions/actions/sendModal/dispatchers/sendModal.js b/src/actions/actions/sendModal/dispatchers/sendModal.js index ca54a356..5256db4c 100644 --- a/src/actions/actions/sendModal/dispatchers/sendModal.js +++ b/src/actions/actions/sendModal/dispatchers/sendModal.js @@ -40,7 +40,8 @@ import { SEND_MODAL_DISABLED_INPUTS, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, REVOKE_IDENTITY_SEND_MODAL, - SEND_MODAL_SYSTEM_ID + SEND_MODAL_SYSTEM_ID, + SEND_MODAL_ENCRYPTED_IDENTITY_SEED } from '../../../../utils/constants/sendModal'; import { CLOSE_SEND_COIN_MODAL, @@ -141,7 +142,8 @@ export const openRevokeIdentitySendModal = (data) => { data == null ? { [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', - [SEND_MODAL_SYSTEM_ID]: coinsList.VRSC.system_id + [SEND_MODAL_SYSTEM_ID]: coinsList.VRSC.system_id, + [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: '' } : data, REVOKE_IDENTITY_SEND_MODAL, diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js index 4e7a3784..90e93102 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js @@ -9,6 +9,7 @@ import { } from '../../../../utils/api/channels/verusid/callCreators'; import {ELECTRUM} from '../../../../utils/constants/intervalConstants'; import { + SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_FORM_STEP_CONFIRM, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, SEND_MODAL_SYSTEM_ID, @@ -17,11 +18,13 @@ import {deriveKeyPair} from '../../../../utils/keys'; import {RevokeIdentityFormRender} from './RevokeIdentityForm.render'; import { createRevokeIdentityTx } from '../../../../utils/api/channels/verusid/requests/updateIdentity'; import { coinsList } from '../../../../utils/CoinData/CoinsList'; +import { decryptkey } from '../../../../utils/seedCrypt'; const RevokeIdentityForm = (props) => { const { height } = Dimensions.get("window"); const dispatch = useDispatch(); const sendModal = useSelector(state => state.sendModal); + const instanceKey = useSelector(state => state.authentication.instanceKey); const formHasError = useCallback(() => { const {data} = sendModal; @@ -52,14 +55,13 @@ const RevokeIdentityForm = (props) => { return false; }, [sendModal, dispatch]); - const getPotentialPrimaryAddresses = useCallback(async (coinObj, channel) => { - const seeds = await props.requestSeeds(); + const getPotentialPrimaryAddresses = useCallback(async (coinObj) => { + const encryptedSeed = sendModal.data[SEND_MODAL_ENCRYPTED_IDENTITY_SEED]; + const seed = decryptkey(instanceKey, encryptedSeed); - const seed = seeds[channel]; + if (!seed) throw new Error("Unable to decrypt seed"); - if (!seed) throw new Error("No seed found"); - - const keyObj = await deriveKeyPair(seed, coinObj, channel); + const keyObj = await deriveKeyPair(seed, coinObj, ELECTRUM); const {addresses} = keyObj; return addresses; @@ -93,7 +95,7 @@ const RevokeIdentityForm = (props) => { } let isInWallet = false; - const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC, ELECTRUM); + const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC); for (const address of revRes.result.identity.primaryaddresses) { if (addrs.includes(address)) { @@ -110,12 +112,12 @@ const RevokeIdentityForm = (props) => { ); } - const friendlyNames = await getFriendlyNameMap(data[SEND_MODAL_SYSTEM_ID], targetId.result); - props.setModalHeight(height >= 720 ? 696 : height - 24); + const friendlyNames = await getFriendlyNameMap(data[SEND_MODAL_SYSTEM_ID], tarRes.result); const targetIdAddr = tarRes.result.identity.identityaddress; const revocationResult = await createRevokeIdentityTx(data[SEND_MODAL_SYSTEM_ID], targetIdAddr, addrs[0]) + props.setModalHeight(height >= 720 ? 696 : height - 24); props.navigation.navigate(SEND_MODAL_FORM_STEP_CONFIRM, { targetId: tarRes.result, revocationId: revRes.result, diff --git a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js index bdde9ee0..5f242387 100644 --- a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js +++ b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js @@ -9,8 +9,10 @@ import { SMALL_DEVICE_HEGHT } from '../../utils/constants/constants'; import { openRevokeIdentitySendModal } from '../../actions/actions/sendModal/dispatchers/sendModal'; import { coinsList } from '../../utils/CoinData/CoinsList'; import ListSelectionModal from '../../components/ListSelectionModal/ListSelectionModal'; +import { SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, SEND_MODAL_SYSTEM_ID } from '../../utils/constants/sendModal'; +import { encryptkey } from '../../utils/seedCrypt'; -export default function RevokeRecoverIdentityForm({ navigation, isRecovery }) { +export default function RevokeRecoverIdentityForm({ navigation, isRecovery, importedSeed }) { const DEFAULT_SYSTEMS = [coinsList.VRSC, coinsList.iExBJfZYK7KREDpuhj6PzZBzqMAKaFg7d2, coinsList.VRSCTEST]; const {height} = Dimensions.get('window'); @@ -18,6 +20,8 @@ export default function RevokeRecoverIdentityForm({ navigation, isRecovery }) { const [identityTerm, setIdentityTerm] = useState(""); const [networkSelectOpen, setNetworkSelectOpen] = useState(false); const [selectedNetwork, setSelectedNetwork] = useState(coinsList.VRSC); + const [loading, setLoading] = useState(false); + const instanceKey = useSelector(state => state.authentication.instanceKey); const validate = () => { const res = { valid: false, message: "" } @@ -37,14 +41,25 @@ export default function RevokeRecoverIdentityForm({ navigation, isRecovery }) { return res } - const next = () => { - const { valid, message } = validate() + const next = async () => { + const { valid, message } = validate(); - if (!valid) createAlert("Error", message) - else { - openRevokeIdentitySendModal( - - ) + if (!valid) createAlert("Error", message); + else if (!loading) { + try { + setLoading(true); + + openRevokeIdentitySendModal({ + [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', + [SEND_MODAL_SYSTEM_ID]: coinsList.VRSC.system_id, + [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: await encryptkey(instanceKey, importedSeed) + }); + + setLoading(false); + } catch(e) { + setLoading(false); + createAlert("Error", e.message); + } } } diff --git a/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js b/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js index 86be8064..9ab061f3 100644 --- a/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js +++ b/src/containers/RootStack/RevokeRecoverStackScreens/RevokeRecoverStackScreens.js @@ -55,6 +55,7 @@ const RevokeRecoverStackScreens = props => { )} diff --git a/src/reducers/authentication.js b/src/reducers/authentication.js index f75ad0f4..026c24c1 100644 --- a/src/reducers/authentication.js +++ b/src/reducers/authentication.js @@ -18,7 +18,8 @@ import { OPEN_SEND_COIN_MODAL, UPDATE_ACCOUNT_DISABLED_SERVICES, UPDATE_ACCOUNT_TESTNET_OVERRIDES_COMPLETE, - UPDATE_SESSION_KEY + UPDATE_SESSION_KEY, + INIT_INSTANCE_KEY } from "../utils/constants/storeType"; import { SERVICES_DISABLED_DEFAULT @@ -27,7 +28,8 @@ import { export const authentication = ( state = { accounts: [], - sessionKey: null, + sessionKey: null, // The session key should be set every time a user logs in + instanceKey: null, // The instance key should be set once per app instance and never changed until app restart activeAccount: { id: null, accountHash: null, @@ -46,6 +48,11 @@ export const authentication = ( action ) => { switch (action.type) { + case INIT_INSTANCE_KEY: + return { + ...state, + instanceKey: state.instanceKey == null ? action.payload.instanceKey : state.instanceKey + }; case OPEN_SEND_COIN_MODAL: return { ...state, diff --git a/src/utils/api/channels/verusid/requests/updateIdentity.js b/src/utils/api/channels/verusid/requests/updateIdentity.js index 05ea30de..d12ec6b6 100644 --- a/src/utils/api/channels/verusid/requests/updateIdentity.js +++ b/src/utils/api/channels/verusid/requests/updateIdentity.js @@ -1,8 +1,7 @@ -import { toIAddress, GetIdentityResponse } from "verus-typescript-primitives"; +import { decompile, GetIdentityResponse, OPS, OptCCParams, Identity, SmartTransactionScript, IdentityID } from "verus-typescript-primitives"; import { CoinDirectory } from "../../../../CoinData/CoinDirectory"; import VrpcProvider from "../../../../vrpc/vrpcInterface" import { IS_PBAAS } from "../../../../constants/intervalConstants"; -import { Identity, decompile, OptCCParams } from 'verus-typescript-primitives'; import { getIdentity } from "./getIdentity"; import { getSpendableUtxos, getTransaction, sendRawTransaction } from "../../vrpc/callCreators"; import { networks, Transaction } from "@bitgo/utxo-lib"; @@ -24,22 +23,18 @@ export const getUpdatableIdentity = async (systemId, getIdentityResult) => { const rawIdTx = rawIdTxRes.result; const identityTransaction = Transaction.fromHex(rawIdTx, networks.verus); const idOutput = identityTransaction.outs[vout]; - const decomp = decompile(idOutput.script); + const decomp = decompile(Buffer.from(idOutput.script)); if (decomp.length !== 4) throw new Error("Identity output script is not the correct length"); if (decomp[1] !== OPS.OP_CHECKCRYPTOCONDITION) throw new Error("Identity output script does not contain a cryptocondition"); if (decomp[3] !== OPS.OP_DROP) throw new Error("Identity output script does not contain a drop"); - const outMaster = OptCCParams.fromChunk(decomp[0]); - const outParams = OptCCParams.fromChunk(decomp[2]); - - if (!outMaster.eval_code.eq(new BN(EVALS.EVAL_NONE))) throw new Error("Identity output script does not contain a master"); - if (!outParams.eval_code.eq(new BN(EVALS.EVAL_IDENTITY_PRIMARY))) throw new Error("Identity output script does not contain a primary identity"); + const outParams = OptCCParams.fromChunk(Buffer.from(decomp[2])); const __identity = new Identity(); - __identity.fromBuffer(outParams.getParamObject()) + __identity.fromBuffer(outParams.getParamObject()); - return { tx: rawIdTxRes, identity: __identity }; + return { tx: rawIdTx, identity: __identity }; } } diff --git a/src/utils/auth/authBox.js b/src/utils/auth/authBox.js index 59809b11..b7a07994 100644 --- a/src/utils/auth/authBox.js +++ b/src/utils/auth/authBox.js @@ -19,6 +19,10 @@ export const initSession = async (password) => { return sessionKey } +export const initInstance = async () => { + return (await randomBytes(32)).toString('hex') +} + export const requestPassword = async () => { const state = store.getState() diff --git a/src/utils/constants/sendModal.js b/src/utils/constants/sendModal.js index 9b868c14..9a0434dd 100644 --- a/src/utils/constants/sendModal.js +++ b/src/utils/constants/sendModal.js @@ -34,6 +34,7 @@ export const SEND_MODAL_STRICT_AMOUNT = 'SEND_MODAL_STRICT_AMOUNT' export const SEND_MODAL_SEND_COMPLETED = 'SEND_MODAL_SEND_COMPLETED' export const SEND_MODAL_IDENTITY_TO_REVOKE_FIELD = 'SEND_MODAL_IDENTITY_TO_REVOKE_FIELD' export const SEND_MODAL_SYSTEM_ID = 'SEND_MODAL_SYSTEM_ID' +export const SEND_MODAL_ENCRYPTED_IDENTITY_SEED = 'SEND_MODAL_ENCRYPTED_IDENTITY_SEED' // Send modal types export const TRADITIONAL_CRYPTO_SEND_MODAL = 'TRADITIONAL_CRYPTO_SEND_MODAL' diff --git a/src/utils/constants/storeType.js b/src/utils/constants/storeType.js index 7424cea8..004af191 100644 --- a/src/utils/constants/storeType.js +++ b/src/utils/constants/storeType.js @@ -26,6 +26,7 @@ export const SIGN_IN_USER = 'SIGN_IN_USER'; export const DISABLE_SELECT_DEFAULT_ACCOUNT = 'DISABLE_SELECT_DEFAULT_ACCOUNT'; export const AUTHENTICATE_USER = 'AUTHENTICATE_USER'; export const UPDATE_SESSION_KEY = 'UPDATE_SESSION_KEY'; +export const INIT_INSTANCE_KEY = 'INIT_INSTANCE_KEY'; export const UPDATE_ACCOUNT_KEYS = 'UPDATE_ACCOUNT_KEYS'; export const SET_ADDRESSES = 'SET_ADDRESSES'; export const BIOMETRIC_AUTH = 'BIOMETRIC_AUTH'; From b93555ead72d92da33cd38bcd6b03636ea27d886 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 30 Sep 2024 12:50:02 +0200 Subject: [PATCH 03/25] Checkpoint for VerusID revocation/recovery, wrap up revocation GUI --- .../LinkIdentityResult/LinkIdentityResult.js | 29 -------- .../RevokeIdentityConfirm.js | 68 ++++++++++------- .../RevokeIdentityConfirm.render.js | 4 +- .../RevokeIdentityForm/RevokeIdentityForm.js | 26 ++++++- .../RevokeIdentityForm.render.js | 12 ++- .../RevokeIdentityResult.js | 47 ++++++++++++ .../RevokeIdentityResult.render.js} | 56 +++++++++++--- src/components/SendModal/SendModal.render.js | 3 +- .../Forms/ImportWallet/Forms/ImportText.js | 2 +- .../RevokeRecoverIdentityForm.js | 73 ++++++++----------- .../RevokeRecoverStackScreens.js | 4 + .../verusid/requests/updateIdentity.js | 2 +- src/utils/constants/sendModal.js | 1 + 13 files changed, 209 insertions(+), 118 deletions(-) delete mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js create mode 100644 src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js rename src/components/SendModal/RevokeIdentity/{LinkIdentityResult/LinkIdentityResult.render.js => RevokeIdentityResult/RevokeIdentityResult.render.js} (51%) diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js deleted file mode 100644 index b6bd791b..00000000 --- a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js +++ /dev/null @@ -1,29 +0,0 @@ -import {useEffect, useState} from 'react'; -import {useDispatch, useSelector} from 'react-redux'; -import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; -import {LinkIdentityResultRender} from './LinkIdentityResult.render'; - -const LinkIdentityResult = (props) => { - const [verusId, setVerusId] = useState(props.route.params == null ? {} : props.route.params.verusId); - const [friendlyNames, setFriendlyNames] = useState(props.route.params == null ? {} : props.route.params.friendlyNames); - const sendModal = useSelector(state => state.sendModal); - const {data} = sendModal; - - const finishSend = async () => { - if (data.noLogin) { - await props.updateSendFormData( - "success", - true, - ); - } - closeSendModal() - }; - - return LinkIdentityResultRender({ - verusId, - friendlyNames, - finishSend - }); -}; - -export default LinkIdentityResult; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js index 08fbc5ba..cceaf96e 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.js @@ -1,25 +1,29 @@ import React, {useState, useCallback} from 'react'; import {useDispatch, useSelector} from 'react-redux'; import {Alert} from 'react-native'; -import {setUserCoins} from '../../../../actions/actionCreators'; -import {updateVerusIdWallet} from '../../../../actions/actions/channels/verusid/dispatchers/VerusidWalletReduxManager'; -import { - clearChainLifecycle, - refreshActiveChainLifecycles, -} from '../../../../actions/actions/intervals/dispatchers/lifecycleManager'; -import {linkVerusId} from '../../../../actions/actions/services/dispatchers/verusid/verusid'; import { + SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_FORM_STEP_FORM, SEND_MODAL_FORM_STEP_RESULT, + SEND_MODAL_SYSTEM_ID, } from '../../../../utils/constants/sendModal'; -import {convertFqnToDisplayFormat} from '../../../../utils/fullyqualifiedname'; import {RevokeIdentityConfirmRender} from './RevokeIdentityConfirm.render'; +import { pushUpdateIdentityTx } from '../../../../utils/api/channels/verusid/requests/updateIdentity'; +import { decryptkey } from '../../../../utils/seedCrypt'; +import { deriveKeyPair } from '../../../../utils/keys'; +import { ELECTRUM } from '../../../../utils/constants/intervalConstants'; +import { coinsList } from '../../../../utils/CoinData/CoinsList'; const RevokeIdentityConfirm = props => { - const [verusId, setVerusId] = useState(props.route.params.targetId); + const [targetId, setTargetId] = useState(props.route.params.targetId); + const [revocationId, setRevocationId] = useState(props.route.params.revocationId); + const [ownedAddress, setOwnedAddress] = useState(props.route.params.ownedAddress); + const [revocableByUser, setRevocableByUser] = useState(props.route.params.revocableByUser); + const [revocationResult, setRevocationResult] = useState(props.route.params.revocationResult); const [friendlyNames, setFriendlyNames] = useState( props.route.params.friendlyNames, ); + const instanceKey = useSelector(state => state.authentication.instanceKey); const dispatch = useDispatch(); const sendModal = useSelector(state => state.sendModal); @@ -33,32 +37,40 @@ const RevokeIdentityConfirm = props => { props.navigation.navigate(SEND_MODAL_FORM_STEP_FORM); }, [props]); + const getSpendingKey = useCallback(async () => { + const encryptedSeed = sendModal.data[SEND_MODAL_ENCRYPTED_IDENTITY_SEED]; + const seed = decryptkey(instanceKey, encryptedSeed); + + if (!seed) throw new Error("Unable to decrypt seed"); + + const keyObj = await deriveKeyPair(seed, coinsList.VRSC, ELECTRUM); + + return keyObj.privKey; + }, []); + const submitData = useCallback(async () => { await props.setLoading(true); await props.setPreventExit(true); + const { data } = sendModal; try { - const {identityaddress} = verusId.identity; - const {coinObj} = sendModal; - - await linkVerusId( - identityaddress, - convertFqnToDisplayFormat(verusId.fullyqualifiedname), - coinObj.id, - ); + const spendingKey = await getSpendingKey(); - await updateVerusIdWallet(); - clearChainLifecycle(coinObj.id); - const setUserCoinsAction = setUserCoins(activeCoinList, activeAccount.id); - dispatch(setUserCoinsAction); + const keys = []; + for (let i = 0; i < revocationResult.utxos.length; i++) { + keys.push([spendingKey]) + } - refreshActiveChainLifecycles( - setUserCoinsAction.payload.activeCoinsForUser, - ); + const result = await pushUpdateIdentityTx(data[SEND_MODAL_SYSTEM_ID], revocationResult.hex, revocationResult.utxos, keys); + + if (result.error) { + throw new Error(result.error.message); + } props.navigation.navigate(SEND_MODAL_FORM_STEP_RESULT, { - verusId, - friendlyNames, + targetId, + revocationId, + txid: result.result }); } catch (e) { Alert.alert('Error', e.message); @@ -67,7 +79,7 @@ const RevokeIdentityConfirm = props => { props.setPreventExit(false); props.setLoading(false); }, [ - verusId, + targetId, friendlyNames, sendModal, activeAccount, @@ -77,7 +89,7 @@ const RevokeIdentityConfirm = props => { ]); return RevokeIdentityConfirmRender({ - verusId, + targetId, friendlyNames, goBack, submitData, diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js index ba6be9f0..746c81a6 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js @@ -5,11 +5,11 @@ import Colors from '../../../../globals/colors'; import Styles from '../../../../styles'; import VerusIdObjectData from '../../../VerusIdObjectData'; -export const RevokeIdentityConfirmRender = ({ verusId, friendlyNames, goBack, submitData, ownedByUser, ownedAddress }) => { +export const RevokeIdentityConfirmRender = ({ targetId, friendlyNames, goBack, submitData, ownedByUser, ownedAddress }) => { return ( { const { height } = Dimensions.get("window"); const dispatch = useDispatch(); const sendModal = useSelector(state => state.sendModal); const instanceKey = useSelector(state => state.authentication.instanceKey); + const [networkName, setNetworkName] = useState(sendModal.data[SEND_MODAL_SYSTEM_ID]); + + useEffect(() => { + try { + const systemObj = CoinDirectory.findSystemCoinObj(sendModal.data[SEND_MODAL_SYSTEM_ID]); + setNetworkName(systemObj.display_name); + } catch(e) {} + }, []) const formHasError = useCallback(() => { const {data} = sendModal; @@ -87,6 +96,12 @@ const RevokeIdentityForm = (props) => { throw new Error(tarRes.error.message); } + if (tarRes.result.status === "revoked") { + throw new Error( + 'Cannot revoke VerusID that is already revoked.', + ); + } + const revocation = tarRes.result.identity.revocationauthority; const revRes = await getIdentity(data[SEND_MODAL_SYSTEM_ID], revocation); @@ -94,6 +109,12 @@ const RevokeIdentityForm = (props) => { throw new Error(revRes.error.message); } + if (revRes.result.status !== "active") { + throw new Error( + 'Revocation identity is not active, and therefore unable to sign transactions.', + ); + } + let isInWallet = false; const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC); @@ -136,7 +157,8 @@ const RevokeIdentityForm = (props) => { return RevokeIdentityFormRender({ submitData, updateSendFormData: props.updateSendFormData, - formDataValue: sendModal.data[SEND_MODAL_IDENTITY_TO_REVOKE_FIELD] + formDataValue: sendModal.data[SEND_MODAL_IDENTITY_TO_REVOKE_FIELD], + networkName }); }; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js index 514a9113..fa5fe915 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js @@ -1,10 +1,11 @@ import React from "react"; import { ScrollView, View, TouchableWithoutFeedback, Keyboard } from "react-native"; -import { TextInput, Button } from "react-native-paper"; +import { TextInput, Button, Paragraph } from "react-native-paper"; import Styles from "../../../../styles"; import { SEND_MODAL_IDENTITY_TO_REVOKE_FIELD } from "../../../../utils/constants/sendModal"; +import Colors from "../../../../globals/colors"; -export const RevokeIdentityFormRender = ({submitData, updateSendFormData, formDataValue}) => { +export const RevokeIdentityFormRender = ({submitData, updateSendFormData, formDataValue, networkName}) => { return ( @@ -29,10 +30,13 @@ export const RevokeIdentityFormRender = ({submitData, updateSendFormData, formDa autoCapitalize={"none"} autoCorrect={false} /> + + {`${networkName} blockchain`} + diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js new file mode 100644 index 00000000..8f1c4f7e --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js @@ -0,0 +1,47 @@ +import {useEffect, useState} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; +import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; +import { RevokeIdentityResultRender } from './RevokeIdentityResult.render'; +import { SEND_MODAL_REVOCATION_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../../../utils/constants/sendModal'; +import { CoinDirectory } from '../../../../utils/CoinData/CoinDirectory'; +import { explorers } from '../../../../utils/CoinData/CoinData'; +import { openUrl } from '../../../../utils/linking'; + +const RevokeIdentityResult = (props) => { + const sendModal = useSelector(state => state.sendModal); + const {data} = sendModal; + const [targetId, setTargetId] = useState(props.route.params == null ? {} : props.route.params.targetId); + const [txid, setTxid] = useState(props.route.params.txid); + const [networkObj, setNetworkObj] = useState(null); + + useEffect(() => { + try { + const systemObj = CoinDirectory.findSystemCoinObj(data[SEND_MODAL_SYSTEM_ID]); + setNetworkObj(systemObj); + } catch(e) {} + }, []) + + const finishSend = async () => { + await props.updateSendFormData( + SEND_MODAL_REVOCATION_COMPLETE, + true, + ); + closeSendModal() + }; + + const openExplorer = () => { + const url = `${explorers[networkObj.id]}/tx/${txid}`; + + openUrl(url); + }; + + return RevokeIdentityResultRender({ + targetId, + networkObj, + finishSend, + txid, + openExplorer + }); +}; + +export default RevokeIdentityResult; diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.render.js similarity index 51% rename from src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js rename to src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.render.js index 78c55379..2e711054 100644 --- a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.render.js @@ -5,12 +5,11 @@ import Colors from '../../../../globals/colors'; import Styles from '../../../../styles'; import {copyToClipboard} from '../../../../utils/clipboard/clipboard'; import AnimatedSuccessCheckmark from '../../../AnimatedSuccessCheckmark'; -import { useSelector } from 'react-redux'; import { convertFqnToDisplayFormat } from '../../../../utils/fullyqualifiedname'; +import { explorers } from '../../../../utils/CoinData/CoinData'; -export const LinkIdentityResultRender = ({verusId, finishSend}) => { - const coinObj = useSelector(state => state.sendModal.coinObj); - const formattedFriendlyName = convertFqnToDisplayFormat(verusId.fullyqualifiedname); +export const RevokeIdentityResultRender = ({targetId, networkObj, finishSend, txid, openExplorer}) => { + const formattedFriendlyName = convertFqnToDisplayFormat(targetId.fullyqualifiedname); return ( { }}> - copyToClipboard(verusId.identity.identityaddress, { + copyToClipboard(targetId.identity.identityaddress, { title: 'Address copied', - message: `${verusId.identity.identityaddress} copied to clipboard.`, + message: `${targetId.identity.identityaddress} copied to clipboard.`, }) } style={{ @@ -37,7 +36,7 @@ export const LinkIdentityResultRender = ({verusId, finishSend}) => { fontSize: 20, color: Colors.verusDarkGray, }}> - {`${formattedFriendlyName} linked`} + {`${formattedFriendlyName} revoked!`} @@ -47,14 +46,24 @@ export const LinkIdentityResultRender = ({verusId, finishSend}) => { }} /> - + - {`Your VerusID will now appear as a card in your ${coinObj.display_ticker} wallet.`} + {`Your VerusID has been revoked on the ${networkObj ? networkObj.display_name : "???"} blockchain.`} + + + + + {'This action may take a few minutes to confirm on-chain.'} { flexDirection: 'row', justifyContent: 'space-evenly', }}> + {networkObj != null && explorers[networkObj.id] != null && ( + + )} + + copyToClipboard(txid, { + title: 'Transaction ID copied', + message: `${txid} copied to clipboard.`, + }) + } + style={{ + width: '75%', + }}> + + {`id: ${txid}`} + + ); }; diff --git a/src/components/SendModal/SendModal.render.js b/src/components/SendModal/SendModal.render.js index 3166e6ea..ff0c6f8f 100644 --- a/src/components/SendModal/SendModal.render.js +++ b/src/components/SendModal/SendModal.render.js @@ -55,6 +55,7 @@ import AddErc20TokenConfirm from "./AddErc20Token/AddErc20TokenConfirm/AddErc20T import AddErc20TokenResult from "./AddErc20Token/AddErc20TokenResult/AddErc20TokenResult"; import RevokeIdentityForm from "./RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm"; import RevokeIdentityConfirm from "./RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm"; +import RevokeIdentityResult from "./RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult"; const TopTabs = createMaterialTopTabNavigator(); const Root = createStackNavigator(); @@ -98,7 +99,7 @@ const SEND_RESULTS = { [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyResult, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendResult, [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenResult, - [REVOKE_IDENTITY_SEND_MODAL]: LinkIdentityResult + [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityResult }; export const SendModalRender = function () { diff --git a/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportText.js b/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportText.js index f2fc2114..3715f3c6 100644 --- a/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportText.js +++ b/src/containers/CreateWallet/Forms/ImportWallet/Forms/ImportText.js @@ -77,7 +77,7 @@ export default function ImportText({ width: 280, }}> { - "Enter your key or mnemonic seed into the text box below, then press 'import' to create your wallet." + "Enter your key or mnemonic seed into the text box below, then press 'import'." } diff --git a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js index 5f242387..f408839b 100644 --- a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js +++ b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import {View, Dimensions, TouchableWithoutFeedback, Keyboard, TouchableOpacity} from 'react-native'; import {Text, Paragraph, TextInput, Portal} from 'react-native-paper'; import { useSelector } from 'react-redux'; @@ -9,34 +9,40 @@ import { SMALL_DEVICE_HEGHT } from '../../utils/constants/constants'; import { openRevokeIdentitySendModal } from '../../actions/actions/sendModal/dispatchers/sendModal'; import { coinsList } from '../../utils/CoinData/CoinsList'; import ListSelectionModal from '../../components/ListSelectionModal/ListSelectionModal'; -import { SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, SEND_MODAL_SYSTEM_ID } from '../../utils/constants/sendModal'; +import { SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, SEND_MODAL_REVOCATION_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../utils/constants/sendModal'; import { encryptkey } from '../../utils/seedCrypt'; -export default function RevokeRecoverIdentityForm({ navigation, isRecovery, importedSeed }) { +export default function RevokeRecoverIdentityForm({ navigation, isRecovery, importedSeed, exitRevokeRecover }) { const DEFAULT_SYSTEMS = [coinsList.VRSC, coinsList.iExBJfZYK7KREDpuhj6PzZBzqMAKaFg7d2, coinsList.VRSCTEST]; const {height} = Dimensions.get('window'); const isKeyboardActive = useSelector(state => state.keyboard.active); - const [identityTerm, setIdentityTerm] = useState(""); const [networkSelectOpen, setNetworkSelectOpen] = useState(false); const [selectedNetwork, setSelectedNetwork] = useState(coinsList.VRSC); const [loading, setLoading] = useState(false); const instanceKey = useSelector(state => state.authentication.instanceKey); + const revocationComplete = useSelector(state => state.sendModal.data[SEND_MODAL_REVOCATION_COMPLETE]); + const sendModalVisible = useSelector(state => state.sendModal.visible); + const [lastSendModalVisible, setLastSendModalVisible] = useState(true); + const [hasRevocationCompleted, setHasRevocationCompleted] = useState(false); + + useEffect(() => { + if (revocationComplete && !hasRevocationCompleted) { + setHasRevocationCompleted(true); + } + }, [revocationComplete]) + + useEffect(() => { + if (!isRecovery && !sendModalVisible && lastSendModalVisible && hasRevocationCompleted) { + setLastSendModalVisible(false) + exitRevokeRecover() + } + }, [sendModalVisible]) + const validate = () => { const res = { valid: false, message: "" } - // if (!profileName || profileName.length < 1) { - // res.message = "Please enter a profile name." - // return res - // } else if (profileName.length > 50) { - // res.message = "Please enter a profile name shorter than 50 characters." - // return res - // } else if (isDuplicateAccount(profileName)) { - // res.message = "A profile with this name already exists." - // return res - // } - res.valid = true return res } @@ -51,7 +57,7 @@ export default function RevokeRecoverIdentityForm({ navigation, isRecovery, impo openRevokeIdentitySendModal({ [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', - [SEND_MODAL_SYSTEM_ID]: coinsList.VRSC.system_id, + [SEND_MODAL_SYSTEM_ID]: selectedNetwork.system_id, [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: await encryptkey(instanceKey, importedSeed) }); @@ -106,31 +112,8 @@ export default function RevokeRecoverIdentityForm({ navigation, isRecovery, impo fontSize: 28, fontWeight: 'bold', }}> - {"Enter VerusID"} + {"Select Blockchain"} - - {`Enter the handle or i-address of the VerusID you want to ${isRecovery ? "recover" : "revoke"}, and select the blockchain that the VerusID exists on.`} - - setIdentityTerm(text)} - /> setNetworkSelectOpen(true)}> + + {`Select the blockchain you want to ${isRecovery ? "recover" : "revoke"} your VerusID on, then press next. Keep in mind, if your identity has been exported to other blockchains, you will need to revoke/recover them separately.`} + {!isKeyboardActive && { const [importedSeed, setImportedSeed] = useState(null); const [isRecovery, setIsRecovery] = useState(false); + const exitRevokeRecover = () => props.navigation.dispatch(NavigationActions.back()) + return ( { navigation={props.navigation} isRecovery={isRecovery} importedSeed={importedSeed} + exitRevokeRecover={exitRevokeRecover} /> )} diff --git a/src/utils/api/channels/verusid/requests/updateIdentity.js b/src/utils/api/channels/verusid/requests/updateIdentity.js index d12ec6b6..ddf65637 100644 --- a/src/utils/api/channels/verusid/requests/updateIdentity.js +++ b/src/utils/api/channels/verusid/requests/updateIdentity.js @@ -74,5 +74,5 @@ export const pushUpdateIdentityTx = (systemId, txHex, inputs, keys) => { const verusid = VrpcProvider.getVerusIdInterface(systemId); const signedTx = verusid.signUpdateIdentityTransaction(txHex, inputs, keys); - return sendRawTransaction(systemId, signedTx, inputs, keys); + return sendRawTransaction(systemId, signedTx); } \ No newline at end of file diff --git a/src/utils/constants/sendModal.js b/src/utils/constants/sendModal.js index 9a0434dd..579d91b9 100644 --- a/src/utils/constants/sendModal.js +++ b/src/utils/constants/sendModal.js @@ -35,6 +35,7 @@ export const SEND_MODAL_SEND_COMPLETED = 'SEND_MODAL_SEND_COMPLETED' export const SEND_MODAL_IDENTITY_TO_REVOKE_FIELD = 'SEND_MODAL_IDENTITY_TO_REVOKE_FIELD' export const SEND_MODAL_SYSTEM_ID = 'SEND_MODAL_SYSTEM_ID' export const SEND_MODAL_ENCRYPTED_IDENTITY_SEED = 'SEND_MODAL_ENCRYPTED_IDENTITY_SEED' +export const SEND_MODAL_REVOCATION_COMPLETE = 'SEND_MODAL_REVOCATION_COMPLETE' // Send modal types export const TRADITIONAL_CRYPTO_SEND_MODAL = 'TRADITIONAL_CRYPTO_SEND_MODAL' From ab66e12dc12f2f923cdca1d16e75a5fae6c57365 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 4 Oct 2024 16:29:20 +0200 Subject: [PATCH 04/25] Checkpoint for VerusID revocation/recovery, get recovery functionality working through recovery modal --- .../sendModal/dispatchers/sendModal.js | 20 +- .../RecoverIdentityConfirm.js | 101 ++++++++ .../RecoverIdentityConfirm.render.js | 44 ++++ .../RecoverIdentityForm.js | 238 ++++++++++++++++++ .../RecoverIdentityForm.render.js | 191 ++++++++++++++ .../RecoverIdentityResult.js | 47 ++++ .../RecoverIdentityResult.render.js | 114 +++++++++ .../RevokeIdentityForm.render.js | 2 +- .../RevokeIdentityResult.js | 4 +- src/components/SendModal/SendModal.js | 4 +- src/components/SendModal/SendModal.render.js | 13 +- .../RevokeRecoverIdentityForm.js | 35 ++- .../verusid/requests/updateIdentity.js | 48 +++- src/utils/constants/sendModal.js | 10 +- 14 files changed, 848 insertions(+), 23 deletions(-) create mode 100644 src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js create mode 100644 src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js create mode 100644 src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js create mode 100644 src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.render.js create mode 100644 src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.js create mode 100644 src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.render.js diff --git a/src/actions/actions/sendModal/dispatchers/sendModal.js b/src/actions/actions/sendModal/dispatchers/sendModal.js index 5256db4c..6c5a0db3 100644 --- a/src/actions/actions/sendModal/dispatchers/sendModal.js +++ b/src/actions/actions/sendModal/dispatchers/sendModal.js @@ -41,7 +41,8 @@ import { SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, REVOKE_IDENTITY_SEND_MODAL, SEND_MODAL_SYSTEM_ID, - SEND_MODAL_ENCRYPTED_IDENTITY_SEED + SEND_MODAL_ENCRYPTED_IDENTITY_SEED, + RECOVER_IDENTITY_SEND_MODAL } from '../../../../utils/constants/sendModal'; import { CLOSE_SEND_COIN_MODAL, @@ -151,6 +152,23 @@ export const openRevokeIdentitySendModal = (data) => { ); }; +export const openRecoverIdentitySendModal = (data) => { + openSendModal( + `Recover VerusID`, + null, + null, + data == null + ? { + [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', + [SEND_MODAL_SYSTEM_ID]: coinsList.VRSC.system_id, + [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: '' + } + : data, + RECOVER_IDENTITY_SEND_MODAL, + 'To recover a VerusID, enter the new primary address you would like your VerusID to use.', + ); +}; + export const openAddPbaasCurrencyModal = (coinObj, data) => { openSendModal( `Add Currency`, diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js new file mode 100644 index 00000000..ac8d5a36 --- /dev/null +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js @@ -0,0 +1,101 @@ +import React, {useState, useCallback} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; +import {Alert} from 'react-native'; +import { + SEND_MODAL_ENCRYPTED_IDENTITY_SEED, + SEND_MODAL_FORM_STEP_FORM, + SEND_MODAL_FORM_STEP_RESULT, + SEND_MODAL_SYSTEM_ID, +} from '../../../../utils/constants/sendModal'; +import {RecoverIdentityConfirmRender} from './RecoverIdentityConfirm.render'; +import { pushUpdateIdentityTx } from '../../../../utils/api/channels/verusid/requests/updateIdentity'; +import { decryptkey } from '../../../../utils/seedCrypt'; +import { deriveKeyPair } from '../../../../utils/keys'; +import { ELECTRUM } from '../../../../utils/constants/intervalConstants'; +import { coinsList } from '../../../../utils/CoinData/CoinsList'; + +const RecoverIdentityConfirm = props => { + const [targetId, setTargetId] = useState(props.route.params.targetId); + const [recoveryId, setRecoveryId] = useState(props.route.params.recoveryId); + const [ownedAddress, setOwnedAddress] = useState(props.route.params.ownedAddress); + const [recoverableByUser, setRecoverableByUser] = useState(props.route.params.recoverableByUser); + const [recoveryResult, setRecoveryResult] = useState(props.route.params.recoveryResult); + const [friendlyNames, setFriendlyNames] = useState( + props.route.params.friendlyNames, + ); + const instanceKey = useSelector(state => state.authentication.instanceKey); + + const dispatch = useDispatch(); + const sendModal = useSelector(state => state.sendModal); + const activeAccount = useSelector( + state => state.authentication.activeAccount, + ); + const activeCoinList = useSelector(state => state.coins.activeCoinList); + + const goBack = useCallback(() => { + props.setModalHeight(); + props.navigation.navigate(SEND_MODAL_FORM_STEP_FORM); + }, [props]); + + const getSpendingKey = useCallback(async () => { + const encryptedSeed = sendModal.data[SEND_MODAL_ENCRYPTED_IDENTITY_SEED]; + const seed = decryptkey(instanceKey, encryptedSeed); + + if (!seed) throw new Error("Unable to decrypt seed"); + + const keyObj = await deriveKeyPair(seed, coinsList.VRSC, ELECTRUM); + + return keyObj.privKey; + }, []); + + const submitData = useCallback(async () => { + await props.setLoading(true); + await props.setPreventExit(true); + const { data } = sendModal; + + try { + const spendingKey = await getSpendingKey(); + + const keys = []; + for (let i = 0; i < recoveryResult.utxos.length; i++) { + keys.push([spendingKey]) + } + + const result = await pushUpdateIdentityTx(data[SEND_MODAL_SYSTEM_ID], recoveryResult.hex, recoveryResult.utxos, keys); + + if (result.error) { + throw new Error(result.error.message); + } + + props.navigation.navigate(SEND_MODAL_FORM_STEP_RESULT, { + targetId, + recoveryId, + txid: result.result + }); + } catch (e) { + Alert.alert('Error', e.message); + } + + props.setPreventExit(false); + props.setLoading(false); + }, [ + targetId, + friendlyNames, + sendModal, + activeAccount, + activeCoinList, + dispatch, + props, + ]); + + return RecoverIdentityConfirmRender({ + targetId, + friendlyNames, + goBack, + submitData, + recoverableByUser: !!props.route.params.recoverableByUser, + ownedAddress: props.route.params.ownedAddress || '', + }); +}; + +export default RecoverIdentityConfirm; diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js new file mode 100644 index 00000000..5079b9cd --- /dev/null +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { ScrollView, View, SafeAreaView } from 'react-native'; +import { Button } from 'react-native-paper'; +import Colors from '../../../../globals/colors'; +import Styles from '../../../../styles'; +import VerusIdObjectData from '../../../VerusIdObjectData'; + +export const RecoverIdentityConfirmRender = ({ targetId, friendlyNames, goBack, submitData, ownedByUser, ownedAddress }) => { + return ( + + + + + + } + /> + + ); +}; diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js new file mode 100644 index 00000000..00286654 --- /dev/null +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js @@ -0,0 +1,238 @@ +import {useCallback, useEffect, useState} from 'react'; +import {useSelector, useDispatch} from 'react-redux'; +import {fromBase58Check} from '@bitgo/utxo-lib/dist/src/address'; +import {Alert, Dimensions} from 'react-native'; +import {createAlert} from '../../../../actions/actions/alert/dispatchers/alert'; +import { + getFriendlyNameMap, + getIdentity, +} from '../../../../utils/api/channels/verusid/callCreators'; +import {ELECTRUM} from '../../../../utils/constants/intervalConstants'; +import { + SEND_MODAL_ENCRYPTED_IDENTITY_SEED, + SEND_MODAL_FORM_STEP_CONFIRM, + SEND_MODAL_IDENTITY_TO_RECOVER_FIELD, + SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD, + SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD, + SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD, + SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD, + SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS, + SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY, + SEND_MODAL_SYSTEM_ID, +} from '../../../../utils/constants/sendModal'; +import {deriveKeyPair} from '../../../../utils/keys'; +import {RecoverIdentityFormRender} from './RecoverIdentityForm.render'; +import { createRecoverIdentityTx, createRevokeIdentityTx } from '../../../../utils/api/channels/verusid/requests/updateIdentity'; +import { coinsList } from '../../../../utils/CoinData/CoinsList'; +import { decryptkey } from '../../../../utils/seedCrypt'; +import { CoinDirectory } from '../../../../utils/CoinData/CoinDirectory'; + +const RecoverIdentityForm = (props) => { + const { height } = Dimensions.get("window"); + const dispatch = useDispatch(); + const sendModal = useSelector(state => state.sendModal); + const instanceKey = useSelector(state => state.authentication.instanceKey); + const [networkName, setNetworkName] = useState(sendModal.data[SEND_MODAL_SYSTEM_ID]); + + const [scannerOpen, setScannerOpen] = useState(false); + const [scannerField, setScannerField] = useState(SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD); + + useEffect(() => { + try { + const systemObj = CoinDirectory.findSystemCoinObj(sendModal.data[SEND_MODAL_SYSTEM_ID]); + setNetworkName(systemObj.display_name); + } catch(e) {} + }, []) + + const formHasError = useCallback(() => { + const {data} = sendModal; + + const identity = + data[SEND_MODAL_IDENTITY_TO_RECOVER_FIELD] != null + ? data[SEND_MODAL_IDENTITY_TO_RECOVER_FIELD].trim() + : ''; + + if (!identity || identity.length < 1) { + createAlert('Required Field', 'Identity is a required field.'); + return true; + } + + try { + fromBase58Check(identity); + } catch (e) { + if (!identity.endsWith('@')) { + createAlert( + 'Invalid Identity', + 'Identity not a valid identity handle or iAddress.', + ) + + return true; + } + } + + return false; + }, [sendModal, dispatch]); + + const getPotentialPrimaryAddresses = useCallback(async (coinObj) => { + const encryptedSeed = sendModal.data[SEND_MODAL_ENCRYPTED_IDENTITY_SEED]; + const seed = decryptkey(instanceKey, encryptedSeed); + + if (!seed) throw new Error("Unable to decrypt seed"); + + const keyObj = await deriveKeyPair(seed, coinObj, ELECTRUM); + const {addresses} = keyObj; + + return addresses; + }, []); + + const handleScan = (e) => { + const result = e.data + setScannerOpen(false) + + if (typeof result === "string" && result.length <= 5000) { + props.updateSendFormData(scannerField, result) + } else { + Alert.alert("Error", "Unknown data in qr code") + } + }; + + const toggleScanner = (field) => { + if (scannerOpen) { + setScannerOpen(false); + props.setModalHeight(null); + } else { + setScannerField(field) + setScannerOpen(true) + props.setModalHeight(height >= 720 ? 696 : height - 24); + } + } + + const toggleEditRevocationRecovery = () => { + props.updateSendFormData(SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY, !sendModal.data[SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY]) + } + + const toggleEditZAddr = () => { + props.updateSendFormData(SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS, !sendModal.data[SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS]) + } + + const submitData = useCallback(async () => { + if (formHasError()) { + return; + } + + props.setLoading(true) + + const {data} = sendModal; + + const identity = data[SEND_MODAL_IDENTITY_TO_RECOVER_FIELD]; + + let ownedAddress = ''; + let recoverableByUser = false; + + try { + const tarRes = await getIdentity(data[SEND_MODAL_SYSTEM_ID], identity); + if (tarRes.error) { + throw new Error(tarRes.error.message); + } + + if (tarRes.result.status !== "revoked") { + throw new Error( + 'Cannot recover VerusID that is not revoked.', + ); + } + + const recovery = tarRes.result.identity.recoveryauthority; + + const recRes = await getIdentity(data[SEND_MODAL_SYSTEM_ID], recovery); + if (recRes.error) { + throw new Error(recRes.error.message); + } + + if (recRes.result.status !== "active") { + throw new Error( + 'Recovery identity is not active, and therefore unable to sign transactions.', + ); + } + + let isInWallet = false; + const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC); + + for (const address of recRes.result.identity.primaryaddresses) { + if (addrs.includes(address)) { + isInWallet = true; + ownedAddress = address; + recoverableByUser = true; + break; + } + } + + if (!isInWallet) { + throw new Error( + 'Ensure that your imported seed/key corresponds to the primary address of the VerusID set as your recovery authority.', + ); + } + + const friendlyNames = await getFriendlyNameMap(data[SEND_MODAL_SYSTEM_ID], tarRes.result); + + const targetIdAddr = tarRes.result.identity.identityaddress; + + const primaryAddr = data[SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD] != null && data[SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD].length > 0 ? + data[SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD] + : + null; + + const revocationAddr = data[SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY] && data[SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD] != null && data[SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD].length > 0 ? + data[SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD] + : + null; + + const recoveryAddr = data[SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY] && data[SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD] != null && data[SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD].length > 0 ? + data[SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD] + : + null; + + const privateAddr = data[SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS] && data[SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD] != null && data[SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD].length > 0 ? + data[SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD] + : + null; + + const recoveryResult = await createRecoverIdentityTx( + data[SEND_MODAL_SYSTEM_ID], + targetIdAddr, + recoveryAddr, + revocationAddr, + [primaryAddr], + privateAddr, + addrs[0] + ) + + props.setModalHeight(height >= 720 ? 696 : height - 24); + props.navigation.navigate(SEND_MODAL_FORM_STEP_CONFIRM, { + targetId: tarRes.result, + recoveryAddr: recRes.result, + friendlyNames, + ownedAddress, + recoverableByUser, + recoveryResult + }) + } catch (e) { + Alert.alert('Error', e.message); + } + + props.setLoading(false) + }, [formHasError, getPotentialPrimaryAddresses, sendModal, dispatch, props]); + + return RecoverIdentityFormRender({ + submitData, + updateSendFormData: props.updateSendFormData, + sendModalData: sendModal.data, + networkName, + scannerOpen, + toggleScanner, + handleScan, + toggleEditRevocationRecovery, + toggleEditZAddr + }); +}; + +export default RecoverIdentityForm; diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.render.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.render.js new file mode 100644 index 00000000..d921086f --- /dev/null +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.render.js @@ -0,0 +1,191 @@ +import React from "react"; +import { ScrollView, View, TouchableWithoutFeedback, Keyboard } from "react-native"; +import { TextInput, Button, Paragraph, Checkbox } from "react-native-paper"; +import Styles from "../../../../styles"; +import { SEND_MODAL_IDENTITY_TO_RECOVER_FIELD, SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD, SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD, SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD, SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD, SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS, SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY } from "../../../../utils/constants/sendModal"; +import Colors from "../../../../globals/colors"; +import BarcodeReader from "../../../BarcodeReader/BarcodeReader"; + +export const RecoverIdentityFormRender = ({ + submitData, + updateSendFormData, + sendModalData, + networkName, + scannerOpen, + toggleScanner, + handleScan, + toggleEditRevocationRecovery, + toggleEditZAddr +}) => { + return scannerOpen ? ( + + handleScan(e)} + button={() => ( + + )} + /> + + ) : + ( + + + + + updateSendFormData(SEND_MODAL_IDENTITY_TO_RECOVER_FIELD, text) + } + autoCapitalize={"none"} + autoCorrect={false} + dense + /> + + {`${networkName} blockchain`} + + + + + updateSendFormData(SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD, text) + } + autoCapitalize={"none"} + autoCorrect={false} + dense + style={{ + flex: 1, + }} + /> + + + + toggleEditRevocationRecovery()} + mode="android" + style={{ + width: '100%', + }} + /> + {sendModalData[SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY] && + (<> + + updateSendFormData(SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD, text) + } + autoCapitalize={"none"} + autoCorrect={false} + dense + style={{ + width: '100%', + }} + /> + + updateSendFormData(SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD, text) + } + autoCapitalize={"none"} + autoCorrect={false} + dense + style={{ + width: '100%', + }} + /> + ) + } + + + toggleEditZAddr()} + mode="android" + style={{ + width: '100%', + }} + /> + {sendModalData[SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS] && + ( + + updateSendFormData(SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD, text) + } + autoCapitalize={"none"} + autoCorrect={false} + dense + style={{ + flex: 1 + }} + /> + + ) + } + + + + + + + ); +}; \ No newline at end of file diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.js new file mode 100644 index 00000000..f521e22d --- /dev/null +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.js @@ -0,0 +1,47 @@ +import {useEffect, useState} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; +import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; +import { RecoverIdentityResultRender } from './RecoverIdentityResult.render'; +import { SEND_MODAL_REVOKE_RECOVER_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../../../utils/constants/sendModal'; +import { CoinDirectory } from '../../../../utils/CoinData/CoinDirectory'; +import { explorers } from '../../../../utils/CoinData/CoinData'; +import { openUrl } from '../../../../utils/linking'; + +const RecoverIdentityResult = (props) => { + const sendModal = useSelector(state => state.sendModal); + const {data} = sendModal; + const [targetId, setTargetId] = useState(props.route.params == null ? {} : props.route.params.targetId); + const [txid, setTxid] = useState(props.route.params.txid); + const [networkObj, setNetworkObj] = useState(null); + + useEffect(() => { + try { + const systemObj = CoinDirectory.findSystemCoinObj(data[SEND_MODAL_SYSTEM_ID]); + setNetworkObj(systemObj); + } catch(e) {} + }, []) + + const finishSend = async () => { + await props.updateSendFormData( + SEND_MODAL_REVOKE_RECOVER_COMPLETE, + true, + ); + closeSendModal() + }; + + const openExplorer = () => { + const url = `${explorers[networkObj.id]}/tx/${txid}`; + + openUrl(url); + }; + + return RecoverIdentityResultRender({ + targetId, + networkObj, + finishSend, + txid, + openExplorer + }); +}; + +export default RecoverIdentityResult; diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.render.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.render.js new file mode 100644 index 00000000..ccf63df6 --- /dev/null +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult.render.js @@ -0,0 +1,114 @@ +import React from 'react'; +import {ScrollView, View, TouchableOpacity} from 'react-native'; +import {Button, Text} from 'react-native-paper'; +import Colors from '../../../../globals/colors'; +import Styles from '../../../../styles'; +import {copyToClipboard} from '../../../../utils/clipboard/clipboard'; +import AnimatedSuccessCheckmark from '../../../AnimatedSuccessCheckmark'; +import { convertFqnToDisplayFormat } from '../../../../utils/fullyqualifiedname'; +import { explorers } from '../../../../utils/CoinData/CoinData'; + +export const RecoverIdentityResultRender = ({targetId, networkObj, finishSend, txid, openExplorer}) => { + const formattedFriendlyName = convertFqnToDisplayFormat(targetId.fullyqualifiedname); + + return ( + + + copyToClipboard(targetId.identity.identityaddress, { + title: 'Address copied', + message: `${targetId.identity.identityaddress} copied to clipboard.`, + }) + } + style={{ + width: '75%', + marginTop: 16, + }}> + + {`${formattedFriendlyName} recovered!`} + + + + + + + + {`Your VerusID has been recovered on the ${networkObj ? networkObj.display_name : "???"} blockchain.`} + + + + + {'This action may take a few minutes to confirm on-chain.'} + + + + {networkObj != null && explorers[networkObj.id] != null && ( + + )} + + + + copyToClipboard(txid, { + title: 'Transaction ID copied', + message: `${txid} copied to clipboard.`, + }) + } + style={{ + width: '75%', + }}> + + {`id: ${txid}`} + + + + ); +}; diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js index fa5fe915..cee3b011 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.render.js @@ -30,7 +30,7 @@ export const RevokeIdentityFormRender = ({submitData, updateSendFormData, formDa autoCapitalize={"none"} autoCorrect={false} /> - + {`${networkName} blockchain`} diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js index 8f1c4f7e..5fdd1600 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult.js @@ -2,7 +2,7 @@ import {useEffect, useState} from 'react'; import {useDispatch, useSelector} from 'react-redux'; import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; import { RevokeIdentityResultRender } from './RevokeIdentityResult.render'; -import { SEND_MODAL_REVOCATION_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../../../utils/constants/sendModal'; +import { SEND_MODAL_REVOKE_RECOVER_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../../../utils/constants/sendModal'; import { CoinDirectory } from '../../../../utils/CoinData/CoinDirectory'; import { explorers } from '../../../../utils/CoinData/CoinData'; import { openUrl } from '../../../../utils/linking'; @@ -23,7 +23,7 @@ const RevokeIdentityResult = (props) => { const finishSend = async () => { await props.updateSendFormData( - SEND_MODAL_REVOCATION_COMPLETE, + SEND_MODAL_REVOKE_RECOVER_COMPLETE, true, ); closeSendModal() diff --git a/src/components/SendModal/SendModal.js b/src/components/SendModal/SendModal.js index e1966b35..eb9aab80 100644 --- a/src/components/SendModal/SendModal.js +++ b/src/components/SendModal/SendModal.js @@ -19,6 +19,7 @@ import { CONVERT_OR_CROSS_CHAIN_SEND_MODAL, ADD_ERC20_TOKEN_MODAL, REVOKE_IDENTITY_SEND_MODAL, + RECOVER_IDENTITY_SEND_MODAL, } from "../../utils/constants/sendModal"; import { SendModalRender } from "./SendModal.render" import { DEVICE_WINDOW_HEIGHT } from "../../utils/constants/constants"; @@ -38,7 +39,8 @@ class SendModal extends Component { [ADD_PBAAS_CURRENCY_MODAL]: 442, [ADD_ERC20_TOKEN_MODAL]: 442, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: 696, - [REVOKE_IDENTITY_SEND_MODAL]: 442 + [REVOKE_IDENTITY_SEND_MODAL]: 442, + [RECOVER_IDENTITY_SEND_MODAL]: 624 }; for (const key in this.DEFAULT_MODAL_HEIGHTS) { diff --git a/src/components/SendModal/SendModal.render.js b/src/components/SendModal/SendModal.render.js index ff0c6f8f..67e29875 100644 --- a/src/components/SendModal/SendModal.render.js +++ b/src/components/SendModal/SendModal.render.js @@ -11,6 +11,7 @@ import { DEPOSIT_SEND_MODAL, LINK_IDENTITY_SEND_MODAL, PROVISION_IDENTITY_SEND_MODAL, + RECOVER_IDENTITY_SEND_MODAL, REVOKE_IDENTITY_SEND_MODAL, SEND_MODAL_FORM_STEP_CONFIRM, SEND_MODAL_FORM_STEP_FORM, @@ -56,6 +57,9 @@ import AddErc20TokenResult from "./AddErc20Token/AddErc20TokenResult/AddErc20Tok import RevokeIdentityForm from "./RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm"; import RevokeIdentityConfirm from "./RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm"; import RevokeIdentityResult from "./RevokeIdentity/RevokeIdentityResult/RevokeIdentityResult"; +import RecoverIdentityForm from "./RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm"; +import RecoverIdentityConfirm from "./RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm"; +import RecoverIdentityResult from "./RecoverIdentity/RecoverIdentityResult/RecoverIdentityResult"; const TopTabs = createMaterialTopTabNavigator(); const Root = createStackNavigator(); @@ -71,7 +75,8 @@ const SEND_FORMS = { [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyForm, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendForm, [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenForm, - [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityForm + [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityForm, + [RECOVER_IDENTITY_SEND_MODAL]: RecoverIdentityForm }; const SEND_CONFIRMATION = { @@ -85,7 +90,8 @@ const SEND_CONFIRMATION = { [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyConfirm, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendConfirm, [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenConfirm, - [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityConfirm + [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityConfirm, + [RECOVER_IDENTITY_SEND_MODAL]: RecoverIdentityConfirm }; const SEND_RESULTS = { @@ -99,7 +105,8 @@ const SEND_RESULTS = { [ADD_PBAAS_CURRENCY_MODAL]: AddPbaasCurrencyResult, [CONVERT_OR_CROSS_CHAIN_SEND_MODAL]: ConvertOrCrossChainSendResult, [ADD_ERC20_TOKEN_MODAL]: AddErc20TokenResult, - [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityResult + [REVOKE_IDENTITY_SEND_MODAL]: RevokeIdentityResult, + [RECOVER_IDENTITY_SEND_MODAL]: RecoverIdentityResult }; export const SendModalRender = function () { diff --git a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js index f408839b..33210de4 100644 --- a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js +++ b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js @@ -6,10 +6,10 @@ import { createAlert } from '../../actions/actions/alert/dispatchers/alert'; import TallButton from '../../components/LargerButton'; import Colors from '../../globals/colors'; import { SMALL_DEVICE_HEGHT } from '../../utils/constants/constants'; -import { openRevokeIdentitySendModal } from '../../actions/actions/sendModal/dispatchers/sendModal'; +import { openRecoverIdentitySendModal, openRevokeIdentitySendModal } from '../../actions/actions/sendModal/dispatchers/sendModal'; import { coinsList } from '../../utils/CoinData/CoinsList'; import ListSelectionModal from '../../components/ListSelectionModal/ListSelectionModal'; -import { SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, SEND_MODAL_REVOCATION_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../utils/constants/sendModal'; +import { SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD, SEND_MODAL_REVOKE_RECOVER_COMPLETE, SEND_MODAL_SYSTEM_ID } from '../../utils/constants/sendModal'; import { encryptkey } from '../../utils/seedCrypt'; export default function RevokeRecoverIdentityForm({ navigation, isRecovery, importedSeed, exitRevokeRecover }) { @@ -22,19 +22,19 @@ export default function RevokeRecoverIdentityForm({ navigation, isRecovery, impo const [loading, setLoading] = useState(false); const instanceKey = useSelector(state => state.authentication.instanceKey); - const revocationComplete = useSelector(state => state.sendModal.data[SEND_MODAL_REVOCATION_COMPLETE]); + const complete = useSelector(state => state.sendModal.data[SEND_MODAL_REVOKE_RECOVER_COMPLETE]); const sendModalVisible = useSelector(state => state.sendModal.visible); const [lastSendModalVisible, setLastSendModalVisible] = useState(true); - const [hasRevocationCompleted, setHasRevocationCompleted] = useState(false); + const [alreadyComplete, setAlreadyComplete] = useState(false); useEffect(() => { - if (revocationComplete && !hasRevocationCompleted) { - setHasRevocationCompleted(true); + if (complete && !alreadyComplete) { + setAlreadyComplete(true); } - }, [revocationComplete]) + }, [complete]) useEffect(() => { - if (!isRecovery && !sendModalVisible && lastSendModalVisible && hasRevocationCompleted) { + if (!sendModalVisible && lastSendModalVisible && alreadyComplete) { setLastSendModalVisible(false) exitRevokeRecover() } @@ -55,11 +55,20 @@ export default function RevokeRecoverIdentityForm({ navigation, isRecovery, impo try { setLoading(true); - openRevokeIdentitySendModal({ - [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', - [SEND_MODAL_SYSTEM_ID]: selectedNetwork.system_id, - [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: await encryptkey(instanceKey, importedSeed) - }); + if (isRecovery) { + openRecoverIdentitySendModal({ + [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', + [SEND_MODAL_SYSTEM_ID]: selectedNetwork.system_id, + [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: await encryptkey(instanceKey, importedSeed) + }); + } else { + openRevokeIdentitySendModal({ + [SEND_MODAL_IDENTITY_TO_REVOKE_FIELD]: '', + [SEND_MODAL_SYSTEM_ID]: selectedNetwork.system_id, + [SEND_MODAL_ENCRYPTED_IDENTITY_SEED]: await encryptkey(instanceKey, importedSeed) + }); + } + setLoading(false); } catch(e) { diff --git a/src/utils/api/channels/verusid/requests/updateIdentity.js b/src/utils/api/channels/verusid/requests/updateIdentity.js index ddf65637..3e6f580f 100644 --- a/src/utils/api/channels/verusid/requests/updateIdentity.js +++ b/src/utils/api/channels/verusid/requests/updateIdentity.js @@ -1,10 +1,11 @@ -import { decompile, GetIdentityResponse, OPS, OptCCParams, Identity, SmartTransactionScript, IdentityID } from "verus-typescript-primitives"; +import { decompile, GetIdentityResponse, OPS, OptCCParams, Identity, SmartTransactionScript, IdentityID, fromBase58Check } from "verus-typescript-primitives"; import { CoinDirectory } from "../../../../CoinData/CoinDirectory"; import VrpcProvider from "../../../../vrpc/vrpcInterface" import { IS_PBAAS } from "../../../../constants/intervalConstants"; import { getIdentity } from "./getIdentity"; import { getSpendableUtxos, getTransaction, sendRawTransaction } from "../../vrpc/callCreators"; import { networks, Transaction } from "@bitgo/utxo-lib"; +import { I_ADDRESS_VERSION } from "../../../../constants/constants"; /** * Safely extracts an editable/serializable identity class instance from the transaction an identity came from, @@ -70,6 +71,51 @@ export const createRevokeIdentityTx = async (systemId, iAddr, changeAaddr) => { } } +export const createRecoverIdentityTx = async (systemId, iAddr, recoveryAuthority, revocationAuthority, primaryAddresses, privateAddress, changeAaddr) => { + const verusid = VrpcProvider.getVerusIdInterface(systemId); + + const idRes = await getIdentity(systemId, iAddr); + + if (idRes.error) throw new Error(idRes.error.message); + else { + const { blockheight } = idRes.result; + const { tx, identity } = await getUpdatableIdentity(systemId, idRes.result); + + async function getAuthorityAddress(authString) { + let addressVersion; + try { + const { version } = fromBase58Check(authString); + addressVersion = version + } catch(e) {} + + if (addressVersion != null && addressVersion === I_ADDRESS_VERSION) { + return authString + } else { + const recRes = await getIdentity(systemId, authString); + if (recRes.error) throw new Error(recRes.error.message); + else { + const id = (await getUpdatableIdentity(systemId, recRes.result)).identity; + return id.getIdentityAddress() + } + } + } + + if (primaryAddresses != null) identity.setPrimaryAddresses(primaryAddresses); + if (privateAddress != null) identity.setPrivateAddress(privateAddress); + + if (recoveryAuthority != null) { + identity.setRecovery(await getAuthorityAddress(recoveryAuthority)) + } + + if (revocationAuthority != null) { + identity.setRevocation(await getAuthorityAddress(revocationAuthority)) + } + + const utxos = await getSpendableUtxos(systemId, systemId, [changeAaddr]); + return verusid.createRecoverIdentityTransaction(identity, changeAaddr, tx, blockheight, utxos); + } +} + export const pushUpdateIdentityTx = (systemId, txHex, inputs, keys) => { const verusid = VrpcProvider.getVerusIdInterface(systemId); const signedTx = verusid.signUpdateIdentityTransaction(txHex, inputs, keys); diff --git a/src/utils/constants/sendModal.js b/src/utils/constants/sendModal.js index 579d91b9..af4453f0 100644 --- a/src/utils/constants/sendModal.js +++ b/src/utils/constants/sendModal.js @@ -35,7 +35,14 @@ export const SEND_MODAL_SEND_COMPLETED = 'SEND_MODAL_SEND_COMPLETED' export const SEND_MODAL_IDENTITY_TO_REVOKE_FIELD = 'SEND_MODAL_IDENTITY_TO_REVOKE_FIELD' export const SEND_MODAL_SYSTEM_ID = 'SEND_MODAL_SYSTEM_ID' export const SEND_MODAL_ENCRYPTED_IDENTITY_SEED = 'SEND_MODAL_ENCRYPTED_IDENTITY_SEED' -export const SEND_MODAL_REVOCATION_COMPLETE = 'SEND_MODAL_REVOCATION_COMPLETE' +export const SEND_MODAL_REVOKE_RECOVER_COMPLETE = 'SEND_MODAL_REVOKE_RECOVER_COMPLETE' +export const SEND_MODAL_IDENTITY_TO_RECOVER_FIELD = 'SEND_MODAL_IDENTITY_TO_RECOVER_FIELD' +export const SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD = 'SEND_MODAL_PRIMARY_RECOVERY_ADDRESS_FIELD' +export const SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD = 'SEND_MODAL_NEW_REVOCATION_IDENTITY_FIELD' +export const SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD = 'SEND_MODAL_NEW_RECOVERY_IDENTITY_FIELD' +export const SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD = 'SEND_MODAL_NEW_PRIVATE_IDENTITY_ADDRESS_FIELD' +export const SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY = 'SEND_MODAL_RECOVERY_CHANGE_REVOCATION_RECOVERY' +export const SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS = 'SEND_MODAL_RECOVERY_CHANGE_PRIVATE_ADDRESS' // Send modal types export const TRADITIONAL_CRYPTO_SEND_MODAL = 'TRADITIONAL_CRYPTO_SEND_MODAL' @@ -49,6 +56,7 @@ export const ADD_PBAAS_CURRENCY_MODAL = 'ADD_PBAAS_CURRENCY_MODAL' export const CONVERT_OR_CROSS_CHAIN_SEND_MODAL = 'CONVERT_OR_CROSS_CHAIN_SEND_MODAL' export const ADD_ERC20_TOKEN_MODAL = 'ADD_ERC20_TOKEN_MODAL' export const REVOKE_IDENTITY_SEND_MODAL = 'REVOKE_IDENTITY_SEND_MODAL' +export const RECOVER_IDENTITY_SEND_MODAL = 'RECOVER_IDENTITY_SEND_MODAL' // Form steps export const SEND_MODAL_FORM_STEP_FORM = "SEND_MODAL_FORM_STEP_FORM" From b230829baf74c70efc594a146dd719d7a764d17f Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 4 Oct 2024 16:39:22 +0200 Subject: [PATCH 05/25] Update bitgojs, verusid-ts-client, verus-typescript-primitives and verusd-rpc-ts-client libraries --- yarn.lock | 75 ++++++++++++------------------------------------------- 1 file changed, 16 insertions(+), 59 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8198d362..9ae1a424 100644 --- a/yarn.lock +++ b/yarn.lock @@ -960,36 +960,10 @@ "@bitgo/blake2b-wasm" "^3.0.1" nanoassert "^2.0.0" -"@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus": +"@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c", "@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus": version "1.9.6" - resolved "git+https://github.com/VerusCoin/BitGoJS.git#ec65bc22093848378aafd024f41a4ac92f029552" - dependencies: - "@bitgo/blake2b" "3.0.1" - bech32 "0.0.3" - bigi "1.4.0" - bip32 "2.0.6" - bip66 "1.1.0" - bitcoin-ops "git+https://github.com/VerusCoin/bitcoin-ops" - bs58check "2.0.0" - create-hash "1.1.0" - create-hmac "1.1.3" - debug "~3.1.0" - ecurve "1.0.0" - merkle-lib "2.0.10" - pushdata-bitcoin "1.0.1" - randombytes "2.0.1" - safe-buffer "5.0.1" - typeforce "1.11.3" - varuint-bitcoin "1.0.4" - verus-typescript-primitives "git+https://github.com/VerusCoin/verus-typescript-primitives.git" - wif "2.0.1" - optionalDependencies: - secp256k1 "3.5.2" - -"@bitgo/utxo-lib@https://github.com/VerusCoin/BitGoJS.git#ec65bc22093848378aafd024f41a4ac92f029552": - version "1.9.6" - uid ec65bc22093848378aafd024f41a4ac92f029552 - resolved "https://github.com/VerusCoin/BitGoJS.git#ec65bc22093848378aafd024f41a4ac92f029552" + uid "8a9c08f51f3af53cd691ff5346196d54b34ae04c" + resolved "git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c" dependencies: "@bitgo/blake2b" "3.0.1" bech32 "0.0.3" @@ -2981,7 +2955,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== -axios@1.6.5: +axios@1.6.5, axios@1.7.4: version "1.6.5" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== @@ -3508,7 +3482,7 @@ bs58@^4.0.0: dependencies: base-x "^3.0.2" -bs58check@2.0.0, bs58check@2.1.2, bs58check@<3.0.0, bs58check@^1.0.6, bs58check@^2.0.0, bs58check@^2.1.1, bs58check@^2.1.2, "bs58check@https://github.com/bitcoinjs/bs58check": +bs58check@2.0.0, bs58check@2.1.2, bs58check@<3.0.0, bs58check@^1.0.6, bs58check@^2.0.0, bs58check@^2.1.1, bs58check@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== @@ -11561,22 +11535,12 @@ version_compare@0.0.3: "verus-typescript-primitives@git+https://github.com/VerusCoin/verus-typescript-primitives.git": version "1.0.0" - resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#b130230b0ba8f330972ea9466a7b152c1690dd75" + resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#c4987fb8ad7e273c670e3831ce9cc7c95da9422a" dependencies: base64url "3.0.1" bech32 "2.0.0" bn.js "5.2.1" - bs58check "https://github.com/bitcoinjs/bs58check" - create-hash "1.2.0" - -"verus-typescript-primitives@https://github.com/VerusCoin/verus-typescript-primitives.git": - version "1.0.0" - resolved "https://github.com/VerusCoin/verus-typescript-primitives.git#75d08c9763e9439569b509dbf24c3845a693d92d" - dependencies: - base64url "3.0.1" - bech32 "2.0.0" - bn.js "5.2.1" - bs58check "https://github.com/bitcoinjs/bs58check" + bs58check "2.0.0" create-hash "1.2.0" "verus-zkedid-utils@git+https://github.com/michaeltout/verus-zkedid-utils.git": @@ -11589,28 +11553,21 @@ version_compare@0.0.3: "verusd-rpc-ts-client@git+https://github.com/VerusCoin/verusd-rpc-ts-client.git": version "0.1.0" - uid d758a068e439df995b2ade9ab4947bda23f811e4 - resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#d758a068e439df995b2ade9ab4947bda23f811e4" + resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#a09e379d073b1b5f6468285a793f08da69f645db" dependencies: - axios "1.6.5" - verus-typescript-primitives "https://github.com/VerusCoin/verus-typescript-primitives.git" - -"verusd-rpc-ts-client@https://github.com/VerusCoin/verusd-rpc-ts-client.git": - version "0.1.0" - resolved "https://github.com/VerusCoin/verusd-rpc-ts-client.git#d758a068e439df995b2ade9ab4947bda23f811e4" - dependencies: - axios "1.6.5" - verus-typescript-primitives "https://github.com/VerusCoin/verus-typescript-primitives.git" + axios "1.7.4" + verus-typescript-primitives "git+https://github.com/VerusCoin/verus-typescript-primitives.git" "verusid-ts-client@git+https://github.com/VerusCoin/verusid-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#b3dd814e293a1384f10bc0431615a44dc29b932d" + resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#c67986d41e7e1c52b874beac62d47a61cd6e63b0" dependencies: - "@bitgo/utxo-lib" "https://github.com/VerusCoin/BitGoJS.git#ec65bc22093848378aafd024f41a4ac92f029552" - axios "1.6.5" + "@bitgo/utxo-lib" "git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c" + axios "1.7.4" bignumber.js "9.1.0" - verus-typescript-primitives "https://github.com/VerusCoin/verus-typescript-primitives.git" - verusd-rpc-ts-client "https://github.com/VerusCoin/verusd-rpc-ts-client.git" + bn.js "5.2.1" + verus-typescript-primitives "git+https://github.com/VerusCoin/verus-typescript-primitives.git" + verusd-rpc-ts-client "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git" vlq@^1.0.0: version "1.0.1" From 670b8949773099568cf42c9883e51768faf3f2ec Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 16 Aug 2024 14:44:06 +0200 Subject: [PATCH 06/25] Update dependencies --- package.json | 6 +++--- yarn.lock | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 84ffa72d..7d59fc8b 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@tradle/react-native-http": "2.0.0", "@xmldom/xmldom": "0.8.6", "assert": "1.1.1", - "axios": "1.6.5", + "axios": "1.7.4", "base-64": "1.0.0", "base64url": "3.0.1", "bigi": "1.4.2", @@ -161,7 +161,7 @@ "levelup": "4.1.0", "mem": "5.1.1", "handlebars": "4.7.7", - "elliptic": "6.5.4", + "elliptic": "6.5.7", "yargs-parser": "13.1.2", "minimist": "1.2.6", "debug": "2.6.9", @@ -190,7 +190,7 @@ "jszip": "3.8.0", "normalize-url": "4.5.1", "glob-parent": "5.1.2", - "axios": "1.6.5", + "axios": "1.7.4", "lodash": "4.17.21", "react-native": "0.68.5", "prompts": "2.4.2", diff --git a/yarn.lock b/yarn.lock index 9ae1a424..fca253dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2956,11 +2956,11 @@ aws4@^1.8.0: integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== axios@1.6.5, axios@1.7.4: - version "1.6.5" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" - integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== + version "1.7.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" + integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== dependencies: - follow-redirects "^1.15.4" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -4526,10 +4526,10 @@ electron-to-chromium@^1.4.411: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.421.tgz#2b8c0ef98ba00d4aef4c664933d570922da52161" integrity sha512-wZOyn3s/aQOtLGAwXMZfteQPN68kgls2wDAnYOA8kCjBvKVrW5RwmWVspxJYTqrcN7Y263XJVsC66VCIGzDO3g== -elliptic@6.5.3, elliptic@6.5.4, elliptic@^6.2.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== +elliptic@6.5.3, elliptic@6.5.4, elliptic@6.5.7, elliptic@^6.2.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -5340,7 +5340,7 @@ flow-parser@^0.121.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== -follow-redirects@1.15.4, follow-redirects@^1.15.4: +follow-redirects@1.15.4, follow-redirects@^1.15.6: version "1.15.4" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== From 25a59b81dc1fce557265e77d66c236ab7fd85dee Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 16 Aug 2024 17:59:42 +0200 Subject: [PATCH 07/25] Update dependencies to VerusCoin versions, remove tx-builder --- package.json | 26 ++-- src/utils/crypto/deprecatedTxDecoderLib.js | 148 ++++++++++++++++++++ src/utils/crypto/txDecoder.js | 6 +- yarn.lock | 150 ++++++--------------- 4 files changed, 202 insertions(+), 128 deletions(-) create mode 100644 src/utils/crypto/deprecatedTxDecoderLib.js diff --git a/package.json b/package.json index 7d59fc8b..c66177d2 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "base64url": "3.0.1", "bigi": "1.4.2", "bignumber.js": "9.0.1", - "bip39": "git+https://github.com/michaeltout/bip39.git", + "bip39": "git+https://github.com/VerusCoin/bip39.git", "bitgo-utxo-lib": "https://github.com/VerusCoin/bitgo-utxo-lib.git#react-native", "browserify-zlib": "0.1.4", "buffer": "6.0.3", @@ -47,7 +47,7 @@ "dns.js": "1.0.1", "domain-browser": "1.1.1", "ethereumjs-util": "6.0.0", - "ethers": "git+https://github.com/michaeltout/ethersjs-package.git", + "ethers": "git+https://github.com/VerusCoin/ethersjs-package.git", "events": "1.0.0", "https-browserify": "0.0.0", "immutable": "4.0.0-rc.12", @@ -66,12 +66,12 @@ "react-native-alert-async": "1.0.3", "react-native-app-intro-slider": "4.0.4", "react-native-barcode-mask": "1.2.4", - "react-native-cache": "git+https://github.com/michaeltout/react-native-cache.git", + "react-native-cache": "git+https://github.com/VerusCoin/react-native-cache.git", "react-native-camera": "3.22.1", "react-native-crypto": "2.1.2", "react-native-dialog": "5.6.0", "react-native-elements": "1.0.0", - "react-native-format-currency": "https://github.com/michaeltout/react-native-format-currency.git", + "react-native-format-currency": "https://github.com/VerusCoin/react-native-format-currency.git", "react-native-fs": "2.18.0", "react-native-gesture-handler": "2.9.0", "react-native-haptic-feedback": "1.11.0", @@ -81,7 +81,7 @@ "react-native-level-fs": "3.0.0", "react-native-loading-spinner-overlay": "1.0.1", "react-native-modal-datetime-picker": "15.0.0", - "react-native-os": "git+https://github.com/michaeltout/react-native-os.git#1f79a5ae777ff296bd4911e2eb37e1de6f711d12", + "react-native-os": "git+https://github.com/VerusCoin/react-native-os.git#1f79a5ae777ff296bd4911e2eb37e1de6f711d12", "react-native-paper": "4.7.1", "react-native-permissions": "3.1.0", "react-native-qrcode-scanner": "1.5.4", @@ -94,11 +94,11 @@ "react-native-svg": "9.13.6", "react-native-svg-transformer": "0.14.3", "react-native-tab-view": "2.x", - "react-native-tcp": "git+https://github.com/aprock/react-native-tcp.git#be5f656ffd3aa4559270e8472ee24a1c36029cf1", + "react-native-tcp": "git+https://github.com/VerusCoin/react-native-tcp.git#be5f656ffd3aa4559270e8472ee24a1c36029cf1", "react-native-text-input-mask": "3.1.4", "react-native-udp": "4.1.5", "react-native-url-polyfill": "1.3.0", - "react-native-vector-icons": "git+https://github.com/michaeltout/react-native-vector-icons.git", + "react-native-vector-icons": "git+https://github.com/VerusCoin/react-native-vector-icons.git", "react-native-verus-light-client": "git+https://github.com/VerusCoin/react-native-verus-light-client.git", "react-redux": "8.0.2", "readable-stream": "1.0.33", @@ -110,15 +110,16 @@ "string_decoder": "0.10.25", "timers-browserify": "1.0.1", "tty-browserify": "0.0.0", - "tx-builder": "0.18.0", + "typeforce": "git+https://github.com/VerusCoin/typeforce.git", "url": "0.10.1", "utf8": "3.0.0", "util": "0.10.3", "uuid": "8.0.0", + "varuint-bitcoin": "1.0.4", "version_compare": "0.0.3", - "verus-pay-utils": "git+https://github.com/michaeltout/verus-pay-utils.git", + "verus-pay-utils": "git+https://github.com/VerusCoin/verus-pay-utils.git", "verus-typescript-primitives": "git+https://github.com/VerusCoin/verus-typescript-primitives.git", - "verus-zkedid-utils": "git+https://github.com/michaeltout/verus-zkedid-utils.git", + "verus-zkedid-utils": "git+https://github.com/VerusCoin/verus-zkedid-utils.git", "verusd-rpc-ts-client": "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git", "verusid-ts-client": "git+https://github.com/VerusCoin/verusid-ts-client.git", "vm-browserify": "0.0.4", @@ -152,7 +153,6 @@ }, "resolutions": { "word-wrap": "1.2.4", - "typeforce": "git+https://github.com/michaeltout/typeforce.git", "bs58check": "2.1.2", "braces": "3.0.2", "tar": "4.4.18", @@ -220,8 +220,8 @@ "@sideway/formula": "3.0.1", "@babel/traverse": "7.23.2", "@babel/core": "7.21.3", - "blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", - "@bitgo/blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", + "blake2b-wasm": "https://github.com/VerusCoin/blake2b-wasm/", + "@bitgo/blake2b-wasm": "https://github.com/VerusCoin/blake2b-wasm/", "browserify-sign": "4.2.2" }, "jest": { diff --git a/src/utils/crypto/deprecatedTxDecoderLib.js b/src/utils/crypto/deprecatedTxDecoderLib.js new file mode 100644 index 00000000..a718ae9c --- /dev/null +++ b/src/utils/crypto/deprecatedTxDecoderLib.js @@ -0,0 +1,148 @@ +// These functions are deprecated and will be removed in the future. This file is only used +// to help decode electrum inputs/outputs and transactions, and is temporarily required in order +// to be able to remove the tx-builder dependancy. + +const varuint = require('varuint-bitcoin') +const typeforce = require('typeforce') +const bitcoin = require('bitgo-utxo-lib'); + +// readSlice :: Number -> Buffer -> (Buffer, Buffer) +const readSlice = offset => buffer => { + return [buffer.slice(0, offset), buffer.slice(offset)] +} + +// readUInt32 :: Buffer -> (Number, Buffer) +function readUInt32 (buffer) { + typeforce(typeforce.Buffer, buffer) + const i = buffer.readUInt32LE(0) + return [i, buffer.slice(4)] +} + +// readInt32 :: Buffer -> (Number, Buffer) +function readInt32 (buffer) { + typeforce(typeforce.Buffer, buffer) + const i = buffer.readInt32LE(0) + return [i, buffer.slice(4)] +} + +// readUInt64 :: Buffer -> (Number, Buffer) +function readUInt64 (buffer) { + typeforce(typeforce.Buffer, buffer) + const a = buffer.readUInt32LE(0) + let b = buffer.readUInt32LE(4) + b *= 0x100000000 + // verifuint(b + a, 0x001fffffffffffff) + return [b + a, buffer.slice(8)] +} + +// readVarInt :: Buffer -> (Res, Buffer) +function readVarInt (buffer) { + const vi = varuint.decode(buffer, 0) + return [vi, buffer.slice(varuint.decode.bytes)] +} + +// readVarSlice :: Buffer -> (Res, Buffer) +function readVarSlice (buffer) { + const [len, bufferLeft] = readVarInt(buffer) + const [res, bufferLeft2] = readSlice(len)(bufferLeft) + return [res, bufferLeft2] +} + +// compose :: [Fn] -> State -> Buffer -> [State, Buffer] +const compose = args => (state, buffer) => { + typeforce(typeforce.Array, args) + typeforce(typeforce.Object, state) + typeforce(typeforce.Buffer, buffer) + return args.reduce(([state, buffer], f) => f(state, buffer), [state, buffer]) +} + +// addProp :: PropName -> Fn -> (State -> Buffer -> [State, Buffer]) +const addProp = (propName, f) => (state, buffer) => { + typeforce(typeforce.String, propName) + typeforce(typeforce.Function, f) + typeforce(typeforce.Object, state) + typeforce(typeforce.Buffer, buffer) + const [res, bufferLeft] = f(buffer) + state[propName] = res + return [state, bufferLeft] +} + +// readInputs :: Fn -> Buffer -> (Res, Buffer) +const readInputs = readFn => buffer => { + const vins = [] + let [vinLen, bufferLeft] = readVarInt(buffer) + let vin + for (let i = 0; i < vinLen; ++i) { + [vin, bufferLeft] = readFn(bufferLeft) + vins.push(vin) + } + return [vins, bufferLeft] +} + +// readSig :: Buffer -> [ScriptSig, Buffer] +const readSig = buffer => { + const [ res, bufferLeft ] = readVarSlice(buffer) + + const [ asmPart, asmBufferLeft ] = readVarSlice(res) + const asm = [ asmPart.toString('hex') ] + const hashType = asmPart.readUInt8(asmPart.length - 1) & ~0x80 + if (hashType <= 0 || hashType >= 4) throw new Error('Invalid hashType ' + hashType) + const [ asmPart2 ] = readVarSlice(asmBufferLeft) + asm.push(asmPart2.toString('hex')) + + return [{ asm: asm.join(' '), hex: res.toString('hex') }, bufferLeft] +} + +// readScript :: Buffer -> [ScriptPubKey, Buffer] +const readScript = buffer => { + const [ scriptBuffer, bufferLeft ] = readVarSlice(buffer) + + return [ { + hex: scriptBuffer.toString('hex'), + type: scriptBuffer[0] === bitcoin.opcodes.OP_DUP && scriptBuffer[1] === bitcoin.opcodes.OP_HASH160 ? 'pubkeyhash' : 'nonstandard', + asm: bitcoin.script.toASM(scriptBuffer), + addresses: [ bitcoin.address.fromOutputScript(scriptBuffer) ] + }, bufferLeft ] +} + +/** + * Transaction's hash is a 256-bit integer, so we need to reverse bytes due to Little Endian byte order. + */ +// readHash :: Buffer -> [Hash, Buffer] +const readHash = buffer => { + const [res, bufferLeft] = readSlice(32)(buffer) + // Note: `buffer.reverse()` mutates the buffer, so make a copy: + const hash = Buffer.from(res).reverse().toString('hex') + return [hash, bufferLeft] +} + +// readInput :: Buffer -> [Res, Buffer] +const readInput = buffer => + ( + compose([ + addProp('txid', readHash), // 32 bytes, Transaction Hash + addProp('vout', readUInt32), // 4 bytes, Output Index + addProp('scriptSig', readSig), // 1-9 bytes (VarInt), Unlocking-Script Size; Variable, Unlocking-Script + addProp('sequence', readUInt32) // 4 bytes, Sequence Number + ])({}, buffer) + ) + +// readOutput :: Buffer -> [Res, Buffer] +const readOutput = buffer => +( + compose([ + addProp('value', readUInt64), // 8 bytes, Amount in satoshis + addProp('scriptPubKey', readScript) // 1-9 bytes (VarInt), Locking-Script Size; Variable, Locking-Script + ])({}, buffer) +) + +module.exports = { + readInt32, + readUInt32, + readSlice, + compose, + addProp, + readInputs, + readInput, + readOutput +}; \ No newline at end of file diff --git a/src/utils/crypto/txDecoder.js b/src/utils/crypto/txDecoder.js index 1885bfa6..d89bfdf7 100644 --- a/src/utils/crypto/txDecoder.js +++ b/src/utils/crypto/txDecoder.js @@ -34,16 +34,12 @@ const { readSlice, readInt32, readUInt32, -} = require('tx-builder/src/buffer-read'); -const { compose, addProp, -} = require('tx-builder/src/compose-read'); -const { readInputs, readInput, readOutput, -} = require('tx-builder/src/tx-decoder'); +} = require('./deprecatedTxDecoderLib'); const crypto = require('react-native-crypto'); const _sha256 = (data) => { return crypto.createHash('sha256').update(data).digest(); diff --git a/yarn.lock b/yarn.lock index fca253dc..dae2f840 100644 --- a/yarn.lock +++ b/yarn.lock @@ -946,9 +946,9 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bitgo/blake2b-wasm@^3.0.1", "@bitgo/blake2b-wasm@https://github.com/michaeltout/blake2b-wasm/", "blake2b-wasm@https://github.com/BitGo/blake2b-wasm#193cdb71656c1a6c7f89b05d0327bb9b758d071b", "blake2b-wasm@https://github.com/michaeltout/blake2b-wasm/": +"@bitgo/blake2b-wasm@^3.0.1", "@bitgo/blake2b-wasm@https://github.com/VerusCoin/blake2b-wasm/", "blake2b-wasm@https://github.com/BitGo/blake2b-wasm#193cdb71656c1a6c7f89b05d0327bb9b758d071b", "blake2b-wasm@https://github.com/VerusCoin/blake2b-wasm/": version "2.0.0" - resolved "https://github.com/michaeltout/blake2b-wasm/#a16070b30bb8d8aee670f0c69b642913eca79998" + resolved "https://github.com/VerusCoin/blake2b-wasm/#a16070b30bb8d8aee670f0c69b642913eca79998" dependencies: nanoassert "1.0.0" @@ -1216,9 +1216,9 @@ dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/providers@https://github.com/michaeltout/ethersjs-providers-package.git": +"@ethersproject/providers@https://github.com/VerusCoin/ethersjs-providers-package.git": version "5.0.5" - resolved "https://github.com/michaeltout/ethersjs-providers-package.git#9d3086fca457b0207f74cdad2c8819b2102ffc63" + resolved "https://github.com/VerusCoin/ethersjs-providers-package.git#dffe7607f48db0dc773ece418dfaf687865219ae" dependencies: "@ethersproject/abstract-provider" "^5.0.0" "@ethersproject/abstract-signer" "^5.0.0" @@ -1272,16 +1272,15 @@ "@ethersproject/properties" "^5.0.3" elliptic "6.5.3" -"@ethersproject/signing-key@^5.0.0", "@ethersproject/signing-key@^5.7.0": +"@ethersproject/signing-key@^5.7.0", "@ethersproject/signing-key@https://github.com/VerusCoin/ethersjs-signing-key-package.git": version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + resolved "https://github.com/VerusCoin/ethersjs-signing-key-package.git#602f32146d0f6c2bfd5d70f2a252b8614151a1cb" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" bn.js "^5.2.1" - elliptic "6.5.4" + elliptic "6.5.7" hash.js "1.1.7" "@ethersproject/solidity@^5.0.0": @@ -3176,11 +3175,6 @@ bech32@2.0.0: resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== -bech32@^1.1.2, bech32@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" - integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== - big-integer@1.6.x: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" @@ -3243,32 +3237,9 @@ bip32@2.0.6: typeforce "^1.11.5" wif "^2.0.6" -bip32@^1.0.2, bip32@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/bip32/-/bip32-1.0.4.tgz#188ad57a45fb1342c9aabe969d0612c704a987b4" - integrity sha512-8T21eLWylZETolyqCPgia+MNp+kY37zFr7PTFDTPObHeNi9JlfG4qGIh8WzerIJidtwoK+NsWq2I5i66YfHoIw== - dependencies: - bs58check "^2.1.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - tiny-secp256k1 "^1.0.0" - typeforce "^1.11.5" - wif "^2.0.6" - -bip39@^2.3.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.6.0.tgz#9e3a720b42ec8b3fbe4038f1e445317b6a99321c" - integrity sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg== - dependencies: - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - safe-buffer "^5.0.1" - unorm "^1.3.3" - -"bip39@git+https://github.com/michaeltout/bip39.git": +"bip39@git+https://github.com/VerusCoin/bip39.git": version "3.0.3" - resolved "git+https://github.com/michaeltout/bip39.git#17d3bbefc80d243903eabd60e0ed64f20aa2bf31" + resolved "git+https://github.com/VerusCoin/bip39.git#17d3bbefc80d243903eabd60e0ed64f20aa2bf31" dependencies: "@types/node" "11.11.6" create-hash "^1.1.0" @@ -3287,31 +3258,10 @@ bip66@^1.1.0, bip66@^1.1.3, bip66@^1.1.5: dependencies: safe-buffer "^5.0.1" -bitcoin-ops@^1.3.0, bitcoin-ops@^1.4.0, bitcoin-ops@^1.4.1, "bitcoin-ops@git+https://github.com/VerusCoin/bitcoin-ops": +bitcoin-ops@^1.3.0, "bitcoin-ops@git+https://github.com/VerusCoin/bitcoin-ops": version "1.4.1" resolved "git+https://github.com/VerusCoin/bitcoin-ops#a5f8f56e658301bc39881d41740b3a0010530efd" -bitcoinjs-lib@^4.0.1: - version "4.0.5" - resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-4.0.5.tgz#2cce3469b950fade9f0442755ebb26bf9de519a1" - integrity sha512-gYs7K2hiY4Xb96J8AIF+Rx+hqbwjVlp5Zt6L6AnHOdzfe/2tODdmDxsEytnaxVCdhOUg0JnsGpl+KowBpGLxtA== - dependencies: - bech32 "^1.1.2" - bip32 "^1.0.4" - bip66 "^1.1.0" - bitcoin-ops "^1.4.0" - bs58check "^2.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.3" - merkle-lib "^2.0.10" - pushdata-bitcoin "^1.0.1" - randombytes "^2.0.1" - safe-buffer "^5.1.1" - tiny-secp256k1 "^1.0.0" - typeforce "^1.11.3" - varuint-bitcoin "^1.0.4" - wif "^2.0.1" - "bitgo-utxo-lib@https://github.com/VerusCoin/bitgo-utxo-lib.git#react-native": version "1.2.1" resolved "https://github.com/VerusCoin/bitgo-utxo-lib.git#5765ef99b05b345d6e7db6d9b8cd5a7de0f56d18" @@ -4526,7 +4476,7 @@ electron-to-chromium@^1.4.411: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.421.tgz#2b8c0ef98ba00d4aef4c664933d570922da52161" integrity sha512-wZOyn3s/aQOtLGAwXMZfteQPN68kgls2wDAnYOA8kCjBvKVrW5RwmWVspxJYTqrcN7Y263XJVsC66VCIGzDO3g== -elliptic@6.5.3, elliptic@6.5.4, elliptic@6.5.7, elliptic@^6.2.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.3, elliptic@6.5.7, elliptic@^6.2.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.7" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== @@ -5036,9 +4986,9 @@ ethereumjs-util@6.0.0: safe-buffer "^5.1.1" secp256k1 "^3.0.1" -"ethers@git+https://github.com/michaeltout/ethersjs-package.git": +"ethers@git+https://github.com/VerusCoin/ethersjs-package.git": version "5.0.8" - resolved "git+https://github.com/michaeltout/ethersjs-package.git#cec8e071f373116182e7804278c78da7a7493f33" + resolved "git+https://github.com/VerusCoin/ethersjs-package.git#4c586d8733f71e793b33ffe5080ff1d4b023463d" dependencies: "@ethersproject/abi" "^5.0.0" "@ethersproject/abstract-provider" "^5.0.0" @@ -5058,11 +5008,11 @@ ethereumjs-util@6.0.0: "@ethersproject/networks" "^5.0.0" "@ethersproject/pbkdf2" "^5.0.0" "@ethersproject/properties" "^5.0.0" - "@ethersproject/providers" "https://github.com/michaeltout/ethersjs-providers-package.git" + "@ethersproject/providers" "https://github.com/VerusCoin/ethersjs-providers-package.git" "@ethersproject/random" "^5.0.0" "@ethersproject/rlp" "^5.0.0" "@ethersproject/sha2" "^5.0.0" - "@ethersproject/signing-key" "^5.0.0" + "@ethersproject/signing-key" "https://github.com/VerusCoin/ethersjs-signing-key-package.git" "@ethersproject/solidity" "^5.0.0" "@ethersproject/strings" "^5.0.0" "@ethersproject/transactions" "^5.0.0" @@ -6953,11 +6903,6 @@ js-sha3@0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -js-sha3@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" - integrity sha512-Wpks3yBDm0UcL5qlVhwW9Jr9n9i4FfeWBFOOXP5puDS/SiudJGhw7DPyBqn3487qD4F0lsC0q3zxink37f7zeA== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -9257,7 +9202,7 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -ramda@0.27.2, ramda@^0.24.1: +ramda@0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.2.tgz#84463226f7f36dc33592f6f4ed6374c48306c3f1" integrity sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA== @@ -9326,9 +9271,9 @@ react-native-barcode-mask@1.2.4: dependencies: prop-types "^15.6.2" -"react-native-cache@git+https://github.com/michaeltout/react-native-cache.git": +"react-native-cache@git+https://github.com/VerusCoin/react-native-cache.git": version "1.1.7" - resolved "git+https://github.com/michaeltout/react-native-cache.git#f8c1a72b50177aa93de5e8c329b1523409505dd6" + resolved "git+https://github.com/VerusCoin/react-native-cache.git#f8c1a72b50177aa93de5e8c329b1523409505dd6" dependencies: async "^2.6.1" @@ -9385,9 +9330,9 @@ react-native-elements@1.0.0: react-native-ratings "^6.3.0" react-native-status-bar-height "^2.2.0" -"react-native-format-currency@https://github.com/michaeltout/react-native-format-currency.git": +"react-native-format-currency@https://github.com/VerusCoin/react-native-format-currency.git": version "0.0.3" - resolved "https://github.com/michaeltout/react-native-format-currency.git#7002ee86a689ea52f1ff1e7fb71e216f282fad35" + resolved "https://github.com/VerusCoin/react-native-format-currency.git#7002ee86a689ea52f1ff1e7fb71e216f282fad35" react-native-fs@2.18.0: version "2.18.0" @@ -9471,9 +9416,9 @@ react-native-modal@^9.0.0: prop-types "^15.6.2" react-native-animatable "^1.2.4" -"react-native-os@git+https://github.com/michaeltout/react-native-os.git#1f79a5ae777ff296bd4911e2eb37e1de6f711d12": +"react-native-os@git+https://github.com/VerusCoin/react-native-os.git#1f79a5ae777ff296bd4911e2eb37e1de6f711d12": version "1.2.7" - resolved "git+https://github.com/michaeltout/react-native-os.git#1f79a5ae777ff296bd4911e2eb37e1de6f711d12" + resolved "git+https://github.com/VerusCoin/react-native-os.git#1f79a5ae777ff296bd4911e2eb37e1de6f711d12" react-native-paper@4.7.1: version "4.7.1" @@ -9590,9 +9535,9 @@ react-native-tab-view@2.x: resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.16.0.tgz#cae72c7084394bd328fac5fefb86cd966df37a86" integrity sha512-ac2DmT7+l13wzIFqtbfXn4wwfgtPoKzWjjZyrK1t+T8sdemuUvD4zIt+UImg03fu3s3VD8Wh/fBrIdcqQyZJWg== -"react-native-tcp@git+https://github.com/aprock/react-native-tcp.git#be5f656ffd3aa4559270e8472ee24a1c36029cf1": +"react-native-tcp@git+https://github.com/VerusCoin/react-native-tcp.git#be5f656ffd3aa4559270e8472ee24a1c36029cf1": version "4.0.0" - resolved "git+https://github.com/aprock/react-native-tcp.git#be5f656ffd3aa4559270e8472ee24a1c36029cf1" + resolved "git+https://github.com/VerusCoin/react-native-tcp.git#be5f656ffd3aa4559270e8472ee24a1c36029cf1" dependencies: base64-js "1.3.1" buffer "^5.4.3" @@ -9621,9 +9566,9 @@ react-native-url-polyfill@1.3.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -"react-native-vector-icons@git+https://github.com/michaeltout/react-native-vector-icons.git": +"react-native-vector-icons@git+https://github.com/VerusCoin/react-native-vector-icons.git": version "8.1.0" - resolved "git+https://github.com/michaeltout/react-native-vector-icons.git#8e7de391c452401b0e2bf0da695aa0edeb8b323d" + resolved "git+https://github.com/VerusCoin/react-native-vector-icons.git#8e7de391c452401b0e2bf0da695aa0edeb8b323d" dependencies: lodash "^4.17.21" prop-types "^15.7.2" @@ -11000,7 +10945,7 @@ timers-browserify@1.0.1: dependencies: process "~0.5.1" -tiny-secp256k1@^1.0.0, tiny-secp256k1@^1.1.3: +tiny-secp256k1@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c" integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA== @@ -11126,23 +11071,6 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -tx-builder@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/tx-builder/-/tx-builder-0.18.0.tgz#efa063790020a5fea63056bad0f44ef0b03d6fa4" - integrity sha512-pr6v2JNdT2KXcZm1vaPgSLg5//wrXlYTY+Tw+6O0hEL1f+cH20cmWooTop2DLWikPBN/qnrUiUzxVIRrYkDpYQ== - dependencies: - bech32 "^1.1.3" - bip32 "^1.0.2" - bip39 "^2.3.1" - bitcoin-ops "^1.4.1" - bitcoinjs-lib "^4.0.1" - bs58check "^2.1.2" - js-sha3 "^0.7.0" - ramda "^0.24.1" - safe-buffer "^5.1.1" - typeforce "^1.11.1" - varuint-bitcoin "^1.0.4" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11208,9 +11136,16 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typeforce@1.11.3, typeforce@^1.11.1, typeforce@^1.11.3, typeforce@^1.11.5, "typeforce@git+https://github.com/michaeltout/typeforce", "typeforce@git+https://github.com/michaeltout/typeforce.git": +typeforce@1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.11.3.tgz#a54d0ff58808788fba358020982270bd6995d8e4" + integrity sha512-+vllWbxe1AKLkO3KNyZPjb51NRHwRE/8bAi/cmF6TK24VqrPiQPRiHrFV19j1xHxxCHQbIvN4Zfco+skuiXSWQ== + dependencies: + inherits "^2.0.1" + +typeforce@^1.11.5, "typeforce@git+https://github.com/VerusCoin/typeforce.git", "typeforce@git+https://github.com/michaeltout/typeforce": version "1.18.0" - resolved "git+https://github.com/michaeltout/typeforce.git#c9b0abc33ff006d7a6a6150eff034bc7eb8b07de" + resolved "git+https://github.com/VerusCoin/typeforce.git#c9b0abc33ff006d7a6a6150eff034bc7eb8b07de" typescript-compare@^0.0.2: version "0.0.2" @@ -11316,11 +11251,6 @@ universalify@^0.2.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -unorm@^1.3.3: - version "1.6.0" - resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" - integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== - unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -11526,9 +11456,9 @@ version_compare@0.0.3: resolved "https://registry.yarnpkg.com/version_compare/-/version_compare-0.0.3.tgz#ec30bbd187526a7f8b55027ac1cb2e65993483a5" integrity sha512-a9YzbQmnPZ3GhnE/LCxyRhL+xCP7SrYN+XAMCszRpwp8WRR89Jp8+1tw1InkBuZ+6jWKxGe+cr6SJpdP8RShtw== -"verus-pay-utils@git+https://github.com/michaeltout/verus-pay-utils.git": +"verus-pay-utils@git+https://github.com/VerusCoin/verus-pay-utils.git": version "2.0.0" - resolved "git+https://github.com/michaeltout/verus-pay-utils.git#cc34a4cae48f30714200742eca3d43ee42e13290" + resolved "git+https://github.com/VerusCoin/verus-pay-utils.git#cc34a4cae48f30714200742eca3d43ee42e13290" dependencies: bignumber.js "^9.0.0" verus-zkedid-utils "https://github.com/michaeltout/verus-zkedid-utils#dev" @@ -11543,9 +11473,9 @@ version_compare@0.0.3: bs58check "2.0.0" create-hash "1.2.0" -"verus-zkedid-utils@git+https://github.com/michaeltout/verus-zkedid-utils.git": +"verus-zkedid-utils@git+https://github.com/VerusCoin/verus-zkedid-utils.git": version "1.0.0" - resolved "git+https://github.com/michaeltout/verus-zkedid-utils.git#70d7f5ed6b4787da14b06de5d2d669d714d1860b" + resolved "git+https://github.com/VerusCoin/verus-zkedid-utils.git#70d7f5ed6b4787da14b06de5d2d669d714d1860b" "verus-zkedid-utils@https://github.com/michaeltout/verus-zkedid-utils#dev": version "1.0.0" From 059898c90c7c79996d1d7f3630e8271148dab231 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 16 Aug 2024 19:18:20 +0200 Subject: [PATCH 08/25] Update @ethersproject/signing-key dependency to VerusCoin version --- package.json | 2 +- yarn.lock | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index c66177d2..fc004508 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@bitgo/utxo-lib": "git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus", - "@ethersproject/signing-key": "5.0.5", + "@ethersproject/signing-key": "git+https://github.com/VerusCoin/ethersjs-signing-key-package.git", "@react-native-async-storage/async-storage": "1.17.10", "@react-native-camera-roll/camera-roll": "5.0.4", "@react-native-community/cli": "7.0.3", diff --git a/yarn.lock b/yarn.lock index dae2f840..536bf4d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1099,7 +1099,7 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.7.0": +"@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== @@ -1189,7 +1189,7 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@^5.0.0", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.7.0": +"@ethersproject/logger@^5.0.0", "@ethersproject/logger@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== @@ -1209,7 +1209,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@^5.0.0", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.7.0": +"@ethersproject/properties@^5.0.0", "@ethersproject/properties@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== @@ -1262,17 +1262,18 @@ "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.5.tgz#acfd06fc05a14180df7e027688bbd23fc4baf782" - integrity sha512-Z1wY7JC1HVO4CvQWY2TyTTuAr8xK3bJijZw1a9G92JEmKdv1j255R/0YLBBcFTl2J65LUjtXynNJ2GbArPGi5g== +"@ethersproject/signing-key@^5.7.0", "@ethersproject/signing-key@git+https://github.com/VerusCoin/ethersjs-signing-key-package.git": + version "5.7.0" + resolved "git+https://github.com/VerusCoin/ethersjs-signing-key-package.git#602f32146d0f6c2bfd5d70f2a252b8614151a1cb" dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - elliptic "6.5.3" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.7" + hash.js "1.1.7" -"@ethersproject/signing-key@^5.7.0", "@ethersproject/signing-key@https://github.com/VerusCoin/ethersjs-signing-key-package.git": +"@ethersproject/signing-key@https://github.com/VerusCoin/ethersjs-signing-key-package.git": version "5.7.0" resolved "https://github.com/VerusCoin/ethersjs-signing-key-package.git#602f32146d0f6c2bfd5d70f2a252b8614151a1cb" dependencies: @@ -4476,7 +4477,7 @@ electron-to-chromium@^1.4.411: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.421.tgz#2b8c0ef98ba00d4aef4c664933d570922da52161" integrity sha512-wZOyn3s/aQOtLGAwXMZfteQPN68kgls2wDAnYOA8kCjBvKVrW5RwmWVspxJYTqrcN7Y263XJVsC66VCIGzDO3g== -elliptic@6.5.3, elliptic@6.5.7, elliptic@^6.2.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.7, elliptic@^6.2.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.7" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== From 7cf05a0cfc23443a57187f03ce389dbbc6f60fcd Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 19 Aug 2024 17:38:05 +0200 Subject: [PATCH 09/25] Add seed recovery functionality to pre-login screen --- .../DisplaySeed/DisplaySeed.js | 94 +++++++++------ src/containers/Login/Login.js | 26 ++-- .../RecoverSeeds/RecoverSeedsSelectAccount.js | 112 ++++++++++++++++++ .../MainStackScreens/MainStackScreens.js | 2 +- .../RecoverSeedsStackScreens.js | 38 ++++++ .../SignedOutStackScreens.js | 11 +- .../ProfileSettings/ProfileSettings.js | 2 +- .../SignedOutDropdown/SignedOutDropdown.js | 58 +++++++++ 8 files changed, 291 insertions(+), 52 deletions(-) rename src/containers/{Settings/ProfileSettings => }/DisplaySeed/DisplaySeed.js (69%) create mode 100644 src/containers/RecoverSeeds/RecoverSeedsSelectAccount.js create mode 100644 src/containers/RootStack/RecoverSeedsStackScreens/RecoverSeedsStackScreens.js create mode 100644 src/containers/SignedOutDropdown/SignedOutDropdown.js diff --git a/src/containers/Settings/ProfileSettings/DisplaySeed/DisplaySeed.js b/src/containers/DisplaySeed/DisplaySeed.js similarity index 69% rename from src/containers/Settings/ProfileSettings/DisplaySeed/DisplaySeed.js rename to src/containers/DisplaySeed/DisplaySeed.js index 1f8ce2ad..dd36a480 100644 --- a/src/containers/Settings/ProfileSettings/DisplaySeed/DisplaySeed.js +++ b/src/containers/DisplaySeed/DisplaySeed.js @@ -12,14 +12,14 @@ import { import { NavigationActions } from '@react-navigation/compat'; import { connect } from 'react-redux'; import QRCode from 'react-native-qrcode-svg'; -import Styles from '../../../../styles/index' -import Colors from "../../../../globals/colors"; +import Styles from '../../styles/index' +import Colors from "../../globals/colors"; import { CommonActions } from '@react-navigation/native'; -import { DLIGHT_PRIVATE, ELECTRUM, ETH, WYRE_SERVICE } from "../../../../utils/constants/intervalConstants"; +import { DLIGHT_PRIVATE, ELECTRUM, ETH, WYRE_SERVICE } from "../../utils/constants/intervalConstants"; import { Card, Paragraph, Title, Button } from 'react-native-paper' -import { deriveKeyPair, dlightSeedToBytes, isSeedPhrase } from "../../../../utils/keys"; -import { createAlert } from "../../../../actions/actions/alert/dispatchers/alert"; -import { coinsList } from "../../../../utils/CoinData/CoinsList"; +import { deriveKeyPair, dlightSeedToBytes, isSeedPhrase } from "../../utils/keys"; +import { createAlert } from "../../actions/actions/alert/dispatchers/alert"; +import { coinsList } from "../../utils/CoinData/CoinsList"; class DisplaySeed extends Component { constructor() { @@ -31,7 +31,8 @@ class DisplaySeed extends Component { dualSameSeed: false, derivedKeys: {}, toggleDerivedKey: {}, - fetchingDerivedKey: {} + fetchingDerivedKey: {}, + completeOnBack: false, }; this.SEED_NAMES = { @@ -65,6 +66,15 @@ class DisplaySeed extends Component { .fromDeleteAccount, }); } + + if ( + data && + data.completeOnBack + ) { + this.setState({ + completeOnBack: data.completeOnBack, + }); + } } resetToScreen = () => { @@ -114,8 +124,9 @@ class DisplaySeed extends Component { } } - // Method to derive the key from seed. Replace this with your actual implementation deriveKeyFromSeed = async (seed, key, coinObj) => { + const { data } = this.props.route.params; + switch (key) { case DLIGHT_PRIVATE: return Buffer.from(await dlightSeedToBytes(seed)).toString('hex'); @@ -124,14 +135,14 @@ class DisplaySeed extends Component { seed, coinsList.ETH, key, - this.props.activeAccount.keyDerivationVersion, + data.keyDerivationVersion, )).privKey; case ELECTRUM: return (await deriveKeyPair( seed, coinObj, key, - this.props.activeAccount.keyDerivationVersion, + data.keyDerivationVersion, )).privKey; default: return seed @@ -139,7 +150,8 @@ class DisplaySeed extends Component { } render() { - const { seeds, toggleDerivedKey, fetchingDerivedKey, derivedKeys } = this.state; + const { seeds, toggleDerivedKey, fetchingDerivedKey, derivedKeys, completeOnBack } = this.state; + const { data } = this.props.route.params; return ( @@ -163,28 +175,30 @@ class DisplaySeed extends Component { - { - ((key === DLIGHT_PRIVATE && isSeedPhrase(seeds[key])) || - key === ETH || - key === ELECTRUM) && ( - - ) - } - { - !isToggleOn && !fetchingDerivedKey[key] && key === ELECTRUM && ( - - ) - } + {data.showDerivedKeys && <> + { + ((key === DLIGHT_PRIVATE && isSeedPhrase(seeds[key])) || + key === ETH || + key === ELECTRUM) && ( + + ) + } + { + !isToggleOn && !fetchingDerivedKey[key] && key === ELECTRUM && ( + + ) + } + } @@ -194,12 +208,16 @@ class DisplaySeed extends Component { - - + {!completeOnBack && ( + + )} diff --git a/src/containers/Login/Login.js b/src/containers/Login/Login.js index 105cdd7d..58aebdaf 100644 --- a/src/containers/Login/Login.js +++ b/src/containers/Login/Login.js @@ -22,6 +22,7 @@ import { } from '../../utils/constants/sendModal'; import {useSelector} from 'react-redux'; import TallButton from '../../components/LargerButton'; +import SignedOutDropdown from '../SignedOutDropdown/SignedOutDropdown'; const {height} = Dimensions.get('window'); @@ -32,6 +33,9 @@ const Login = props => { const authModalUsed = useSelector( state => state.authentication.authModalUsed, ); + const modalVisible = useSelector( + state => state.sendModal.visible, + ); const accounts = useSelector(state => state.authentication.accounts); openAuthModal = ignoreDefault => { @@ -69,6 +73,10 @@ const Login = props => { handleRevokeRecover = () => { props.navigation.navigate('RevokeRecover'); + } + + handleRecoverSeed = () => { + props.navigation.navigate("RecoverSeeds"); }; return ( @@ -77,17 +85,13 @@ const Login = props => { backgroundColor: Colors.secondaryColor, ...Styles.focalCenter, }}> - - - + + {!modalVisible && handleRecoverSeed()} + handleRevokeRecover={() => handleRevokeRecover()} + hasAccount={true} + />} + { + const accounts = useSelector(state => state.authentication.accounts); + const [numSeeds, setNumSeeds] = useState({}); + const [passwordDialogOpen, setPasswordDialogOpen] = useState(false); + const [passwordDialogTitle, setPasswordDialogTitle] = useState(''); + const [passwordDialogAccount, setPasswordDialogAccount] = useState(null); + + useEffect(() => { + for (let account of accounts) { + setNumSeeds(prevState => ({ + ...prevState, + [account.accountHash]: Object.values(account.encryptedKeys).filter(x => x != null).length, + })); + } + }, [accounts]); + + const handleCardPress = async (account) => { + if (await canShowSeed()) { + setPasswordDialogAccount(account); + setPasswordDialogOpen(true); + setPasswordDialogTitle(`Enter your password for "${account.id}"`); + } + }; + + const onPasswordResult = async (result) => { + if (result.valid) { + setPasswordDialogOpen(false); + + try { + const seeds = await checkPinForUser(result.password, passwordDialogAccount.id); + + navigation.dispatch(CommonActions.reset({ + index: 0, + routes: [{ name: 'DisplaySeed', params: { data: { seeds, showDerivedKeys: true, keyDerivationVersion: passwordDialogAccount.keyDerivationVersion, completeOnBack: true } } }], + })); + } catch(e) { + createAlert("Error", "Failed to retrieve seeds"); + } + } else { + createAlert("Authentication Error", "Incorrect password"); + } + } + + const renderAccountCards = () => { + return accounts.map(account => ( + + handleCardPress(account)} + key={account.accountHash} + style={{ backgroundColor: Colors.primaryColor }} + > + 1 ? 's' : ''}`} + left={() => } + descriptionStyle={{ color: Colors.secondaryColor }} + right={() => ( + + )} + /> + + + )); + }; + + return ( + + {passwordDialogOpen && + setPasswordDialogOpen(false)} + submit={(result) => onPasswordResult(result)} + visible={passwordDialogOpen} + title={passwordDialogTitle} + userName={passwordDialogAccount ? passwordDialogAccount.id : ''} + account={passwordDialogAccount} + allowBiometry={true} + /> + } + {'Select a Profile'} + + {renderAccountCards()} + + navigation.goBack()} + mode="outlined" + style={{ marginHorizontal: 8 }} + > + {"Cancel"} + + + ); +}; + +export default RecoverSeedsSelectAccount; diff --git a/src/containers/RootStack/MainStackScreens/MainStackScreens.js b/src/containers/RootStack/MainStackScreens/MainStackScreens.js index 4a2fe261..2374d222 100644 --- a/src/containers/RootStack/MainStackScreens/MainStackScreens.js +++ b/src/containers/RootStack/MainStackScreens/MainStackScreens.js @@ -3,7 +3,7 @@ import { createStackNavigator } from "@react-navigation/stack"; import { defaultHeaderOptions } from '../../../utils/navigation/header'; import AddCoin from '../../AddCoin/AddCoin' import CoinDetails from '../../CoinDetails/CoinDetails' -import DisplaySeed from '../../Settings/ProfileSettings/DisplaySeed/DisplaySeed' +import DisplaySeed from '../../DisplaySeed/DisplaySeed' import SettingsMenus from '../../Settings/SettingsMenus' import CoinMenus from '../../Coin/CoinMenus' import VerusPay from '../../VerusPay/VerusPay' diff --git a/src/containers/RootStack/RecoverSeedsStackScreens/RecoverSeedsStackScreens.js b/src/containers/RootStack/RecoverSeedsStackScreens/RecoverSeedsStackScreens.js new file mode 100644 index 00000000..c4c24098 --- /dev/null +++ b/src/containers/RootStack/RecoverSeedsStackScreens/RecoverSeedsStackScreens.js @@ -0,0 +1,38 @@ +import React, { useState } from 'react'; +import { createStackNavigator } from "@react-navigation/stack"; +import { defaultHeaderOptions } from '../../../utils/navigation/header'; +import RecoverSeedsSelectAccount from '../../RecoverSeeds/RecoverSeedsSelectAccount'; +import DisplaySeed from '../../DisplaySeed/DisplaySeed'; + +const RecoverSeedsStack = createStackNavigator(); + +const RecoverSeedsStackScreens = props => { + return ( + + + {(_props) => ( + + )} + + + + + ); +}; + +export default RecoverSeedsStackScreens; \ No newline at end of file diff --git a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js index dc8c1465..eb174a25 100644 --- a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js +++ b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js @@ -1,6 +1,6 @@ import React, {useEffect, useState} from 'react'; import {createStackNavigator} from '@react-navigation/stack'; -import DisplaySeed from '../../Settings/ProfileSettings/DisplaySeed/DisplaySeed'; +import DisplaySeed from '../../DisplaySeed/DisplaySeed'; import RecoverSeed from '../../Settings/ProfileSettings/RecoverSeed/RecoverSeed'; import DeleteProfile from '../../Settings/ProfileSettings/DeleteProfile/DeleteProfile'; import SecureLoading from '../../SecureLoading/SecureLoading'; @@ -10,6 +10,7 @@ import {useDispatch, useSelector} from 'react-redux'; import {setDeeplinkUrl} from '../../../actions/actionCreators'; import CreateProfile from '../../Onboard/CreateProfile/CreateProfile'; import RevokeRecoverStackScreens from '../RevokeRecoverStackScreens/RevokeRecoverStackScreens'; +import RecoverSeedsStackScreens from '../RecoverSeedsStackScreens/RecoverSeedsStackScreens'; const SignedOutStack = createStackNavigator(); @@ -93,6 +94,14 @@ const SignedOutStackScreens = props => { headerShown: false, }} /> + + ); }; diff --git a/src/containers/Settings/ProfileSettings/ProfileSettings.js b/src/containers/Settings/ProfileSettings/ProfileSettings.js index 1a4ffc66..bd259000 100644 --- a/src/containers/Settings/ProfileSettings/ProfileSettings.js +++ b/src/containers/Settings/ProfileSettings/ProfileSettings.js @@ -276,7 +276,7 @@ class ProfileSettings extends Component { .then((seeds) => { this.setState({ password: null }, () => { this.props.navigation.navigate("DisplaySeed", { - data: { seeds }, + data: { seeds, showDerivedKeys: true, keyDerivationVersion: this.props.activeAccount.keyDerivationVersion }, }); }); }) diff --git a/src/containers/SignedOutDropdown/SignedOutDropdown.js b/src/containers/SignedOutDropdown/SignedOutDropdown.js new file mode 100644 index 00000000..c0bd91e6 --- /dev/null +++ b/src/containers/SignedOutDropdown/SignedOutDropdown.js @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { Divider, FAB, Menu, Portal, Button, IconButton } from 'react-native-paper'; +import { SafeAreaView, View } from 'react-native' +import Colors from '../../globals/colors'; + +const SignedOutDropdown = (props) => { + const { + handleRecoverSeed, + hasAccount + } = props; + const [visible, setVisible] = React.useState(false); + + const openMenu = () => setVisible(true); + + const closeMenu = () => setVisible(false); + + const actions = !hasAccount + ? [] + : [ + { + label: 'Recover account seeds', + onPress: handleRecoverSeed, + } + ]; + + return ( + + + }> + { + actions.map((action, index) => { + return ( + { + closeMenu(); + action.onPress(props); + }} title={action.label} /> + ); + }) + } + + + ); +}; + +export default SignedOutDropdown; \ No newline at end of file From ad1da68e54b5e899939b0071a63cc60a882614bd Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 19 Aug 2024 17:38:28 +0200 Subject: [PATCH 10/25] Ignore EventEmitter logs --- src/VerusMobile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/VerusMobile.js b/src/VerusMobile.js index 4921cb00..74d4c46d 100644 --- a/src/VerusMobile.js +++ b/src/VerusMobile.js @@ -88,6 +88,8 @@ class VerusMobile extends React.Component { } componentDidMount() { + LogBox.ignoreLogs(['EventEmitter']); + activateKeyboardListener() AppState.addEventListener("change", (nextAppState) => this._handleAppStateChange(nextAppState)); From 3711d87e5312dd82a377dbb91b997fad6b0d3725 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 12 Aug 2024 18:09:26 +0200 Subject: [PATCH 11/25] Checkpoint for VerusID revocation/recovery, implement beginnings of UI from home screen, start linking revocation code to verusid-ts-client library --- .../LinkIdentityResult/LinkIdentityResult.js | 29 +++++++ .../LinkIdentityResult.render.js | 76 +++++++++++++++++++ src/containers/Login/Login.js | 2 +- .../SignedOutStackScreens.js | 1 + .../SignedOutDropdown/SignedOutDropdown.js | 5 ++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js create mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js new file mode 100644 index 00000000..b6bd791b --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js @@ -0,0 +1,29 @@ +import {useEffect, useState} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; +import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; +import {LinkIdentityResultRender} from './LinkIdentityResult.render'; + +const LinkIdentityResult = (props) => { + const [verusId, setVerusId] = useState(props.route.params == null ? {} : props.route.params.verusId); + const [friendlyNames, setFriendlyNames] = useState(props.route.params == null ? {} : props.route.params.friendlyNames); + const sendModal = useSelector(state => state.sendModal); + const {data} = sendModal; + + const finishSend = async () => { + if (data.noLogin) { + await props.updateSendFormData( + "success", + true, + ); + } + closeSendModal() + }; + + return LinkIdentityResultRender({ + verusId, + friendlyNames, + finishSend + }); +}; + +export default LinkIdentityResult; diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js new file mode 100644 index 00000000..78c55379 --- /dev/null +++ b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js @@ -0,0 +1,76 @@ +import React from 'react'; +import {ScrollView, View, TouchableOpacity} from 'react-native'; +import {Button, Text} from 'react-native-paper'; +import Colors from '../../../../globals/colors'; +import Styles from '../../../../styles'; +import {copyToClipboard} from '../../../../utils/clipboard/clipboard'; +import AnimatedSuccessCheckmark from '../../../AnimatedSuccessCheckmark'; +import { useSelector } from 'react-redux'; +import { convertFqnToDisplayFormat } from '../../../../utils/fullyqualifiedname'; + +export const LinkIdentityResultRender = ({verusId, finishSend}) => { + const coinObj = useSelector(state => state.sendModal.coinObj); + const formattedFriendlyName = convertFqnToDisplayFormat(verusId.fullyqualifiedname); + + return ( + + + copyToClipboard(verusId.identity.identityaddress, { + title: 'Address copied', + message: `${verusId.identity.identityaddress} copied to clipboard.`, + }) + } + style={{ + width: '75%', + marginTop: 16, + }}> + + {`${formattedFriendlyName} linked`} + + + + + + + + {`Your VerusID will now appear as a card in your ${coinObj.display_ticker} wallet.`} + + + + + + + ); +}; diff --git a/src/containers/Login/Login.js b/src/containers/Login/Login.js index 58aebdaf..a14e5515 100644 --- a/src/containers/Login/Login.js +++ b/src/containers/Login/Login.js @@ -76,7 +76,7 @@ const Login = props => { } handleRecoverSeed = () => { - props.navigation.navigate("RecoverSeeds"); + props.navigation.navigate('RecoverSeed'); }; return ( diff --git a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js index eb174a25..49155e12 100644 --- a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js +++ b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js @@ -11,6 +11,7 @@ import {setDeeplinkUrl} from '../../../actions/actionCreators'; import CreateProfile from '../../Onboard/CreateProfile/CreateProfile'; import RevokeRecoverStackScreens from '../RevokeRecoverStackScreens/RevokeRecoverStackScreens'; import RecoverSeedsStackScreens from '../RecoverSeedsStackScreens/RecoverSeedsStackScreens'; +import RevokeRecoverStackScreens from '../RevokeRecoverStackScreens/RevokeRecoverStackScreens'; const SignedOutStack = createStackNavigator(); diff --git a/src/containers/SignedOutDropdown/SignedOutDropdown.js b/src/containers/SignedOutDropdown/SignedOutDropdown.js index c0bd91e6..a0428797 100644 --- a/src/containers/SignedOutDropdown/SignedOutDropdown.js +++ b/src/containers/SignedOutDropdown/SignedOutDropdown.js @@ -6,6 +6,7 @@ import Colors from '../../globals/colors'; const SignedOutDropdown = (props) => { const { handleRecoverSeed, + handleRevokeRecover, hasAccount } = props; const [visible, setVisible] = React.useState(false); @@ -20,6 +21,10 @@ const SignedOutDropdown = (props) => { { label: 'Recover account seeds', onPress: handleRecoverSeed, + }, + { + label: 'Revoke/Recover VerusID', + onPress: handleRevokeRecover, } ]; From b49bc1bfd513442d6f6c0c809ab8d6ae44238643 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 4 Oct 2024 16:58:14 +0200 Subject: [PATCH 12/25] Fix duplicate import --- .../RootStack/SignedOutStackScreens/SignedOutStackScreens.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js index 49155e12..eb174a25 100644 --- a/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js +++ b/src/containers/RootStack/SignedOutStackScreens/SignedOutStackScreens.js @@ -11,7 +11,6 @@ import {setDeeplinkUrl} from '../../../actions/actionCreators'; import CreateProfile from '../../Onboard/CreateProfile/CreateProfile'; import RevokeRecoverStackScreens from '../RevokeRecoverStackScreens/RevokeRecoverStackScreens'; import RecoverSeedsStackScreens from '../RecoverSeedsStackScreens/RecoverSeedsStackScreens'; -import RevokeRecoverStackScreens from '../RevokeRecoverStackScreens/RevokeRecoverStackScreens'; const SignedOutStack = createStackNavigator(); From ff603b783a7202ac506dced6931676b028cdcd51 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 4 Oct 2024 16:59:27 +0200 Subject: [PATCH 13/25] Update yarn.lock to match package.json --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 536bf4d8..26518c01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2955,7 +2955,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== -axios@1.6.5, axios@1.7.4: +axios@1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== @@ -3433,7 +3433,7 @@ bs58@^4.0.0: dependencies: base-x "^3.0.2" -bs58check@2.0.0, bs58check@2.1.2, bs58check@<3.0.0, bs58check@^1.0.6, bs58check@^2.0.0, bs58check@^2.1.1, bs58check@^2.1.2: +bs58check@2.0.0, bs58check@2.1.2, bs58check@<3.0.0, bs58check@^1.0.6, bs58check@^2.0.0, bs58check@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== From 5e4eea0c334eae12f95d8653311f3450e19fa408 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 4 Oct 2024 17:01:57 +0200 Subject: [PATCH 14/25] Navigate handleRecoverSeed correctly --- src/containers/Login/Login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Login/Login.js b/src/containers/Login/Login.js index a14e5515..c12c8d8f 100644 --- a/src/containers/Login/Login.js +++ b/src/containers/Login/Login.js @@ -76,7 +76,7 @@ const Login = props => { } handleRecoverSeed = () => { - props.navigation.navigate('RecoverSeed'); + props.navigation.navigate('RecoverSeeds'); }; return ( From 883c2ee0c83586cfb53c8255fae0598f0b05c644 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Fri, 4 Oct 2024 17:10:49 +0200 Subject: [PATCH 15/25] Remove unused files --- .../LinkIdentityResult/LinkIdentityResult.js | 29 ------- .../LinkIdentityResult.render.js | 76 ------------------- 2 files changed, 105 deletions(-) delete mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js delete mode 100644 src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js deleted file mode 100644 index b6bd791b..00000000 --- a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.js +++ /dev/null @@ -1,29 +0,0 @@ -import {useEffect, useState} from 'react'; -import {useDispatch, useSelector} from 'react-redux'; -import {closeSendModal} from '../../../../actions/actions/sendModal/dispatchers/sendModal'; -import {LinkIdentityResultRender} from './LinkIdentityResult.render'; - -const LinkIdentityResult = (props) => { - const [verusId, setVerusId] = useState(props.route.params == null ? {} : props.route.params.verusId); - const [friendlyNames, setFriendlyNames] = useState(props.route.params == null ? {} : props.route.params.friendlyNames); - const sendModal = useSelector(state => state.sendModal); - const {data} = sendModal; - - const finishSend = async () => { - if (data.noLogin) { - await props.updateSendFormData( - "success", - true, - ); - } - closeSendModal() - }; - - return LinkIdentityResultRender({ - verusId, - friendlyNames, - finishSend - }); -}; - -export default LinkIdentityResult; diff --git a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js b/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js deleted file mode 100644 index 78c55379..00000000 --- a/src/components/SendModal/RevokeIdentity/LinkIdentityResult/LinkIdentityResult.render.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import {ScrollView, View, TouchableOpacity} from 'react-native'; -import {Button, Text} from 'react-native-paper'; -import Colors from '../../../../globals/colors'; -import Styles from '../../../../styles'; -import {copyToClipboard} from '../../../../utils/clipboard/clipboard'; -import AnimatedSuccessCheckmark from '../../../AnimatedSuccessCheckmark'; -import { useSelector } from 'react-redux'; -import { convertFqnToDisplayFormat } from '../../../../utils/fullyqualifiedname'; - -export const LinkIdentityResultRender = ({verusId, finishSend}) => { - const coinObj = useSelector(state => state.sendModal.coinObj); - const formattedFriendlyName = convertFqnToDisplayFormat(verusId.fullyqualifiedname); - - return ( - - - copyToClipboard(verusId.identity.identityaddress, { - title: 'Address copied', - message: `${verusId.identity.identityaddress} copied to clipboard.`, - }) - } - style={{ - width: '75%', - marginTop: 16, - }}> - - {`${formattedFriendlyName} linked`} - - - - - - - - {`Your VerusID will now appear as a card in your ${coinObj.display_ticker} wallet.`} - - - - - - - ); -}; From e7377daf4dfa8cf338b831f944b842be9712b725 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 7 Oct 2024 17:22:09 +0200 Subject: [PATCH 16/25] Make revocation and recovery spend fee from recovery/revocation ID --- .../RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js | 2 +- .../RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js index 00286654..58ce76d8 100644 --- a/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js @@ -203,7 +203,7 @@ const RecoverIdentityForm = (props) => { revocationAddr, [primaryAddr], privateAddr, - addrs[0] + recRes.result.identity.identityaddress ) props.setModalHeight(height >= 720 ? 696 : height - 24); diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js index d234808e..207daf09 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js @@ -136,7 +136,7 @@ const RevokeIdentityForm = (props) => { const friendlyNames = await getFriendlyNameMap(data[SEND_MODAL_SYSTEM_ID], tarRes.result); const targetIdAddr = tarRes.result.identity.identityaddress; - const revocationResult = await createRevokeIdentityTx(data[SEND_MODAL_SYSTEM_ID], targetIdAddr, addrs[0]) + const revocationResult = await createRevokeIdentityTx(data[SEND_MODAL_SYSTEM_ID], targetIdAddr, revRes.result.identity.identityaddress) props.setModalHeight(height >= 720 ? 696 : height - 24); props.navigation.navigate(SEND_MODAL_FORM_STEP_CONFIRM, { From 92bc737a6c25db3fb6df10f5eaf453b62318e129 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 7 Oct 2024 17:38:22 +0200 Subject: [PATCH 17/25] Prevent minsigs > 1 VerusIDs from moving past form step as recovery/revocation authorities --- .../RecoverIdentityForm/RecoverIdentityForm.js | 6 ++++++ .../RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js index 58ce76d8..3ecf8d6f 100644 --- a/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityForm/RecoverIdentityForm.js @@ -154,6 +154,12 @@ const RecoverIdentityForm = (props) => { ); } + if (recRes.result.identity.minimumsignatures > 1) { + throw new Error( + 'Recovery identity has minimum signatures > 1. Please recover through CLI or Verus Desktop.', + ); + } + let isInWallet = false; const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC); diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js index 207daf09..a7ed1037 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityForm/RevokeIdentityForm.js @@ -115,6 +115,12 @@ const RevokeIdentityForm = (props) => { ); } + if (revRes.result.identity.minimumsignatures > 1) { + throw new Error( + 'Revocation identity has minimum signatures > 1. Please revoke through CLI or Verus Desktop.', + ); + } + let isInWallet = false; const addrs = await getPotentialPrimaryAddresses(coinsList.VRSC); From 901a904c90bc19a3de66efd392f4edc713ef166a Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 7 Oct 2024 17:38:43 +0200 Subject: [PATCH 18/25] Add vDEX to list of blockchains to revoke/recover on --- src/containers/RevokeRecover/RevokeRecoverIdentityForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js index 33210de4..c4eb76f9 100644 --- a/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js +++ b/src/containers/RevokeRecover/RevokeRecoverIdentityForm.js @@ -13,7 +13,7 @@ import { SEND_MODAL_ENCRYPTED_IDENTITY_SEED, SEND_MODAL_IDENTITY_TO_REVOKE_FIELD import { encryptkey } from '../../utils/seedCrypt'; export default function RevokeRecoverIdentityForm({ navigation, isRecovery, importedSeed, exitRevokeRecover }) { - const DEFAULT_SYSTEMS = [coinsList.VRSC, coinsList.iExBJfZYK7KREDpuhj6PzZBzqMAKaFg7d2, coinsList.VRSCTEST]; + const DEFAULT_SYSTEMS = [coinsList.VRSC, coinsList.iExBJfZYK7KREDpuhj6PzZBzqMAKaFg7d2, coinsList.iHog9UCTrn95qpUBFCZ7kKz7qWdMA8MQ6N, coinsList.VRSCTEST]; const {height} = Dimensions.get('window'); const isKeyboardActive = useSelector(state => state.keyboard.active); From 402a6e132fe0b02fcbab06209c3ccf208cb5ceab Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 7 Oct 2024 18:08:48 +0200 Subject: [PATCH 19/25] Update tBTC logo and theme color --- src/images/cryptologo/default/web3/tbtc/tbtc_dark.svg | 11 +++++++---- .../cryptologo/default/web3/tbtc/tbtc_light.svg | 11 +++++++---- src/utils/CoinData/CoinsList.js | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/images/cryptologo/default/web3/tbtc/tbtc_dark.svg b/src/images/cryptologo/default/web3/tbtc/tbtc_dark.svg index 6a461a00..19403b41 100644 --- a/src/images/cryptologo/default/web3/tbtc/tbtc_dark.svg +++ b/src/images/cryptologo/default/web3/tbtc/tbtc_dark.svg @@ -1,5 +1,8 @@ - - - - + + + + + + + diff --git a/src/images/cryptologo/default/web3/tbtc/tbtc_light.svg b/src/images/cryptologo/default/web3/tbtc/tbtc_light.svg index 11f8ee97..19403b41 100644 --- a/src/images/cryptologo/default/web3/tbtc/tbtc_light.svg +++ b/src/images/cryptologo/default/web3/tbtc/tbtc_light.svg @@ -1,5 +1,8 @@ - - - - + + + + + + + diff --git a/src/utils/CoinData/CoinsList.js b/src/utils/CoinData/CoinsList.js index 19937939..ad67cfac 100644 --- a/src/utils/CoinData/CoinsList.js +++ b/src/utils/CoinData/CoinsList.js @@ -157,7 +157,7 @@ export const coinsList = { display_ticker: "tBTC.vETH", display_name: "tBTC on Verus", alt_names: [], - theme_color: "#000000", + theme_color: "#1D2229", compatible_channels: [VERUSID, VRPC, GENERAL], tags: [IS_VERUS, IS_ZCASH, IS_PBAAS], proto: "vrsc", @@ -945,7 +945,7 @@ export const coinsList = { alt_names: [], website: 'https://tbtc.network/', compatible_channels: [ERC20, GENERAL], - theme_color: '#000000', + theme_color: '#1D2229', dominant_channel: ERC20, decimals: ETHERS, tags: [], From b350abd5f1732ae761837d928244b5636f07cda2 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Tue, 8 Oct 2024 15:56:07 +0200 Subject: [PATCH 20/25] Add updates parameter to VerusIdObjectData to make revocation/recovery confirm screen more intuitive --- .../RecoverIdentityConfirm.js | 10 ++++ .../RecoverIdentityConfirm.render.js | 19 +++++- .../RecoverIdentityForm.js | 6 +- .../RevokeIdentityConfirm.render.js | 5 ++ src/components/VerusIdObjectData.js | 59 +++++++++++++++---- 5 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js index ac8d5a36..2e1d0273 100644 --- a/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.js @@ -20,6 +20,11 @@ const RecoverIdentityConfirm = props => { const [ownedAddress, setOwnedAddress] = useState(props.route.params.ownedAddress); const [recoverableByUser, setRecoverableByUser] = useState(props.route.params.recoverableByUser); const [recoveryResult, setRecoveryResult] = useState(props.route.params.recoveryResult); + const [revocationAddr, setRevocationAddr] = useState(props.route.params.revocationAddr); + const [recoveryAddr, setRecoveryAddr] = useState(props.route.params.recoveryAddr); + const [primaryAddr, setPrimaryAddr] = useState(props.route.params.primaryAddr); + const [privateAddr, setPrivateAddr] = useState(props.route.params.privateAddr); + const [friendlyNames, setFriendlyNames] = useState( props.route.params.friendlyNames, ); @@ -95,6 +100,11 @@ const RecoverIdentityConfirm = props => { submitData, recoverableByUser: !!props.route.params.recoverableByUser, ownedAddress: props.route.params.ownedAddress || '', + sendModal, + revocationAddr, + recoveryAddr, + primaryAddr, + privateAddr }); }; diff --git a/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js index 5079b9cd..fbdf3365 100644 --- a/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js +++ b/src/components/SendModal/RecoverIdentity/RecoverIdentityConfirm/RecoverIdentityConfirm.render.js @@ -5,7 +5,7 @@ import Colors from '../../../../globals/colors'; import Styles from '../../../../styles'; import VerusIdObjectData from '../../../VerusIdObjectData'; -export const RecoverIdentityConfirmRender = ({ targetId, friendlyNames, goBack, submitData, ownedByUser, ownedAddress }) => { +export const RecoverIdentityConfirmRender = ({ targetId, friendlyNames, goBack, submitData, ownedByUser, ownedAddress, sendModal, revocationAddr, recoveryAddr, primaryAddr, privateAddr }) => { return ( { friendlyNames, ownedAddress, recoverableByUser, - recoveryResult + recoveryResult, + revocationAddr, + recoveryAddr, + primaryAddr, + privateAddr }) } catch (e) { Alert.alert('Error', e.message); diff --git a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js index 746c81a6..1fc63ee3 100644 --- a/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js +++ b/src/components/SendModal/RevokeIdentity/RevokeIdentityConfirm/RevokeIdentityConfirm.render.js @@ -13,6 +13,11 @@ export const RevokeIdentityConfirmRender = ({ targetId, friendlyNames, goBack, s friendlyNames={friendlyNames} ownedByUser={false} ownedAddress={ownedAddress} + updates={{ + ["Status"]: { + data: "Revoked" + } + }} StickyFooterComponent={ ; export default function VerusIdObjectData(props) { - const { friendlyNames, verusId, StickyFooterComponent, flex, ownedByUser, ownedAddress } = props; + const { friendlyNames, verusId, StickyFooterComponent, flex, ownedByUser, ownedAddress, updates } = props; const [listData, setListData] = useState([]); tryDisplayFriendlyName = iAddr => { return friendlyNames[iAddr] ? friendlyNames[iAddr] : iAddr; @@ -124,15 +124,14 @@ export default function VerusIdObjectData(props) { }, ]; - if (verusId.identity.privateaddress) { + if (verusId.identity.privateaddress || (updates && updates['Private Address'])) { data.push({ key: 'Private Address', - data: verusId.identity.privateaddress, - onPress: () => - copyDataToClipboard( - verusId.identity.privateaddress, - 'Private Address', - ), + data: !verusId.identity.privateaddress ? "None" : verusId.identity.privateaddress, + onPress: !verusId.identity.privateaddress ? undefined : () => copyDataToClipboard( + verusId.identity.privateaddress, + 'Private Address', + ), }); } @@ -148,7 +147,11 @@ export default function VerusIdObjectData(props) { }); } - setListData(data); + setListData(updates ? data.sort((a, b) => { + if (updates[a.key]) return -1 + else if (updates[b.key]) return 1 + else return 1 + }) : data); } }, [verusId, friendlyNames]); @@ -174,12 +177,14 @@ export default function VerusIdObjectData(props) { onPress={() => item.onPress()}> item.right ? ( @@ -197,6 +202,36 @@ export default function VerusIdObjectData(props) { /> + {updates && updates[item.key] && + <> + + item.right ? ( + + {item.right} + + ) : null + } + /> + + + } ); } else if (item.condition == 'warning') { From ba62eab33cd8cb751e8843d84c3af3a7601d9c7e Mon Sep 17 00:00:00 2001 From: michaeltout Date: Wed, 9 Oct 2024 17:37:06 +0200 Subject: [PATCH 21/25] Update verusd-rps-ts-client and verusid-ts-client libraries --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 26518c01..99a572e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11484,14 +11484,14 @@ version_compare@0.0.3: "verusd-rpc-ts-client@git+https://github.com/VerusCoin/verusd-rpc-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#a09e379d073b1b5f6468285a793f08da69f645db" + resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#ca30b2ea22541164dec57f7b127afbdb2a4ea687" dependencies: axios "1.7.4" verus-typescript-primitives "git+https://github.com/VerusCoin/verus-typescript-primitives.git" "verusid-ts-client@git+https://github.com/VerusCoin/verusid-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#c67986d41e7e1c52b874beac62d47a61cd6e63b0" + resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#3b36fb6e1e62b1b1314c5969168f941a201e75bc" dependencies: "@bitgo/utxo-lib" "git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c" axios "1.7.4" From 4f52bc6b1af1481593e3664f65e573357f43a845 Mon Sep 17 00:00:00 2001 From: michaeltout Date: Thu, 10 Oct 2024 14:39:08 +0200 Subject: [PATCH 22/25] Update Verus TS libraries to automatically clear out contentmultimap on identity revocation/recovery for identity used as update object --- src/utils/api/channels/verusid/requests/updateIdentity.js | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/api/channels/verusid/requests/updateIdentity.js b/src/utils/api/channels/verusid/requests/updateIdentity.js index 3e6f580f..86b539aa 100644 --- a/src/utils/api/channels/verusid/requests/updateIdentity.js +++ b/src/utils/api/channels/verusid/requests/updateIdentity.js @@ -1,4 +1,4 @@ -import { decompile, GetIdentityResponse, OPS, OptCCParams, Identity, SmartTransactionScript, IdentityID, fromBase58Check } from "verus-typescript-primitives"; +import { decompile, GetIdentityResponse, OPS, OptCCParams, Identity, fromBase58Check } from "verus-typescript-primitives"; import { CoinDirectory } from "../../../../CoinData/CoinDirectory"; import VrpcProvider from "../../../../vrpc/vrpcInterface" import { IS_PBAAS } from "../../../../constants/intervalConstants"; diff --git a/yarn.lock b/yarn.lock index 99a572e4..a6f28d61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11466,7 +11466,7 @@ version_compare@0.0.3: "verus-typescript-primitives@git+https://github.com/VerusCoin/verus-typescript-primitives.git": version "1.0.0" - resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#c4987fb8ad7e273c670e3831ce9cc7c95da9422a" + resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#37babf5e26b1cb463926a88590d0d3dcb0354a16" dependencies: base64url "3.0.1" bech32 "2.0.0" @@ -11484,14 +11484,14 @@ version_compare@0.0.3: "verusd-rpc-ts-client@git+https://github.com/VerusCoin/verusd-rpc-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#ca30b2ea22541164dec57f7b127afbdb2a4ea687" + resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#b7a225ab3bdd5de769389ccdd57674ce21956941" dependencies: axios "1.7.4" verus-typescript-primitives "git+https://github.com/VerusCoin/verus-typescript-primitives.git" "verusid-ts-client@git+https://github.com/VerusCoin/verusid-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#3b36fb6e1e62b1b1314c5969168f941a201e75bc" + resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#96570cbc8a22f7069be849b376778f078c52bca4" dependencies: "@bitgo/utxo-lib" "git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c" axios "1.7.4" From 3cd4e3aac1f42044237e439ae94f5394cc58776e Mon Sep 17 00:00:00 2001 From: michaeltout Date: Mon, 14 Oct 2024 15:00:16 +0200 Subject: [PATCH 23/25] Update bitgo utxo lib and verus typescript primitives library --- package.json | 3 ++- yarn.lock | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index fc004508..61ba6032 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "bundle-ios": "react-native bundle --dev false --entry-file index.js --bundle-output ios/main.jsbundle --platform ios --assets-dest='./ios'", "dist-android": "cd android && ./gradlew assembleRelease && cd ..", "test": "jest", - "lint": "eslint ." + "lint": "eslint .", + "update-verus-libs": "yarn remove verus-typescript-primitives && yarn add 'git+https://github.com/VerusCoin/verus-typescript-primitives.git' && yarn remove @bitgo/utxo-lib && yarn add 'git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus' && yarn remove verusd-rpc-ts-client && yarn add 'git+https://github.com/VerusCoin/verusd-rpc-ts-client.git' && yarn remove verusid-ts-client && yarn add 'git+https://github.com/VerusCoin/verusid-ts-client.git'" }, "dependencies": { "@bitgo/utxo-lib": "git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus", diff --git a/yarn.lock b/yarn.lock index a6f28d61..a8cb4287 100644 --- a/yarn.lock +++ b/yarn.lock @@ -960,10 +960,10 @@ "@bitgo/blake2b-wasm" "^3.0.1" nanoassert "^2.0.0" -"@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c", "@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus": +"@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#3c6efada45b6a4fc2a1065951c307016dca7f953", "@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#utxo-lib-verus": version "1.9.6" - uid "8a9c08f51f3af53cd691ff5346196d54b34ae04c" - resolved "git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c" + uid "3c6efada45b6a4fc2a1065951c307016dca7f953" + resolved "git+https://github.com/VerusCoin/BitGoJS.git#3c6efada45b6a4fc2a1065951c307016dca7f953" dependencies: "@bitgo/blake2b" "3.0.1" bech32 "0.0.3" @@ -11466,7 +11466,7 @@ version_compare@0.0.3: "verus-typescript-primitives@git+https://github.com/VerusCoin/verus-typescript-primitives.git": version "1.0.0" - resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#37babf5e26b1cb463926a88590d0d3dcb0354a16" + resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#ca149bcef98d226ffc6eccfd4bd089620ca2c965" dependencies: base64url "3.0.1" bech32 "2.0.0" @@ -11484,16 +11484,16 @@ version_compare@0.0.3: "verusd-rpc-ts-client@git+https://github.com/VerusCoin/verusd-rpc-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#b7a225ab3bdd5de769389ccdd57674ce21956941" + resolved "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git#80fa22bc486e476b9c4b7448fa287a608c3f8857" dependencies: axios "1.7.4" verus-typescript-primitives "git+https://github.com/VerusCoin/verus-typescript-primitives.git" "verusid-ts-client@git+https://github.com/VerusCoin/verusid-ts-client.git": version "0.1.0" - resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#96570cbc8a22f7069be849b376778f078c52bca4" + resolved "git+https://github.com/VerusCoin/verusid-ts-client.git#a8538489efb45f24551e0ae9644dc3813e913403" dependencies: - "@bitgo/utxo-lib" "git+https://github.com/VerusCoin/BitGoJS.git#8a9c08f51f3af53cd691ff5346196d54b34ae04c" + "@bitgo/utxo-lib" "git+https://github.com/VerusCoin/BitGoJS.git#3c6efada45b6a4fc2a1065951c307016dca7f953" axios "1.7.4" bignumber.js "9.1.0" bn.js "5.2.1" From 5f1429461200807c1a56d6421eaab34db157e507 Mon Sep 17 00:00:00 2001 From: Asher Dawes Date: Fri, 18 Oct 2024 14:29:41 -0700 Subject: [PATCH 24/25] Add JitPack repository to build.gradle --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index de8df98d..d870e3d9 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -52,9 +52,9 @@ allprojects { maven { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") + url 'https://www.jitpack.io' } google() - //maven { url 'https://www.jitpack.io' } } } From 2fa220770e6e0196956efed58ad87eeec2ada207 Mon Sep 17 00:00:00 2001 From: Asher Dawes Date: Fri, 18 Oct 2024 16:38:24 -0700 Subject: [PATCH 25/25] Set version --- android/app/build.gradle | 2 +- env/main.android.json | 2 +- env/main.ios.json | 2 +- ios/assets/env/main.json | 2 +- ios/verusMobile.xcodeproj/project.pbxproj | 4 ++-- package.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index d5c1cb6a..14185ba9 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -4,7 +4,7 @@ import com.android.build.OutputFile def versionMajor = 1 def versionMinor = 0 -def versionRevision = 17 +def versionRevision = 18 def versionBuild = 0 def keystorePropertiesFile = rootProject.file("keystore.properties"); diff --git a/env/main.android.json b/env/main.android.json index a260feef..c1c3bc31 100644 --- a/env/main.android.json +++ b/env/main.android.json @@ -1,5 +1,5 @@ { - "APP_VERSION": "1.0.17", + "APP_VERSION": "1.0.18", "ELECTRUM_PROTOCOL_CHANGE": 1.4, "KEY_DERIVATION_VERSION": 1, diff --git a/env/main.ios.json b/env/main.ios.json index ec3ac716..2f608e6a 100644 --- a/env/main.ios.json +++ b/env/main.ios.json @@ -1,5 +1,5 @@ { - "APP_VERSION": "1.0.17", + "APP_VERSION": "1.0.18", "ELECTRUM_PROTOCOL_CHANGE": 1.4, "KEY_DERIVATION_VERSION": 1, diff --git a/ios/assets/env/main.json b/ios/assets/env/main.json index ec3ac716..2f608e6a 100644 --- a/ios/assets/env/main.json +++ b/ios/assets/env/main.json @@ -1,5 +1,5 @@ { - "APP_VERSION": "1.0.17", + "APP_VERSION": "1.0.18", "ELECTRUM_PROTOCOL_CHANGE": 1.4, "KEY_DERIVATION_VERSION": 1, diff --git a/ios/verusMobile.xcodeproj/project.pbxproj b/ios/verusMobile.xcodeproj/project.pbxproj index ec10c1f5..7a4b04ac 100644 --- a/ios/verusMobile.xcodeproj/project.pbxproj +++ b/ios/verusMobile.xcodeproj/project.pbxproj @@ -721,7 +721,7 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/rn-fetch-blob\"", ); - MARKETING_VERSION = 1.0.17; + MARKETING_VERSION = 1.0.18; PRODUCT_BUNDLE_IDENTIFIER = org.reactjs.native.verusmobile; PRODUCT_NAME = verusmobile; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -792,7 +792,7 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/rn-fetch-blob\"", ); - MARKETING_VERSION = 1.0.17; + MARKETING_VERSION = 1.0.18; PRODUCT_BUNDLE_IDENTIFIER = org.reactjs.native.verusmobile; PRODUCT_NAME = verusmobile; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/package.json b/package.json index becacaf8..6ac618f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "verusmobile", - "version": "1.0.17", + "version": "1.0.18", "private": true, "scripts": { "postinstall": "./node_modules/.bin/rn-nodeify --hack --install --yarn && npx jetify",