Refactored key-management a lot.
This commit is contained in:
@@ -12,6 +12,7 @@ rust-crypto = "^0.2"
|
|||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
web-view = { version = "0.7.2", features = [] }
|
web-view = { version = "0.7.2", features = [] }
|
||||||
|
tinyfiledialogs = "3.3.10"
|
||||||
serde = { version = "1.0.102", features = ["derive"] }
|
serde = { version = "1.0.102", features = ["derive"] }
|
||||||
serde_json = "1.0.42"
|
serde_json = "1.0.42"
|
||||||
num-bigint = "0.2"
|
num-bigint = "0.2"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::event::Event;
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::sync::MutexGuard;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ pub enum Event {
|
|||||||
MinerStopped,
|
MinerStopped,
|
||||||
KeyGeneratorStarted,
|
KeyGeneratorStarted,
|
||||||
KeyGeneratorStopped,
|
KeyGeneratorStopped,
|
||||||
|
KeyCreated {path: String, public: String},
|
||||||
|
KeyLoaded {path: String, public: String},
|
||||||
|
KeySaved {path: String, public: String},
|
||||||
NewBlockReceived,
|
NewBlockReceived,
|
||||||
BlockchainChanged,
|
BlockchainChanged,
|
||||||
ActionStopMining,
|
ActionStopMining,
|
||||||
|
|||||||
+19
-5
@@ -21,6 +21,8 @@ pub struct Keystore {
|
|||||||
private_key: Bytes,
|
private_key: Bytes,
|
||||||
public_key: Bytes,
|
public_key: Bytes,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
path: String,
|
||||||
|
#[serde(skip)]
|
||||||
seed: Vec<u8>
|
seed: Vec<u8>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,18 +32,21 @@ impl Keystore {
|
|||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
rng.fill(&mut buf);
|
rng.fill(&mut buf);
|
||||||
let (private, public) = keypair(&buf);
|
let (private, public) = keypair(&buf);
|
||||||
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), seed: Vec::from(&buf[..])}
|
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), path: String::new(), seed: Vec::from(&buf[..])}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(seed: &[u8]) -> Self {
|
pub fn from_bytes(seed: &[u8]) -> Self {
|
||||||
let (private, public) = keypair(&seed);
|
let (private, public) = keypair(&seed);
|
||||||
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), seed: Vec::from(seed)}
|
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), path: String::new(), seed: Vec::from(seed)}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
||||||
match fs::read(&Path::new(filename)) {
|
let path = Path::new(filename);
|
||||||
|
match fs::read(&path) {
|
||||||
Ok(key) => {
|
Ok(key) => {
|
||||||
Some(Self::from_bytes(key.as_slice()))
|
let mut keystore = Self::from_bytes(key.as_slice());
|
||||||
|
keystore.path = path.to_str().unwrap().to_owned();
|
||||||
|
Some(keystore)
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
None
|
None
|
||||||
@@ -50,11 +55,12 @@ impl Keystore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO Implement error conditions
|
//TODO Implement error conditions
|
||||||
pub fn save(&self, filename: &str, _password: &str) {
|
pub fn save(&mut self, filename: &str, _password: &str) {
|
||||||
match File::create(Path::new(filename)) {
|
match File::create(Path::new(filename)) {
|
||||||
Ok(mut f) => {
|
Ok(mut f) => {
|
||||||
//TODO implement key encryption
|
//TODO implement key encryption
|
||||||
f.write_all(&self.seed).expect("Error saving keystore");
|
f.write_all(&self.seed).expect("Error saving keystore");
|
||||||
|
self.path = filename.to_owned();
|
||||||
}
|
}
|
||||||
Err(_) => { println!("Error saving key file!"); }
|
Err(_) => { println!("Error saving key file!"); }
|
||||||
}
|
}
|
||||||
@@ -68,6 +74,10 @@ impl Keystore {
|
|||||||
self.private_key.clone()
|
self.private_key.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_path(&self) -> &str {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
|
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
|
||||||
signature(message, self.private_key.data.as_slice())
|
signature(message, self.private_key.data.as_slice())
|
||||||
}
|
}
|
||||||
@@ -120,6 +130,10 @@ impl Bytes {
|
|||||||
self.data.as_slice()
|
self.data.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
crate::utils::to_hex(&self.data)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn zero32() -> Self {
|
pub fn zero32() -> Self {
|
||||||
Bytes { data: [0u8; 32].to_vec() }
|
Bytes { data: [0u8; 32].to_vec() }
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-30
@@ -1,8 +1,9 @@
|
|||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
extern crate web_view;
|
extern crate web_view;
|
||||||
|
extern crate tinyfiledialogs as tfd;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
@@ -27,7 +28,10 @@ fn main() {
|
|||||||
println!("ALFIS 0.1.0");
|
println!("ALFIS 0.1.0");
|
||||||
let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings");
|
let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings");
|
||||||
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
||||||
None => { generate_key(KEYSTORE_DIFFICULTY, Arc::new(AtomicBool::new(true))).expect("Could not load or generate keypair") }
|
None => {
|
||||||
|
println!("Generated temporary keystore. Please, generate full-privileged keys.");
|
||||||
|
Keystore::new()
|
||||||
|
}
|
||||||
Some(keystore) => { keystore }
|
Some(keystore) => { keystore }
|
||||||
};
|
};
|
||||||
let blockchain: Blockchain = Blockchain::new(&settings);
|
let blockchain: Blockchain = Blockchain::new(&settings);
|
||||||
@@ -81,36 +85,66 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|||||||
Loaded => {
|
Loaded => {
|
||||||
web_view.eval("showMiningIndicator(false);").expect("Error evaluating!");
|
web_view.eval("showMiningIndicator(false);").expect("Error evaluating!");
|
||||||
let handle = web_view.handle();
|
let handle = web_view.handle();
|
||||||
|
let context_copy = context.clone();
|
||||||
let mut c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
c.bus.register(move |_uuid, e| {
|
c.bus.register(move |_uuid, e| {
|
||||||
println!("Got event from bus {:?}", &e);
|
println!("Got event from bus {:?}", &e);
|
||||||
let visible = match e {
|
let eval = match e {
|
||||||
Event::MinerStarted => { true }
|
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
||||||
Event::KeyGeneratorStarted => { true }
|
Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
||||||
Event::MinerStopped => { false }
|
Event::KeySaved { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
||||||
Event::KeyGeneratorStopped => { false }
|
Event::MinerStarted => { format!("showMiningIndicator({});", true) }
|
||||||
_ => { false }
|
Event::KeyGeneratorStarted => { format!("showMiningIndicator({});", true) }
|
||||||
|
Event::MinerStopped => { format!("showMiningIndicator({});", false) }
|
||||||
|
Event::KeyGeneratorStopped => { format!("showMiningIndicator({});", false) }
|
||||||
|
_ => { String::new() }
|
||||||
};
|
};
|
||||||
handle.dispatch(move |web_view| {
|
|
||||||
web_view.eval(&format!("showMiningIndicator({});", visible)).expect("Error evaluating!");
|
if !eval.is_empty() {
|
||||||
return WVResult::Ok(());
|
println!("Evaluating {}", &eval);
|
||||||
}).expect("Error dispatching!");
|
handle.dispatch(move |web_view| {
|
||||||
|
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
|
||||||
|
return WVResult::Ok(());
|
||||||
|
}).expect("Error dispatching!");
|
||||||
|
}
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
LoadKey { name, pass } => {
|
LoadKey {} => {
|
||||||
match Keystore::from_file(&name, &pass) {
|
let result = tfd::open_file_dialog("Open keys file", "", Some((&["*.key"], "*.key")));
|
||||||
None => {
|
match result {
|
||||||
println!("Error loading keystore '{}'!", &name);
|
None => {}
|
||||||
},
|
Some(file_name) => {
|
||||||
Some(keystore) => {
|
match Keystore::from_file(&file_name, "") {
|
||||||
let mut c = context.lock().unwrap();
|
None => {
|
||||||
c.set_keystore(keystore);
|
println!("Error loading keystore '{}'!", &file_name);
|
||||||
|
},
|
||||||
|
Some(keystore) => {
|
||||||
|
println!("Loaded keystore with key: {:?}", &keystore.get_public());
|
||||||
|
let mut c = context.lock().unwrap();
|
||||||
|
c.bus.post(Event::KeyLoaded {path: keystore.get_path().to_owned(), public: keystore.get_public().to_string()});
|
||||||
|
c.set_keystore(keystore);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CreateKey { name, pass } => {
|
CreateKey {} => {
|
||||||
create_key(context.clone(), &name, &pass);
|
create_key(context.clone());
|
||||||
|
}
|
||||||
|
SaveKey {} => {
|
||||||
|
let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.key"], "Key files (*.key)");
|
||||||
|
match result {
|
||||||
|
None => {}
|
||||||
|
Some(new_path) => {
|
||||||
|
let mut c = context.lock().unwrap();
|
||||||
|
let path = new_path.clone();
|
||||||
|
let public = c.keystore.get_public().to_string();
|
||||||
|
c.keystore.save(&new_path, "");
|
||||||
|
println!("Key file saved to {}", &path);
|
||||||
|
c.bus.post(Event::KeySaved {path, public });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CheckDomain { name} => {
|
CheckDomain { name} => {
|
||||||
let c = context.lock().unwrap();
|
let c = context.lock().unwrap();
|
||||||
@@ -170,28 +204,32 @@ fn create_transaction<S: Into<String>>(keystore: &Keystore, name: S, method: S,
|
|||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_key(context: Arc<Mutex<Context>>, filename: &str, password: &str) {
|
fn create_key(context: Arc<Mutex<Context>>) {
|
||||||
let mining = Arc::new(AtomicBool::new(true));
|
let mining = Arc::new(AtomicBool::new(true));
|
||||||
|
let miners_count = Arc::new(AtomicUsize::new(0));
|
||||||
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
||||||
for _ in 0..num_cpus::get() {
|
for _ in 0..num_cpus::get() {
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
let filename= filename.to_owned();
|
|
||||||
let password= password.to_owned();
|
|
||||||
let mining = mining.clone();
|
let mining = mining.clone();
|
||||||
|
let miners_count = miners_count.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
miners_count.fetch_add(1, Ordering::Relaxed);
|
||||||
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
||||||
None => {
|
None => {
|
||||||
println!("Keystore mining finished");
|
println!("Keystore mining finished");
|
||||||
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
|
||||||
}
|
}
|
||||||
Some(keystore) => {
|
Some(keystore) => {
|
||||||
|
println!("Key mined successfully: {:?}", &keystore.get_public());
|
||||||
let mut c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
mining.store(false,Ordering::Relaxed);
|
mining.store(false,Ordering::Relaxed);
|
||||||
keystore.save(&filename, &password);
|
c.bus.post(Event::KeyCreated {path: keystore.get_path().to_owned(), public: keystore.get_public().to_string()});
|
||||||
c.set_keystore(keystore);
|
c.set_keystore(keystore);
|
||||||
c.bus.post(Event::KeyGeneratorStopped);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let miners = miners_count.fetch_sub(1, Ordering::Relaxed) - 1;
|
||||||
|
if miners == 0 {
|
||||||
|
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
context.lock().unwrap().bus.register(move |_uuid, e| {
|
context.lock().unwrap().bus.register(move |_uuid, e| {
|
||||||
@@ -222,8 +260,9 @@ fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore>
|
|||||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
Loaded,
|
Loaded,
|
||||||
LoadKey{name: String, pass: String},
|
LoadKey{},
|
||||||
CreateKey{name: String, pass: String},
|
CreateKey{},
|
||||||
|
SaveKey{},
|
||||||
CheckDomain{name: String},
|
CheckDomain{name: String},
|
||||||
CreateDomain{name: String, records: String, tags: String},
|
CreateDomain{name: String, records: String, tags: String},
|
||||||
ChangeDomain{name: String, records: String, tags: String},
|
ChangeDomain{name: String, records: String, tags: String},
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use num_cpus;
|
|||||||
|
|
||||||
use crate::{Block, Bytes, Context, hash_is_good, Transaction};
|
use crate::{Block, Bytes, Context, hash_is_good, Transaction};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
context: Arc<Mutex<Context>>,
|
context: Arc<Mutex<Context>>,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use mio::net::{TcpListener, TcpStream};
|
|||||||
|
|
||||||
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
||||||
use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown};
|
use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown};
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
const SERVER: Token = Token(0);
|
const SERVER: Token = Token(0);
|
||||||
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
||||||
|
|||||||
+15
-77
@@ -37,9 +37,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<p class="menu-label">Key management</p>
|
<p class="menu-label">Key management</p>
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
<li><a onclick="openTab(this, 'key_load')">Load key</a></li>
|
<li><a onclick="openTab(this, 'key_load')">Manage keys</a></li>
|
||||||
<li><a onclick="openTab(this, 'key_create')">Create key</a></li>
|
|
||||||
<li><a onclick="openTab(this, 'key_passwd')">Change password</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p class="menu-label">Domain management</p>
|
<p class="menu-label">Domain management</p>
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
@@ -60,93 +58,33 @@
|
|||||||
<div class="content is-hidden" id="key_load">
|
<div class="content is-hidden" id="key_load">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Key file name</label>
|
<label class="label">Key path</label>
|
||||||
<div class="control">
|
<p id="key_file_name">Key not saved</p>
|
||||||
<input class="input" type="text" placeholder="default.key" id="load_key_name">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Key password</label>
|
<label class="label">Key public key</label>
|
||||||
<div class="control">
|
<p id="key_public_key">00000050C9516C42946D38EDD6A2DB1279F6E73E9C6D2EA6769A5C2BA3E381D6</p>
|
||||||
<input class="input" type="password" placeholder="123456" id="load_key_password">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-link" onclick="loadKey();">Load</button>
|
<button class="button is-success" onclick="loadKey();">Load key</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-warning" onclick="createKey();">Mine new key</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-primary" onclick="saveKey();">Save key</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="content is-hidden" id="key_create">
|
|
||||||
<form action="#">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Key file name</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" placeholder="default.key" id="create_key_name">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Key password</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="password" placeholder="123456" id="create_key_password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field is-grouped">
|
|
||||||
<div class="control">
|
|
||||||
<button class="button is-link" onclick="createKey();">Create</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="content is-hidden" id="key_passwd">
|
|
||||||
<form action="#">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Key file name</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" placeholder="default.key" id="change_key_name">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Old key password</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="password" placeholder="123456" id="change_key_password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">New key password</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="password" placeholder="123456" id="change_key_password_new">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Repeat key password</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="password" placeholder="123456" id="change_key_password_repeat">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field is-grouped">
|
|
||||||
<div class="control">
|
|
||||||
<button class="button is-link">Change password</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="content is-hidden" id="dom_new">
|
<div class="content is-hidden" id="dom_new">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|||||||
+16
-6
@@ -24,15 +24,15 @@ function openTab(element, tabName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadKey() {
|
function loadKey() {
|
||||||
key_name = document.getElementById("load_key_name").value;
|
external.invoke(JSON.stringify({cmd: 'loadKey'}));
|
||||||
key_pass = document.getElementById("load_key_password").value;
|
|
||||||
external.invoke(JSON.stringify({cmd: 'loadKey', name: key_name, pass: key_pass}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createKey() {
|
function createKey() {
|
||||||
key_name = document.getElementById("create_key_name").value;
|
external.invoke(JSON.stringify({cmd: 'createKey'}));
|
||||||
key_pass = document.getElementById("create_key_password").value;
|
}
|
||||||
external.invoke(JSON.stringify({cmd: 'createKey', name: key_name, pass: key_pass}));
|
|
||||||
|
function saveKey() {
|
||||||
|
external.invoke(JSON.stringify({cmd: 'saveKey'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDomain() {
|
function createDomain() {
|
||||||
@@ -115,4 +115,14 @@ function miningIndicatorClick(element) {
|
|||||||
showModalDialog("Do you really want to stop mining?", function() {
|
showModalDialog("Do you really want to stop mining?", function() {
|
||||||
external.invoke(JSON.stringify({cmd: 'stopMining'}));
|
external.invoke(JSON.stringify({cmd: 'stopMining'}));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function keystoreChanged(path, pub_key) {
|
||||||
|
if (path == '') {
|
||||||
|
path = "In memory";
|
||||||
|
}
|
||||||
|
key_file_name = document.getElementById("key_file_name");
|
||||||
|
key_file_name.innerHTML = path;
|
||||||
|
key_file_key = document.getElementById("key_public_key");
|
||||||
|
key_file_key.innerHTML = pub_key;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user