Skip to content

Commit

Permalink
feat: show seed words (#43)
Browse files Browse the repository at this point in the history
Description
---
Originally posted in #38 

Co-authored-by: @mmrrnn

---------

Co-authored-by: Marcin Papież <mmpapiez@gmail.com>
  • Loading branch information
stringhandler and mmrrnn authored Aug 9, 2024
1 parent c42f264 commit eff71ce
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ sanitize-filename = "0.5"
async-trait = "0.1.81"
sysinfo = "0.31.2"
log4rs = "1.3.0"
keyring = "3.0.5"
keyring = { version = "3.0.5", features = ["windows-native", "apple-native", "linux-native"] }
nix = { version = "0.29.0", features = ["signal"] }
# static bind lzma
xz2 = { version = "0.1.7", features = ["static"] }
Expand Down
13 changes: 12 additions & 1 deletion src-tauri/src/internal_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use tari_core::transactions::key_manager::{
TransactionKeyManagerInterface,
};
use tari_key_manager::mnemonic::{Mnemonic, MnemonicLanguage};
use tari_key_manager::SeedWords;
use tari_utilities::hex::Hex;

const KEY_MANAGER_COMMS_SECRET_KEY_BRANCH_KEY: &str = "comms";
Expand Down Expand Up @@ -80,7 +81,6 @@ impl InternalWallet {
});

let seed = CipherSeed::new();
// TODO: Don't print out the seed words lol
let seed_words = seed.to_mnemonic(MnemonicLanguage::English, None).unwrap();
for i in 0..seed_words.len() {
dbg!(seed_words.get_word(i).unwrap());
Expand Down Expand Up @@ -124,6 +124,17 @@ impl InternalWallet {
))
}

pub fn decrypt_seed_words(&self) -> Result<SeedWords, anyhow::Error> {
let entry = Entry::new("com.tari.universe", "internal_wallet")?;

let passphrase = SafePassword::from(entry.get_password()?);
let seed_binary = Vec::<u8>::from_base58(&self.config.seed_words_encrypted_base58)
.map_err(|e| anyhow!(e.to_string()))?;
let seed = CipherSeed::from_enciphered_bytes(&seed_binary, Some(passphrase))?;
let seed_words = seed.to_mnemonic(MnemonicLanguage::English, None)?;
Ok(seed_words)
}

pub fn get_view_key(&self) -> String {
self.config.view_key_private_hex.clone()
}
Expand Down
23 changes: 22 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,26 @@ async fn set_auto_mining<'r>(
Ok(())
}

#[tauri::command]
async fn get_seed_words<'r>(
_window: tauri::Window,
_state: tauri::State<'r, UniverseAppState>,
app: tauri::AppHandle,
) -> Result<Vec<String>, String> {
let config_path = app.path_resolver().app_config_dir().unwrap();
let internal_wallet = InternalWallet::load_or_create(config_path)
.await
.map_err(|e| e.to_string())?;
let seed_words = internal_wallet
.decrypt_seed_words()
.map_err(|e| e.to_string())?;
let mut res = vec![];
for i in 0..seed_words.len() {
res.push(seed_words.get_word(i).unwrap().clone());
}
Ok(res)
}

