Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: show seed words #43

Merged
merged 5 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;