mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-23 19:37:52 +00:00
added seedphrase feature
This commit is contained in:
parent
78e85f9e79
commit
ba7d81dbf8
45
package-lock.json
generated
45
package-lock.json
generated
@ -57,6 +57,7 @@
|
|||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"jssha": "3.3.1",
|
"jssha": "3.3.1",
|
||||||
|
"lit": "^3.2.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime": "^4.0.4",
|
"mime": "^4.0.4",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
@ -2523,6 +2524,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@lit-labs/ssr-dom-shim": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@lit/reactive-element": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit-labs/ssr-dom-shim": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@malept/cross-spawn-promise": {
|
"node_modules/@malept/cross-spawn-promise": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
|
||||||
@ -4438,8 +4452,7 @@
|
|||||||
"node_modules/@types/trusted-types": {
|
"node_modules/@types/trusted-types": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/use-sync-external-store": {
|
"node_modules/@types/use-sync-external-store": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
@ -10011,6 +10024,34 @@
|
|||||||
"uc.micro": "^2.0.0"
|
"uc.micro": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lit": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit/reactive-element": "^2.0.4",
|
||||||
|
"lit-element": "^4.1.0",
|
||||||
|
"lit-html": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-element": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit-labs/ssr-dom-shim": "^1.2.0",
|
||||||
|
"@lit/reactive-element": "^2.0.4",
|
||||||
|
"lit-html": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-html": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/load-json-file": {
|
"node_modules/load-json-file": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"jssha": "3.3.1",
|
"jssha": "3.3.1",
|
||||||
|
"lit": "^3.2.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime": "^4.0.4",
|
"mime": "^4.0.4",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
|
47
src/App.tsx
47
src/App.tsx
@ -43,11 +43,13 @@ import Return from "./assets/svgs/Return.svg";
|
|||||||
import Success from "./assets/svgs/Success.svg";
|
import Success from "./assets/svgs/Success.svg";
|
||||||
import Info from "./assets/svgs/Info.svg";
|
import Info from "./assets/svgs/Info.svg";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import './utils/seedPhrase/RandomSentenceGenerator';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createAccount,
|
createAccount,
|
||||||
generateRandomSentence,
|
generateRandomSentence,
|
||||||
saveFileToDisk,
|
saveFileToDisk,
|
||||||
|
saveSeedPhraseToDisk,
|
||||||
} from "./utils/generateWallet/generateWallet";
|
} from "./utils/generateWallet/generateWallet";
|
||||||
import { kdf } from "./deps/kdf";
|
import { kdf } from "./deps/kdf";
|
||||||
import { generateSaveWalletData } from "./utils/generateWallet/storeWallet";
|
import { generateSaveWalletData } from "./utils/generateWallet/storeWallet";
|
||||||
@ -123,6 +125,7 @@ import { fileToBase64 } from "./utils/fileReading";
|
|||||||
import { handleGetFileFromIndexedDB } from "./utils/indexedDB";
|
import { handleGetFileFromIndexedDB } from "./utils/indexedDB";
|
||||||
import { CoreSyncStatus } from "./components/CoreSyncStatus";
|
import { CoreSyncStatus } from "./components/CoreSyncStatus";
|
||||||
import { Wallets } from "./Wallets";
|
import { Wallets } from "./Wallets";
|
||||||
|
import { RandomSentenceGenerator } from "./utils/seedPhrase/RandomSentenceGenerator";
|
||||||
|
|
||||||
type extStates =
|
type extStates =
|
||||||
| "not-authenticated"
|
| "not-authenticated"
|
||||||
@ -210,6 +213,7 @@ const controlAllQueues = (action) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const clearAllQueues = () => {
|
export const clearAllQueues = () => {
|
||||||
Object.keys(allQueues).forEach((key) => {
|
Object.keys(allQueues).forEach((key) => {
|
||||||
const val = allQueues[key];
|
const val = allQueues[key];
|
||||||
@ -384,7 +388,12 @@ function App() {
|
|||||||
useRecoilState(enabledDevModeAtom);
|
useRecoilState(enabledDevModeAtom);
|
||||||
|
|
||||||
const { toggleFullScreen } = useAppFullScreen(setFullScreen);
|
const { toggleFullScreen } = useAppFullScreen(setFullScreen);
|
||||||
|
const generatorRef = useRef(null)
|
||||||
|
const exportSeedphrase = ()=> {
|
||||||
|
console.log('hello', generatorRef.current.parsedString)
|
||||||
|
const seedPhrase = generatorRef.current.parsedString
|
||||||
|
saveSeedPhraseToDisk(seedPhrase)
|
||||||
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isDevModeFromStorage = localStorage.getItem("isEnabledDevMode");
|
const isDevModeFromStorage = localStorage.getItem("isEnabledDevMode");
|
||||||
if (isDevModeFromStorage) {
|
if (isDevModeFromStorage) {
|
||||||
@ -899,7 +908,7 @@ function App() {
|
|||||||
res();
|
res();
|
||||||
}, 250);
|
}, 250);
|
||||||
});
|
});
|
||||||
const res = await createAccount();
|
const res = await createAccount(generatorRef.current.parsedString);
|
||||||
const wallet = await res.generateSaveWalletData(
|
const wallet = await res.generateSaveWalletData(
|
||||||
walletToBeDownloadedPassword,
|
walletToBeDownloadedPassword,
|
||||||
crypto.kdfThreads,
|
crypto.kdfThreads,
|
||||||
@ -2430,6 +2439,40 @@ function App() {
|
|||||||
Set up your Qortal account
|
Set up your Qortal account
|
||||||
</TextP>
|
</TextP>
|
||||||
<Spacer height="14px" />
|
<Spacer height="14px" />
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
maxWidth: '100%',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '10px'
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
maxWidth: '400px',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '10px'
|
||||||
|
}}>
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: '14px'
|
||||||
|
}}>Your seedphrase</Typography>
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: '12px'
|
||||||
|
}}>Only shown once! Please copy and keep safe!</Typography>
|
||||||
|
|
||||||
|
<random-sentence-generator
|
||||||
|
ref={generatorRef}
|
||||||
|
template="adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun"
|
||||||
|
|
||||||
|
></random-sentence-generator>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<CustomButton sx={{
|
||||||
|
padding: '7px',
|
||||||
|
fontSize: '12px'
|
||||||
|
}} onClick={exportSeedphrase}>
|
||||||
|
Export Seedphrase
|
||||||
|
</CustomButton>
|
||||||
|
<Spacer height="14px" />
|
||||||
<CustomLabel htmlFor="standard-adornment-password">
|
<CustomLabel htmlFor="standard-adornment-password">
|
||||||
Wallet Password
|
Wallet Password
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
|
147
src/Wallets.tsx
147
src/Wallets.tsx
@ -6,13 +6,19 @@ import ListItemText from "@mui/material/ListItemText";
|
|||||||
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@mui/material/Avatar";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import { Box, Button, ButtonBase, IconButton, Input } from "@mui/material";
|
import { Box, Button, ButtonBase, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Input } from "@mui/material";
|
||||||
import { CustomButton } from "./App-styles";
|
import { CustomButton } from "./App-styles";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
import EditIcon from "@mui/icons-material/Edit";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import { Label } from "./components/Group/AddGroup";
|
import { Label } from "./components/Group/AddGroup";
|
||||||
import { Spacer } from "./common/Spacer";
|
import { Spacer } from "./common/Spacer";
|
||||||
import { getWallets, storeWallets } from "./background";
|
import { getWallets, storeWallets, walletVersion } from "./background";
|
||||||
|
import { useModal } from "./common/useModal";
|
||||||
|
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
|
||||||
|
import { decryptStoredWalletFromSeedPhrase } from "./utils/decryptWallet";
|
||||||
|
import { crypto } from "./constants/decryptWallet";
|
||||||
|
import { LoadingButton } from "@mui/lab";
|
||||||
|
import { PasswordField } from "./components";
|
||||||
|
|
||||||
const parsefilenameQortal = (filename)=> {
|
const parsefilenameQortal = (filename)=> {
|
||||||
return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename;
|
return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename;
|
||||||
@ -21,6 +27,15 @@ const parsefilenameQortal = (filename)=> {
|
|||||||
export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||||
const [wallets, setWallets] = useState([]);
|
const [wallets, setWallets] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [seedValue, setSeedValue] = useState("");
|
||||||
|
const [seedName, setSeedName] = useState("");
|
||||||
|
const [seedError, setSeedError] = useState("");
|
||||||
|
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [isOpenSeedModal, setIsOpenSeedModal] = useState(false);
|
||||||
|
const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false);
|
||||||
|
|
||||||
|
const { isShow, onCancel, onOk, show, } = useModal();
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
accept: {
|
accept: {
|
||||||
@ -81,7 +96,6 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
setWallets((prev) => {
|
setWallets((prev) => {
|
||||||
let copyPrev = [...prev];
|
let copyPrev = [...prev];
|
||||||
if (wallet === null) {
|
if (wallet === null) {
|
||||||
console.log("entered");
|
|
||||||
copyPrev.splice(idx, 1); // Use splice to remove the item
|
copyPrev.splice(idx, 1); // Use splice to remove the item
|
||||||
return copyPrev;
|
return copyPrev;
|
||||||
} else {
|
} else {
|
||||||
@ -91,6 +105,42 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSetSeedValue = async ()=> {
|
||||||
|
try {
|
||||||
|
setIsOpenSeedModal(true)
|
||||||
|
const {seedValue, seedName, password} = await show({
|
||||||
|
message: "",
|
||||||
|
publishFee: "",
|
||||||
|
});
|
||||||
|
setIsLoadingEncryptSeed(true)
|
||||||
|
const res = await decryptStoredWalletFromSeedPhrase(seedValue)
|
||||||
|
const wallet2 = new PhraseWallet(res, walletVersion);
|
||||||
|
const wallet = await wallet2.generateSaveWalletData(
|
||||||
|
password,
|
||||||
|
crypto.kdfThreads,
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
if(wallet?.address0){
|
||||||
|
setWallets([...wallets, {
|
||||||
|
...wallet,
|
||||||
|
name: seedName
|
||||||
|
}]);
|
||||||
|
setIsOpenSeedModal(false)
|
||||||
|
setSeedValue('')
|
||||||
|
setSeedName('')
|
||||||
|
setPassword('')
|
||||||
|
setSeedError('')
|
||||||
|
} else {
|
||||||
|
setSeedError('Could not create wallet.')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
setSeedError(error?.message || 'Could not create wallet.')
|
||||||
|
} finally {
|
||||||
|
setIsLoadingEncryptSeed(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const selectedWalletFunc = (wallet) => {
|
const selectedWalletFunc = (wallet) => {
|
||||||
setRawWallet(wallet);
|
setRawWallet(wallet);
|
||||||
setExtState("wallet-dropped");
|
setExtState("wallet-dropped");
|
||||||
@ -179,12 +229,98 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
right: '20px'
|
right: '20px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomButton {...getRootProps()}>
|
<CustomButton onClick={handleSetSeedValue} sx={{
|
||||||
|
padding: '10px'
|
||||||
|
}} >
|
||||||
|
|
||||||
|
Add seed-phrase
|
||||||
|
</CustomButton>
|
||||||
|
<CustomButton sx={{
|
||||||
|
padding: '10px'
|
||||||
|
}} {...getRootProps()}>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
Add wallets
|
Add wallets
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={isOpenSeedModal}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' && seedValue && seedName && password) {
|
||||||
|
onOk({seedValue, seedName, password});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle id="alert-dialog-title">
|
||||||
|
Type or paste in your seed-phrase
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Label>Name</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Name"
|
||||||
|
value={seedName}
|
||||||
|
onChange={(e) => setSeedName(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Spacer height="7px" />
|
||||||
|
<Label>Seed-phrase</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Seed-phrase"
|
||||||
|
value={seedValue}
|
||||||
|
onChange={(e) => setSeedValue(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Spacer height="7px" />
|
||||||
|
|
||||||
|
<Label>Choose new password</Label>
|
||||||
|
<PasswordField
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button disabled={isLoadingEncryptSeed} variant="contained" onClick={()=> {
|
||||||
|
setIsOpenSeedModal(false)
|
||||||
|
setSeedValue('')
|
||||||
|
setSeedName('')
|
||||||
|
setPassword('')
|
||||||
|
setSeedError('')
|
||||||
|
}}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
<LoadingButton
|
||||||
|
loading={isLoadingEncryptSeed}
|
||||||
|
disabled={!seedValue || !seedName || !password}
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => {
|
||||||
|
if(!seedValue || !seedName || !password) return
|
||||||
|
onOk({seedValue, seedName, password});
|
||||||
|
}}
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</LoadingButton>
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
visibility: seedError ? 'visible' : 'hidden'
|
||||||
|
}}>{seedError}</Typography>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -338,6 +474,9 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -21,4 +21,14 @@ export const decryptStoredWallet = async (password, wallet) => {
|
|||||||
}
|
}
|
||||||
const decryptedBytes = AES_CBC.decrypt(encryptedSeedBytes, encryptionKey, false, iv)
|
const decryptedBytes = AES_CBC.decrypt(encryptedSeedBytes, encryptionKey, false, iv)
|
||||||
return decryptedBytes
|
return decryptedBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decryptStoredWalletFromSeedPhrase = async (password) => {
|
||||||
|
console.log('p')
|
||||||
|
const threads = doInitWorkers(crypto.kdfThreads)
|
||||||
|
const salt = new Uint8Array(void 0)
|
||||||
|
|
||||||
|
|
||||||
|
const seed = await kdf(password, salt, threads)
|
||||||
|
return seed
|
||||||
}
|
}
|
@ -75,8 +75,8 @@ export function generateRandomSentence(template = 'adverb verb noun adjective no
|
|||||||
return parse(template);
|
return parse(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createAccount = async()=> {
|
export const createAccount = async(generatedSeedPhrase)=> {
|
||||||
const generatedSeedPhrase = generateRandomSentence()
|
if(!generatedSeedPhrase) throw new Error('No generated seed-phrase')
|
||||||
const threads = doInitWorkers(crypto.kdfThreads)
|
const threads = doInitWorkers(crypto.kdfThreads)
|
||||||
|
|
||||||
const seed = await kdf(generatedSeedPhrase, void 0, threads)
|
const seed = await kdf(generatedSeedPhrase, void 0, threads)
|
||||||
@ -93,4 +93,13 @@ export const createAccount = async()=> {
|
|||||||
|
|
||||||
await FileSaver.saveAs(blob, fileName);
|
await FileSaver.saveAs(blob, fileName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveSeedPhraseToDisk = async (data) => {
|
||||||
|
|
||||||
|
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
|
||||||
|
const fileName = "qortal_seedphrase.txt"
|
||||||
|
|
||||||
|
await FileSaver.saveAs(blob, fileName);
|
||||||
|
|
||||||
}
|
}
|
175
src/utils/seedPhrase/RandomSentenceGenerator.ts
Normal file
175
src/utils/seedPhrase/RandomSentenceGenerator.ts
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// Author: irontiga <irontiga@gmail.com>
|
||||||
|
|
||||||
|
import { html, LitElement, css } from 'lit'
|
||||||
|
import * as WORDLISTS from './wordList'
|
||||||
|
|
||||||
|
class RandomSentenceGenerator extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
template: { type: String, attribute: 'template' },
|
||||||
|
parsedString: { type: String },
|
||||||
|
fetchedWordlistCount: { type: Number, value: 0 },
|
||||||
|
capitalize: { type: Boolean },
|
||||||
|
partsOfSpeechMap: { type: Object },
|
||||||
|
templateEntropy: { type: Number, reflect: true, attribute: 'template-entropy' },
|
||||||
|
maxWordLength: { type: Number, attribute: 'max-word-length' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.template = 'adjective noun verb adverb.'
|
||||||
|
this.maxWordLength = 0
|
||||||
|
this.parsedString = ''
|
||||||
|
this.fetchedWordlistCount = 0
|
||||||
|
this.capitalize = true
|
||||||
|
this.partsOfSpeechMap = {
|
||||||
|
'noun': 'nouns',
|
||||||
|
'adverb': 'adverbs',
|
||||||
|
'adv': 'adverbs',
|
||||||
|
'verb': 'verbs',
|
||||||
|
'interjection': 'interjections',
|
||||||
|
'adjective': 'adjectives',
|
||||||
|
'adj': 'adjectives',
|
||||||
|
'verbed': 'verbed'
|
||||||
|
}
|
||||||
|
this.partsOfSpeech = Object.keys(this.partsOfSpeechMap)
|
||||||
|
this._wordlists = WORDLISTS
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
div {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #1f2023;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div>${this.parsedString}</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
console.log('this.template', this.template)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
updated(changedProperties) {
|
||||||
|
let regen = false
|
||||||
|
|
||||||
|
if (changedProperties.has('template')) {
|
||||||
|
regen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has('maxWordLength')) {
|
||||||
|
console.dir(this.maxWordLength)
|
||||||
|
|
||||||
|
if (this.maxWordLength) {
|
||||||
|
const wl = { ...this._wordlists }
|
||||||
|
|
||||||
|
for (const partOfSpeech in this._wordlists) {
|
||||||
|
console.log(this._wordlists[partOfSpeech])
|
||||||
|
if (Array.isArray(this._wordlists[partOfSpeech])) {
|
||||||
|
wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._wordlists = wl
|
||||||
|
}
|
||||||
|
|
||||||
|
regen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regen) this.generate()
|
||||||
|
}
|
||||||
|
|
||||||
|
_RNG(entropy) {
|
||||||
|
if (entropy > 1074) {
|
||||||
|
throw new Error('Javascript can not handle that much entropy!')
|
||||||
|
}
|
||||||
|
|
||||||
|
let randNum = 0
|
||||||
|
|
||||||
|
const crypto = window.crypto || window.msCrypto
|
||||||
|
|
||||||
|
if (crypto) {
|
||||||
|
const entropy256 = Math.ceil(entropy / 8)
|
||||||
|
|
||||||
|
let buffer = new Uint8Array(entropy256)
|
||||||
|
|
||||||
|
crypto.getRandomValues(buffer)
|
||||||
|
|
||||||
|
randNum = buffer.reduce((num, value) => {
|
||||||
|
return num * value
|
||||||
|
}, 1) / Math.pow(256, entropy256)
|
||||||
|
} else {
|
||||||
|
console.warn('Secure RNG not found. Using Math.random')
|
||||||
|
|
||||||
|
randNum = Math.random()
|
||||||
|
}
|
||||||
|
|
||||||
|
return randNum
|
||||||
|
}
|
||||||
|
|
||||||
|
setRNG(fn) {
|
||||||
|
this._RNG = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
_captitalize(str) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
getWord(partOfSpeech) {
|
||||||
|
const words = this._wordlists[this.partsOfSpeechMap[partOfSpeech]]
|
||||||
|
const requiredEntropy = Math.log(words.length) / Math.log(2)
|
||||||
|
const index = this._RNG(requiredEntropy) * words.length
|
||||||
|
|
||||||
|
return {
|
||||||
|
word: words[Math.round(index)],
|
||||||
|
entropy: words.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate() {
|
||||||
|
this.parsedString = this.parse(this.template)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(template) {
|
||||||
|
const split = template.split(/[\s]/g)
|
||||||
|
|
||||||
|
let entropy = 1
|
||||||
|
|
||||||
|
const final = split.map(word => {
|
||||||
|
const lower = word.toLowerCase()
|
||||||
|
|
||||||
|
this.partsOfSpeech.some(partOfSpeech => {
|
||||||
|
const partOfSpeechIndex = lower.indexOf(partOfSpeech) // Check it exists
|
||||||
|
const nextChar = word.charAt(partOfSpeech.length)
|
||||||
|
|
||||||
|
if (partOfSpeechIndex === 0 && !(nextChar && (nextChar.match(/[a-zA-Z]/g) != null))) {
|
||||||
|
const replacement = this.getWord(partOfSpeech)
|
||||||
|
word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation)
|
||||||
|
entropy = entropy * replacement.entropy
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return word
|
||||||
|
})
|
||||||
|
|
||||||
|
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
|
||||||
|
|
||||||
|
return final.join(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('random-sentence-generator', RandomSentenceGenerator)
|
||||||
|
|
||||||
|
export default RandomSentenceGenerator
|
40
src/utils/seedPhrase/verb-past-tense.ts
Normal file
40
src/utils/seedPhrase/verb-past-tense.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export const EXCEPTIONS = {
|
||||||
|
'are': 'were',
|
||||||
|
'eat': 'ate',
|
||||||
|
'go': 'went',
|
||||||
|
'have': 'had',
|
||||||
|
'inherit': 'inherited',
|
||||||
|
'is': 'was',
|
||||||
|
'run': 'ran',
|
||||||
|
'sit': 'sat',
|
||||||
|
'visit': 'visited'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPastTense = (verb, exceptions = EXCEPTIONS) => {
|
||||||
|
if (exceptions[verb]) {
|
||||||
|
return exceptions[verb]
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((/e$/i).test(verb)) {
|
||||||
|
return verb + 'd'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((/[aeiou]c$/i).test(verb)) {
|
||||||
|
return verb + 'ked'
|
||||||
|
}
|
||||||
|
|
||||||
|
// for american english only
|
||||||
|
if ((/el$/i).test(verb)) {
|
||||||
|
return verb + 'ed'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
|
||||||
|
return verb + 'ed'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
|
||||||
|
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
|
||||||
|
}
|
||||||
|
|
||||||
|
return verb + 'ed'
|
||||||
|
}
|
32
src/utils/seedPhrase/wordList.ts
Normal file
32
src/utils/seedPhrase/wordList.ts
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user