#[tauri::command]
async fn start_mining<'r>(
window: tauri::Window,
Expand Down Expand Up @@ -537,7 +557,8 @@ fn main() {
stop_mining,
set_auto_mining,
set_mode,
open_log_dir
open_log_dir,
get_seed_words
])
.build(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
83 changes: 81 additions & 2 deletions src/containers/SideBar/components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import { CgEye, CgEyeAlt, CgCopy } from 'react-icons/cg';
import {
IconButton,
Dialog,
Expand All @@ -10,16 +11,28 @@ import {
Box,
Typography,
Divider,
CircularProgress,
Tooltip,
} from '@mui/material';
import { IoSettingsOutline, IoClose } from 'react-icons/io5';
import { useGetSeedWords } from '../../../hooks/useGetSeedWords';
import truncateString from '../../../utils/truncateString';
import { invoke } from '@tauri-apps/api/tauri';

const Settings: React.FC = () => {
const [open, setOpen] = useState(false);
const [formState, setFormState] = useState({ field1: '', field2: '' });
const [showSeedWords, setShowSeedWords] = useState(false);
const [isCopyTooltipHidden, setIsCopyTooltipHidden] = useState(true);
const { seedWords, getSeedWords, seedWordsFetched, seedWordsFetching } =
useGetSeedWords();

const handleClickOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const handleClose = () => {
setOpen(false);
setFormState({ field1: '', field2: '' });
setShowSeedWords(false);
};

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
Expand Down Expand Up @@ -47,6 +60,22 @@ const Settings: React.FC = () => {
});
};

const toggleSeedWordsVisibility = async () => {
if (!seedWordsFetched) {
await getSeedWords();
}
setShowSeedWords((p) => !p);
};

const copySeedWords = async () => {
if (!seedWordsFetched) {
await getSeedWords();
}
setIsCopyTooltipHidden(false);
navigator.clipboard.writeText(seedWords.join(','));
setTimeout(() => setIsCopyTooltipHidden(true), 1000);
};

return (
<>
<IconButton onClick={handleClickOpen}>
Expand All @@ -71,7 +100,48 @@ const Settings: React.FC = () => {
</IconButton>
</Stack>
<Divider />
<Box component="form" onSubmit={handleSubmit}>
<Box my={1}>
<Typography sx={{}} variant="h5">
Seed Words
</Typography>
<Stack flexDirection="row" alignItems="center" gap={1}>
<Typography variant="body2">
{showSeedWords
? truncateString(seedWords.join(','), 50)
: '****************************************************'}
</Typography>
{seedWordsFetching ? (
<CircularProgress size="34px" />
) : (
<>
<IconButton
onClick={toggleSeedWordsVisibility}
>
{showSeedWords ? (
<CgEyeAlt />
) : (
<CgEye />
)}
</IconButton>
<Tooltip
title="Copied!"
placement="top"
open={!isCopyTooltipHidden}
disableFocusListener
disableHoverListener
disableTouchListener
PopperProps={{ disablePortal: true }}
>
<IconButton onClick={copySeedWords}>
<CgCopy />
</IconButton>
</Tooltip>
</>
)}
</Stack>
</Box>
<Box component="form" onSubmit={handleSubmit} my={1}>
<Typography variant="h5">Random</Typography>
<Stack spacing={1} pt={1}>
<TextField
label="Field 1"
Expand Down Expand Up @@ -101,6 +171,15 @@ const Settings: React.FC = () => {
Open logs directory
</Button>
</Stack>
<Divider />
<DialogActions>
<Button onClick={handleCancel} variant="outlined">
Cancel
</Button>
<Button type="submit" variant="contained">
Submit
</Button>
</DialogActions>
</DialogContent>
</Dialog>
</>
Expand Down
26 changes: 26 additions & 0 deletions src/hooks/useGetSeedWords.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useCallback, useState } from 'react';
import { invoke } from '@tauri-apps/api/tauri';

export function useGetSeedWords() {
const [seedWords, setSeedWords] = useState<string[]>([]);
const [seedWordsFetching, setSeedWordsFetching] = useState(false);

const getSeedWords = useCallback(async () => {
setSeedWordsFetching(true);
try {
const seedWords = await invoke('get_seed_words') as string[];
setSeedWords(seedWords);
} catch (e) {
console.error('Could not get seed words', e);
} finally {
setSeedWordsFetching(false);
}
}, []);

return {
seedWords,
getSeedWords,
seedWordsFetched: seedWords.length > 0,
seedWordsFetching,
};
}
6 changes: 6 additions & 0 deletions src/utils/truncateString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const truncateString = (str: string, num: number): string => {
if (str.length <= num) return str;
return str.slice(0, num) + "...";
};

export default truncateString;

0 comments on commit eff71ce

Please sign in to comment.