Made a full refactoring of synchronization primitives between settings, keystore, blockchain and miner.
This commit is contained in:
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wyrd_ns"
|
name = "alfis"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Revertron <rev@revertron.com>"]
|
authors = ["Revertron <rev@revertron.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -10,7 +10,7 @@ edition = "2018"
|
|||||||
rust-crypto = "^0.2"
|
rust-crypto = "^0.2"
|
||||||
num_cpus = "1.10.1"
|
num_cpus = "1.10.1"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
pancurses = "0.16"
|
web-view = { version = "0.5.4", features = [] }
|
||||||
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"
|
||||||
|
|||||||
Binary file not shown.
+6
-3
@@ -10,9 +10,6 @@ pub struct Blockchain {
|
|||||||
impl Blockchain {
|
impl Blockchain {
|
||||||
pub fn new(chain_id: u32, version: u32) -> Self {
|
pub fn new(chain_id: u32, version: u32) -> Self {
|
||||||
let mut blockchain = Blockchain{chain_id, version, blocks: Vec::new()};
|
let mut blockchain = Blockchain{chain_id, version, blocks: Vec::new()};
|
||||||
let mut genesis = Self::genesis(chain_id, version);
|
|
||||||
genesis.mine();
|
|
||||||
blockchain.add_block(genesis);
|
|
||||||
blockchain
|
blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +23,12 @@ impl Blockchain {
|
|||||||
Block::new(0, Utc::now().timestamp(), chain_id, version, Key::zero32(), None)
|
Block::new(0, Utc::now().timestamp(), chain_id, version, Key::zero32(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_genesis(&mut self) {
|
||||||
|
let mut genesis = Self::genesis(self.chain_id, self.version);
|
||||||
|
genesis.mine();
|
||||||
|
self.add_block(genesis);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_block(&mut self, block: Block) {
|
pub fn add_block(&mut self, block: Block) {
|
||||||
if self.check_block(&block, None) {
|
if self.check_block(&block, None) {
|
||||||
println!("Adding block:\n{:?}", &block);
|
println!("Adding block:\n{:?}", &block);
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
use crate::{Keystore, Blockchain};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
pub(crate) settings: Settings,
|
||||||
|
pub(crate) keystore: Keystore,
|
||||||
|
pub(crate) blockchain: Blockchain,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
/// Creating an essential context to work with
|
||||||
|
pub fn new(settings: Settings, keystore: Keystore, blockchain: Blockchain) -> Context {
|
||||||
|
Context { settings, keystore, blockchain }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load keystore and return Context
|
||||||
|
pub fn load_keystore<S: Into<String>>(mut self, name: S, password: S) -> Context {
|
||||||
|
let filename = &name.into();
|
||||||
|
match Keystore::from_file(filename, &password.into()) {
|
||||||
|
None => {
|
||||||
|
println!("Error loading keystore '{}'!", filename);
|
||||||
|
},
|
||||||
|
Some(keystore) => {
|
||||||
|
self.keystore = keystore;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_keystore(&self) -> Keystore {
|
||||||
|
self.keystore.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_keystore(&mut self, keystore: Keystore) {
|
||||||
|
self.keystore = keystore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Settings {
|
||||||
|
pub chain_id: u32,
|
||||||
|
pub version: u32,
|
||||||
|
salts: HashMap<String, String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
/// TODO parse settings
|
||||||
|
pub fn new<S: Into<String>>(chain_id: u32, version: u32, settings: S) -> Settings {
|
||||||
|
Settings { chain_id, version, salts: HashMap::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
+271
@@ -0,0 +1,271 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Wyrd</title>
|
||||||
|
{styles}
|
||||||
|
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
|
||||||
|
{scripts}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<br/>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-one-fifth">
|
||||||
|
<div class="menu">
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" onclick="openTab(this, 'main')">Main</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Key management</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a onclick="openTab(this, 'key_load')">Load key</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>
|
||||||
|
<p class="menu-label">Domain management</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a onclick="openTab(this, 'dom_new')">Create domain</a></li>
|
||||||
|
<li><a onclick="openTab(this, 'dom_edit')">Manage domain</a></li>
|
||||||
|
<li><a onclick="openTab(this, 'dom_renew')">Renew domain</a></li>
|
||||||
|
<li><a onclick="openTab(this, 'dom_transfer')">Transfer domain</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div><!--column-->
|
||||||
|
|
||||||
|
<div class="column auto">
|
||||||
|
<div class="content center" id="main">This screen in now yet implemented</div>
|
||||||
|
|
||||||
|
<div class="content is-hidden" id="key_load">
|
||||||
|
<form action="#">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Key file name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="default.key" id="load_key_name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Key password</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="password" placeholder="123456" id="load_key_password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link" onclick="loadKey();">Load</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</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 class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</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 class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="content is-hidden" id="dom_new">
|
||||||
|
<form action="#">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">New domain name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="example.ygg" id="new_domain">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Domain records</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea class="textarea" placeholder="@ IN AAAA 200:1111:2222:3333:4444:5555:6666:7777" id="new_domain_records"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Domain tags (will be used for search)</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="blog, community, friendship" id="new_domain_tags">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link" onclick="createDomain();">Create domain</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="content is-hidden" id="dom_edit">
|
||||||
|
<form action="#">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Your existing domain name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="example.ygg" id="change_domain">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">All new domain records</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea class="textarea" placeholder="@ IN AAAA 200:1111:2222:3333:4444:5555:6666:7777" id="change_domain_records"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Domain tags (will be used for search)</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="blog, community, friendship" id="change_domain_tags">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link" onclick="changeDomain();">Replace records</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="content is-hidden" id="dom_renew">
|
||||||
|
<form action="#">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Your existing domain name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="example.ygg" id="renew_domain">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Days to add</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="365" value="365" id="renew_domain_extend_days" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link" onclick="renewDomain();">Renew domain</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="content is-hidden" id="dom_transfer">
|
||||||
|
<form action="#">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Your existing domain name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="example.ygg" id="transfer_domain">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Public key of new owner</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="3764ef954577a1815db3cc65aa3e2b18a52f12a3f6fcbd6a10d9ce8d06741ddd" id="transfer_domain_transfer_owner">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link" onclick="transferDomain();">Transfer domain</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link is-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- columns -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer is-hidden">Some footer text is here</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+6
-4
@@ -12,23 +12,25 @@ use serde::de::{Error as DeError, Visitor};
|
|||||||
use serde::export::Formatter;
|
use serde::export::Formatter;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Signature {
|
pub struct Keystore {
|
||||||
private_key: Key,
|
private_key: Key,
|
||||||
public_key: Key,
|
public_key: Key,
|
||||||
|
#[serde(skip)]
|
||||||
|
seed: Vec<u8>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signature {
|
impl Keystore {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut buf = [0u8; 64];
|
let mut buf = [0u8; 64];
|
||||||
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);
|
||||||
Signature {private_key: Key::from_bytes(&private), public_key: Key::from_bytes(&public)}
|
Keystore {private_key: Key::from_bytes(&private), public_key: Key::from_bytes(&public), 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);
|
||||||
Signature {private_key: Key::from_bytes(&private), public_key: Key::from_bytes(&public)}
|
Keystore {private_key: Key::from_bytes(&private), public_key: Key::from_bytes(&public), seed: Vec::from(seed)}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
||||||
|
|||||||
+5
-1
@@ -8,5 +8,9 @@ pub use crate::transaction::Transaction;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub use crate::utils::*;
|
pub use crate::utils::*;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub use crate::keys::Signature;
|
pub use crate::keys::Keystore;
|
||||||
pub use crate::keys::Key;
|
pub use crate::keys::Key;
|
||||||
|
pub mod miner;
|
||||||
|
pub mod context;
|
||||||
|
pub use crate::context::Context;
|
||||||
|
pub use crate::context::Settings;
|
||||||
+121
-185
@@ -1,204 +1,140 @@
|
|||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
extern crate pancurses;
|
extern crate web_view;
|
||||||
use pancurses::{initscr, endwin, Input, noecho, resize_term, Window, start_color, init_pair, colorpair::ColorPair, COLOR_PAIR};
|
use alfis::{Blockchain, Block, Action, Transaction, Keystore, Key, Settings, Context};
|
||||||
|
use web_view::*;
|
||||||
|
use std::thread;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use wyrd_ns::{Blockchain, Block, Action, Transaction, Signature, Key};
|
extern crate serde;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use alfis::miner::Miner;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const COLOR_TEXT: i16 = 0;
|
extern crate serde_json;
|
||||||
const COLOR_TITLE: i16 = 1;
|
|
||||||
const COLOR_MENU_NORMAL: i16 = 2;
|
|
||||||
const COLOR_MENU_FOCUSED: i16 = 3;
|
|
||||||
const COLOR_STATUS: i16 = 4;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Wyrd DNS 0.1.0");
|
println!("ALFIS 0.1.0");
|
||||||
run_interface();
|
let settings = Settings::new(42, 0,"");
|
||||||
//test_blockchain()
|
let keystore: Keystore = Keystore::from_file("default.key", "").unwrap();
|
||||||
|
let blockchain: Blockchain = Blockchain::new(settings.chain_id, settings.version);
|
||||||
|
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
||||||
|
|
||||||
|
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(Miner::new(context.clone())));
|
||||||
|
|
||||||
|
// TODO check settings and if there is no mention of bootstrap nodes, generate genesis block
|
||||||
|
/*let chain_for_genesis = blockchain.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut block = Blockchain::genesis(42, 0);
|
||||||
|
// TODO remake genesis to use Miner
|
||||||
|
block.mine();
|
||||||
|
chain_for_genesis.lock().unwrap().add_block(block);
|
||||||
|
println!("Blockchain with genesis block has been created");
|
||||||
|
});*/
|
||||||
|
run_interface(context.clone(), miner.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_colors() {
|
fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||||
start_color();
|
let file_content = include_str!("index.html");
|
||||||
let background = pancurses::COLOR_BLACK;
|
let styles = inline_style(include_str!("bulma.css"));
|
||||||
let accent = pancurses::COLOR_CYAN;
|
let scripts = inline_script(include_str!("scripts.js"));
|
||||||
let text = pancurses::COLOR_WHITE;
|
|
||||||
|
|
||||||
init_pair(COLOR_TEXT, text, background);
|
web_view::builder()
|
||||||
init_pair(COLOR_TITLE, background, accent);
|
.title("ALFIS 0.1.0")
|
||||||
init_pair(COLOR_MENU_NORMAL, text, background);
|
.content(Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts)))
|
||||||
init_pair(COLOR_MENU_FOCUSED, background, accent);
|
.size(1024, 720)
|
||||||
init_pair(COLOR_STATUS, background, accent);
|
.resizable(true)
|
||||||
}
|
.debug(true)
|
||||||
|
.user_data(())
|
||||||
fn draw_title(win: &Window, title: &str) {
|
.invoke_handler(|_web_view, arg| {
|
||||||
win.color_set(COLOR_TITLE);
|
use Cmd::*;
|
||||||
win.mvprintw(0, win.get_beg_x(), format!("{:width$}", title, width = win.get_max_x() as usize));
|
println!("Command {}", arg);
|
||||||
}
|
match serde_json::from_str(arg).unwrap() {
|
||||||
|
LoadKey { name, pass } => {
|
||||||
fn draw_status(win: &Window, title: &str) {
|
match Keystore::from_file(&name, &pass) {
|
||||||
win.color_set(COLOR_TITLE);
|
None => {
|
||||||
win.mvprintw(win.get_max_y() - 1, win.get_beg_x(), format!("{:width$}", title, width = win.get_max_x() as usize));
|
println!("Error loading keystore '{}'!", &name);
|
||||||
}
|
},
|
||||||
|
Some(k) => {
|
||||||
#[derive(Debug)]
|
let mut c = context.lock().unwrap();
|
||||||
pub struct MenuItem {
|
c.set_keystore(k);
|
||||||
id: usize,
|
},
|
||||||
caption: String,
|
}
|
||||||
hint: String
|
},
|
||||||
}
|
CreateKey { name, pass } => {
|
||||||
|
let mut c = context.lock().unwrap();
|
||||||
impl MenuItem {
|
c.set_keystore(Keystore::new());
|
||||||
pub fn simple<S: Into<String>>(id: usize, caption: S) -> Self {
|
|
||||||
MenuItem{ id, caption: caption.into(), hint: String::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn full<S: Into<String>>(id: usize, caption: S, hint: S) -> Self {
|
|
||||||
MenuItem{ id, caption: caption.into(), hint: hint.into() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn separator() -> Self {
|
|
||||||
MenuItem { id: 0, caption: String::new(), hint: String::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_separator(&self) -> bool {
|
|
||||||
self.caption.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_id(&self) -> usize {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_caption(&self) -> &str {
|
|
||||||
&self.caption
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_hint(&self) -> &str {
|
|
||||||
&self.hint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Menu {
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
items: Vec<MenuItem>,
|
|
||||||
position: usize,
|
|
||||||
max_width: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Menu {
|
|
||||||
fn new(x: i32, y: i32, items: Vec<MenuItem>) -> Self {
|
|
||||||
let mut max = 0;
|
|
||||||
for item in items.iter() {
|
|
||||||
if item.get_caption().len() > max {
|
|
||||||
max = item.get_caption().len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Menu{ x, y, items, position: 0, max_width: max }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn up(&mut self) {
|
|
||||||
if self.position > 0 {
|
|
||||||
self.position = self.position - 1;
|
|
||||||
if self.items[self.position].is_separator() {
|
|
||||||
self.up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn down(&mut self) {
|
|
||||||
if self.position < (self.items.len() - 1) {
|
|
||||||
self.position = self.position + 1;
|
|
||||||
if self.items[self.position].is_separator() {
|
|
||||||
self.down();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn position(&self) -> usize {
|
|
||||||
self.position
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current(&self) -> &MenuItem {
|
|
||||||
&self.items[self.position]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&self, win: &Window) {
|
|
||||||
let mut pos = 0;
|
|
||||||
for item in self.items.iter() {
|
|
||||||
let color = { if pos == self.position {COLOR_MENU_FOCUSED} else {COLOR_MENU_NORMAL} };
|
|
||||||
win.color_set(color);
|
|
||||||
win.mvprintw(self.y + pos as i32, self.x, format!(" {:width$} ", item.get_caption(), width = self.max_width));
|
|
||||||
pos = pos + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_interface() {
|
|
||||||
let window = initscr();
|
|
||||||
resize_term(24, 80);
|
|
||||||
init_colors();
|
|
||||||
draw_title(&window, " Wyrd 0.1.0");
|
|
||||||
window.refresh();
|
|
||||||
window.keypad(true);
|
|
||||||
window.timeout(20);
|
|
||||||
pancurses::noecho();
|
|
||||||
window.keypad(true);
|
|
||||||
pancurses::curs_set(0);
|
|
||||||
let mut menu = create_menu();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match window.getch() {
|
|
||||||
Some(Input::Character(c)) => {
|
|
||||||
if c == '\n' {
|
|
||||||
println!("Selected {:?}", menu.current());
|
|
||||||
} else {
|
|
||||||
window.addch(c);
|
|
||||||
}
|
}
|
||||||
},
|
CreateDomain { name, records, tags } => {
|
||||||
Some(Input::KeyResize) => { resize_term(0, 0); },
|
let mut c = context.lock().unwrap();
|
||||||
Some(Input::KeyUp) => { menu.up(); },
|
create_domain(miner.clone(), name, records, tags, c.get_keystore());
|
||||||
Some(Input::KeyDown) => { menu.down(); },
|
}
|
||||||
Some(Input::KeyEnter) => { println!("Selected {:?}", menu.current()); },
|
ChangeDomain { name, records, tags } => {
|
||||||
Some(Input::KeyDC) => break,
|
|
||||||
Some(input) => { window.addstr(&format!("{:?}", input)); },
|
}
|
||||||
None => ()
|
RenewDomain { name, days } => {}
|
||||||
}
|
TransferDomain { name, owner } => {}
|
||||||
draw_status(&window, menu.current().get_hint());
|
}
|
||||||
menu.paint(&window);
|
//dbg!(&signature);
|
||||||
window.refresh();
|
Ok(())
|
||||||
}
|
})
|
||||||
endwin();
|
.run()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_menu() -> Menu {
|
fn create_domain(miner: Arc<Mutex<Miner>>, name: String, records: String, tags: String, keystore: Keystore) {
|
||||||
let menu_items = vec![
|
let rec_vector: Vec<String> = records.trim().split("\n").map(|s| s.trim()).map(String::from).collect();
|
||||||
MenuItem::full(1, "Key create", "Create keypair to sign domain operations"),
|
let tags_vector: Vec<String> = tags.trim().split(",").map(|s| s.trim()).map(String::from).collect();
|
||||||
MenuItem::full(2, "Key load", "Load existing keypair"),
|
let mut transaction = { transaction_new_domain(keystore, name, rec_vector, tags_vector, 365) };
|
||||||
MenuItem::separator(),
|
let mut miner_guard = miner.lock().unwrap();
|
||||||
MenuItem::full(3, "Domain create new", "Create and mine new domain"),
|
miner_guard.add_transaction(transaction);
|
||||||
MenuItem::full(4, "Domain change records", "Change DNS records for your domain"),
|
miner_guard.mine();
|
||||||
MenuItem::full(5, "Domain renew", "Renew your domain name, needs additional mining"),
|
}
|
||||||
MenuItem::full(6, "Domain transfer", "Transfer your domain to a new owner (new keypair)"),
|
|
||||||
MenuItem::separator(),
|
fn transaction_claim_name<S: Into<String>>(keystore: Keystore, name: S, salt: S) -> Transaction {
|
||||||
MenuItem::full(7, "Test blockchain", "Do some test blockchain operations with simple mining")
|
// Creating transaction
|
||||||
];
|
let action = Action::claim_name(name.into(), salt.into(), &keystore);
|
||||||
Menu::new(1, 3, menu_items)
|
let mut transaction = Transaction::new(action, keystore.get_public().clone());
|
||||||
|
// Signing it with private key from Signature
|
||||||
|
let sign_hash = keystore.sign(&transaction.get_bytes());
|
||||||
|
transaction.set_signature(Key::from_bytes(&sign_hash));
|
||||||
|
transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction_new_domain<S: Into<String>>(keystore: Keystore, name: S, records: Vec<String>, tags: Vec<String>, days: u16) -> Transaction {
|
||||||
|
// Creating transaction
|
||||||
|
let action = Action::new_domain(name.into(), &keystore, records, tags, days);
|
||||||
|
let mut transaction = Transaction::new(action, keystore.get_public().clone());
|
||||||
|
// Signing it with private key from Signature
|
||||||
|
let sign_hash = keystore.sign(&transaction.get_bytes());
|
||||||
|
transaction.set_signature(Key::from_bytes(&sign_hash));
|
||||||
|
transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||||
|
pub enum Cmd {
|
||||||
|
LoadKey{name: String, pass: String},
|
||||||
|
CreateKey{name: String, pass: String},
|
||||||
|
CreateDomain{name: String, records: String, tags: String},
|
||||||
|
ChangeDomain{name: String, records: String, tags: String},
|
||||||
|
RenewDomain{name: String, days: u16},
|
||||||
|
TransferDomain{name: String, owner: String},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_style(s: &str) -> String {
|
||||||
|
format!(r#"<style type="text/css">{}</style>"#, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_script(s: &str) -> String {
|
||||||
|
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_blockchain() -> () {
|
fn test_blockchain() -> () {
|
||||||
let mut blockchain = Blockchain::new(42, 0);
|
let mut blockchain = Blockchain::new(42, 0);
|
||||||
println!("Blockchain with genesis block has been created");
|
println!("Blockchain with genesis block has been created");
|
||||||
let signature = Signature::from_file("default.key", "").unwrap();
|
let keystore = Keystore::from_file("default.key", "").unwrap();
|
||||||
|
|
||||||
// Creating transaction
|
/*let mut block = create_transaction(&mut blockchain, keystore.clone(), "test.zz", vec!["AAAA IN 301:2925::1".to_owned()], vec!["testing".to_owned(), "example".to_owned()], 365);
|
||||||
let action = Action::new_domain("test.zz".to_owned(), &signature, vec!["AAAA IN 301:2925::1".to_owned()], vec!["testing".to_owned(), "example".to_owned()], 365);
|
|
||||||
let mut transaction = Transaction::new(action, signature.get_public().clone());
|
|
||||||
|
|
||||||
// Signing it with private key from Signature
|
|
||||||
let sign_hash = signature.sign(&transaction.get_bytes());
|
|
||||||
transaction.set_signature(Key::from_bytes(&sign_hash));
|
|
||||||
|
|
||||||
// Creating a block with that signed transaction
|
|
||||||
let mut block = blockchain.new_block(transaction);
|
|
||||||
|
|
||||||
// Mining the nonce
|
// Mining the nonce
|
||||||
block.mine();
|
block.mine();
|
||||||
@@ -210,7 +146,7 @@ fn test_blockchain() -> () {
|
|||||||
println!("Second block added");
|
println!("Second block added");
|
||||||
|
|
||||||
let block2: Block = serde_json::from_str(&s).unwrap();
|
let block2: Block = serde_json::from_str(&s).unwrap();
|
||||||
println!("DeSerialized block:\n{:?}", block2);
|
println!("DeSerialized block:\n{:?}", block2);*/
|
||||||
|
|
||||||
// Let's check if the blockchain is valid
|
// Let's check if the blockchain is valid
|
||||||
if blockchain.check() {
|
if blockchain.check() {
|
||||||
|
|||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
use crate::{Transaction, Block, Keystore, Key, Context};
|
||||||
|
use std::sync::{Mutex, Arc};
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use chrono::Utc;
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
use num_traits::One;
|
||||||
|
use crypto::sha2::Sha256;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
pub struct Miner {
|
||||||
|
context: Arc<Mutex<Context>>,
|
||||||
|
keystore: Keystore,
|
||||||
|
chain_id: u32,
|
||||||
|
version: u32,
|
||||||
|
transactions: Arc<Mutex<Vec<Transaction>>>,
|
||||||
|
last_block: Option<Block>,
|
||||||
|
running: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Miner {
|
||||||
|
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
||||||
|
let c = context.lock().unwrap();
|
||||||
|
Miner {
|
||||||
|
context: context.clone(),
|
||||||
|
keystore: c.keystore.clone(),
|
||||||
|
chain_id: c.settings.chain_id,
|
||||||
|
version: c.settings.version,
|
||||||
|
transactions: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
last_block: c.blockchain.blocks.last().cloned(),
|
||||||
|
running: Arc::new(AtomicBool::new(false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_transaction(&mut self, transaction: Transaction) {
|
||||||
|
self.transactions.lock().unwrap().push(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
self.running.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mine(&mut self) {
|
||||||
|
let transaction = { self.transactions.lock().unwrap().first().cloned() };
|
||||||
|
match transaction {
|
||||||
|
Some(transaction) => {
|
||||||
|
self.mine_internal(transaction);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("Nothing to mine");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mining(&self) -> bool {
|
||||||
|
self.running.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mine_internal(&mut self, mut transaction: Transaction) {
|
||||||
|
let mut last_block_time = 0i64;
|
||||||
|
let block = {
|
||||||
|
// Signing it with private key from Keystore
|
||||||
|
let sign_hash = self.keystore.sign(&transaction.get_bytes());
|
||||||
|
transaction.set_signature(Key::from_bytes(&sign_hash));
|
||||||
|
|
||||||
|
match &self.last_block {
|
||||||
|
None => {
|
||||||
|
// Creating a block with that signed transaction
|
||||||
|
Block::new(0,Utc::now().timestamp(), self.chain_id, self.version, Key::zero32(), Some(transaction))
|
||||||
|
},
|
||||||
|
Some(block) => {
|
||||||
|
last_block_time = block.timestamp;
|
||||||
|
// Creating a block with that signed transaction
|
||||||
|
Block::new(block.index + 1,Utc::now().timestamp(), self.chain_id, self.version, block.hash.clone(), Some(transaction))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//let blockchain = self.blockchain.clone();
|
||||||
|
let transactions = self.transactions.clone();
|
||||||
|
let running = self.running.clone();
|
||||||
|
running.store(true, Ordering::Relaxed);
|
||||||
|
let context = self.context.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
match find_hash(&mut Sha256::new(), block, last_block_time, running.clone()) {
|
||||||
|
None => {
|
||||||
|
println!("Mining stopped");
|
||||||
|
},
|
||||||
|
Some(block) => {
|
||||||
|
//blockchain.lock().unwrap().add_block(block);
|
||||||
|
transactions.lock().unwrap().remove(0);
|
||||||
|
running.store(false, Ordering::Relaxed);
|
||||||
|
context.lock().unwrap().blockchain.add_block(block);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_hash(digest: &mut dyn Digest, mut block: Block, prev_block_time: i64, running: Arc<AtomicBool>) -> Option<Block> {
|
||||||
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
|
block.random = rand::random();
|
||||||
|
let start_difficulty = block.difficulty;
|
||||||
|
for nonce in 0..std::u64::MAX {
|
||||||
|
if !running.load(Ordering::Relaxed) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
block.timestamp = Utc::now().timestamp();
|
||||||
|
block.nonce = nonce;
|
||||||
|
// TODO uncomment for real run
|
||||||
|
//block.difficulty = start_difficulty + get_time_difficulty(prev_block_time, block.timestamp);
|
||||||
|
|
||||||
|
digest.reset();
|
||||||
|
digest.input(serde_json::to_string(&block).unwrap().as_bytes());
|
||||||
|
digest.result(&mut buf);
|
||||||
|
if hash_is_good(&buf, block.difficulty) {
|
||||||
|
block.hash = Key::from_bytes(&buf);
|
||||||
|
return Some(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_time_difficulty(prev_time: i64, now: i64) -> usize {
|
||||||
|
let diff = now - prev_time;
|
||||||
|
if diff < 900_000 {
|
||||||
|
(900_000 as usize - diff as usize) / 60_000
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_is_good(hash: &[u8], difficulty: usize) -> bool {
|
||||||
|
let target = BigUint::one() << ((hash.len() << 3) - difficulty);
|
||||||
|
let hash_int = BigUint::from_bytes_be(&hash);
|
||||||
|
|
||||||
|
return hash_int < target;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
function openTab(element, tabName) {
|
||||||
|
// Declare all variables
|
||||||
|
var i, tabContent, tabLinks;
|
||||||
|
|
||||||
|
// Get all elements with class="content" and hide them
|
||||||
|
tabContent = document.getElementsByClassName("content");
|
||||||
|
for (i = 0; i < tabContent.length; i++) {
|
||||||
|
tabContent[i].className = "context is-hidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all elements with class="tablinks" and remove the class "active"
|
||||||
|
tabLinks = document.getElementsByClassName("is-active");
|
||||||
|
for (i = 0; i < tabLinks.length; i++) {
|
||||||
|
tabLinks[i].className = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||||
|
document.getElementById(tabName).className = "content";
|
||||||
|
element.className = "is-active";
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadKey() {
|
||||||
|
key_name = document.getElementById("load_key_name").value;
|
||||||
|
key_pass = document.getElementById("load_key_password").value;
|
||||||
|
external.invoke(JSON.stringify({cmd: 'loadKey', name: key_name, pass: key_pass}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createKey() {
|
||||||
|
key_name = document.getElementById("create_key_name").value;
|
||||||
|
key_pass = document.getElementById("create_key_password").value;
|
||||||
|
external.invoke(JSON.stringify({cmd: 'createKey', name: key_name, pass: key_pass}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDomain() {
|
||||||
|
new_domain = document.getElementById("new_domain").value;
|
||||||
|
new_dom_records = document.getElementById("new_domain_records").value;
|
||||||
|
new_dom_tags = document.getElementById("new_domain_tags").value;
|
||||||
|
external.invoke(JSON.stringify({cmd: 'createDomain', name: new_domain, records: new_dom_records, tags: new_dom_tags}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeDomain() {
|
||||||
|
domain = document.getElementById("change_domain").value;
|
||||||
|
dom_records = document.getElementById("change_domain_records").value;
|
||||||
|
dom_tags = document.getElementById("change_domain_records").value;
|
||||||
|
external.invoke(JSON.stringify({cmd: 'changeDomain', name: domain, records: dom_records, tags: dom_tags}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renewDomain() {
|
||||||
|
domain = document.getElementById("renew_domain").value;
|
||||||
|
days = document.getElementById("renew_domain_extend_days").value;
|
||||||
|
external.invoke(JSON.stringify({cmd: 'renewDomain', name: domain, days: days}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function transferDomain() {
|
||||||
|
domain = document.getElementById("transfer_domain").value;
|
||||||
|
new_owner = document.getElementById("transfer_domain_transfer_owner").value;
|
||||||
|
external.invoke(JSON.stringify({cmd: 'transferDomain', name: domain, owner: new_owner}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendAction(param) {
|
||||||
|
external.invoke(JSON.stringify(param));
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
/// Signal to a serializable object how much of its data should be serialized
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum SerializationMode {
|
|
||||||
/// Serialize everything sufficiently to fully reconstruct the object
|
|
||||||
Full,
|
|
||||||
/// Serialize the data that defines the object
|
|
||||||
Hash,
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
html {
|
||||||
|
background: #202020;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: linear-gradient(0deg, #404040, #090909);
|
||||||
|
background-attachment: fixed;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
h1, h2, h3 { color: #dddddd; }
|
||||||
|
p { color: #dddddd; }
|
||||||
|
a { color: #dddddd; }
|
||||||
|
/* Style the tab */
|
||||||
|
.tab {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0px;
|
||||||
|
background: linear-gradient(180deg, #404040, #202020);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the buttons that are used to open the tab content */
|
||||||
|
.tab button {
|
||||||
|
background: inherit;
|
||||||
|
color: #dddddd;
|
||||||
|
float: left;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px 16px;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change background color of buttons on hover */
|
||||||
|
.tab button:hover {
|
||||||
|
background: linear-gradient(180deg, #808080, #202020);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create an active/current tablink class */
|
||||||
|
.tab button.active {
|
||||||
|
background: linear-gradient(180deg, #606060, #202020);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the tab content */
|
||||||
|
.tabcontent {
|
||||||
|
display: none;
|
||||||
|
padding: 12px 12px;
|
||||||
|
border: 0px;
|
||||||
|
border-top: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
color: #dddddd;
|
||||||
|
padding: 8px 8px;
|
||||||
|
background: linear-gradient(180deg, #404040, #202020);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontent button {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #404040;
|
||||||
|
color: #dddddd;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
+39
-6
@@ -1,4 +1,3 @@
|
|||||||
use crate::transaction::Action::{MoveDomain, RenewDomain, ChangeDomain, NewDomain};
|
|
||||||
use crate::keys::*;
|
use crate::keys::*;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
@@ -10,27 +9,43 @@ use std::fmt;
|
|||||||
#[derive(Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Serialize, Deserialize, PartialEq)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
ClaimName { hash: String, owner: Key },
|
||||||
NewDomain { name: String, owner: Key, #[serde(default, skip_serializing_if = "Vec::is_empty")] records: Vec<String>, #[serde(default, skip_serializing_if = "Vec::is_empty")] tags: Vec<String>, days: u16 },
|
NewDomain { name: String, owner: Key, #[serde(default, skip_serializing_if = "Vec::is_empty")] records: Vec<String>, #[serde(default, skip_serializing_if = "Vec::is_empty")] tags: Vec<String>, days: u16 },
|
||||||
ChangeDomain { name: String, records: Vec<String>, tags: Vec<String> },
|
ChangeDomain { name: String, records: Vec<String>, tags: Vec<String> },
|
||||||
RenewDomain { name: String, days: u16 },
|
RenewDomain { name: String, days: u16 },
|
||||||
MoveDomain { name: String, new_owner: Key },
|
MoveDomain { name: String, new_owner: Key },
|
||||||
|
NewZone { name: String, difficulty: u16},
|
||||||
|
ChangeZone { name: String, difficulty: u16},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Action {
|
impl Action {
|
||||||
pub fn new_domain(name: String, signature: &Signature, records: Vec<String>, tags: Vec<String>, days: u16) -> Self {
|
pub fn claim_name(name: String, salt: String, signature: &Keystore) -> Self {
|
||||||
NewDomain {name, owner: signature.get_public(), records, tags, days}
|
let hash = format!("{} {}", salt, name);
|
||||||
|
Action::ClaimName {hash, owner: signature.get_public()}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_domain(name: String, signature: &Keystore, records: Vec<String>, tags: Vec<String>, days: u16) -> Self {
|
||||||
|
Action::NewDomain {name, owner: signature.get_public(), records, tags, days}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_domain(name: String, records: Vec<String>, tags: Vec<String>) -> Self {
|
pub fn change_domain(name: String, records: Vec<String>, tags: Vec<String>) -> Self {
|
||||||
ChangeDomain {name, records, tags}
|
Action::ChangeDomain {name, records, tags}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn renew_domain(name: String, days: u16) -> Self {
|
pub fn renew_domain(name: String, days: u16) -> Self {
|
||||||
RenewDomain {name, days}
|
Action::RenewDomain {name, days}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_domain(name: String, new_owner: [u8; 32]) -> Self {
|
pub fn move_domain(name: String, new_owner: [u8; 32]) -> Self {
|
||||||
MoveDomain {name, new_owner: Key::from_bytes(&new_owner)}
|
Action::MoveDomain {name, new_owner: Key::from_bytes(&new_owner)}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_zone(name: String, difficulty: u16) -> Self {
|
||||||
|
Action::NewZone {name, difficulty}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_zone(name: String, difficulty: u16) -> Self {
|
||||||
|
Action::ChangeZone {name, difficulty}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bytes(&self) -> Vec<u8> {
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
@@ -47,6 +62,12 @@ impl Action {
|
|||||||
impl fmt::Debug for Action {
|
impl fmt::Debug for Action {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Action::ClaimName { hash, owner } => {
|
||||||
|
fmt.debug_struct("ClaimName")
|
||||||
|
.field("hash", hash)
|
||||||
|
.field("owner", owner)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
Action::NewDomain { name, owner, records, tags, days } => {
|
Action::NewDomain { name, owner, records, tags, days } => {
|
||||||
fmt.debug_struct("NewDomain")
|
fmt.debug_struct("NewDomain")
|
||||||
.field("name", name)
|
.field("name", name)
|
||||||
@@ -75,6 +96,18 @@ impl fmt::Debug for Action {
|
|||||||
.field("new_owner", new_owner)
|
.field("new_owner", new_owner)
|
||||||
.finish()
|
.finish()
|
||||||
},
|
},
|
||||||
|
Action::NewZone { name, difficulty } => {
|
||||||
|
fmt.debug_struct("NewZone")
|
||||||
|
.field("name", name)
|
||||||
|
.field("difficulty", difficulty)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
|
Action::ChangeZone { name, difficulty } => {
|
||||||
|
fmt.debug_struct("ChangeZone")
|
||||||
|
.field("name", name)
|
||||||
|
.field("difficulty", difficulty)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user